Shrine

Shrine

  • Guides
  • Plugins
  • External
  • Discourse
  • GitHub
  • Wiki

›Flow

Attachment

  • Active Record
  • Column
  • Entity
  • Model
  • Sequel

Flow

  • Backgrounding
  • Presign Endpoint
  • Upload Endpoint

Processing

  • Derivation Endpoint
  • Derivatives

Source

  • Data URI
  • Rack File
  • Remote URL

Validation

  • Remove Invalid
  • Validation
  • Validation Helpers

Metadata

  • Add Metadata
  • Determine MIME Type
  • Infer Extension
  • Metadata Attributes
  • Refresh Metadata
  • Restore Cached Data
  • Signature
  • Store Dimensions
  • Type Predicates

Downloading

  • Download Endpoint
  • Rack Response
  • Tempfile

Form

  • Cached Attachment Data
  • Form Assign
  • Remove Attachment

Settings

  • Default Storage
  • Default URL
  • Dynamic Storage
  • Multi Cache
  • Pretty Location
  • Upload Options
  • URL Options

Other

  • Atomic Helpers
  • Included
  • Instrumentation
  • Keep Files
  • Mirroring
Edit

Backgrounding

The backgrounding plugin enables you to move promoting and deleting of files into background jobs. This is especially useful if you're processing derivatives and storing files to a remote storage service.

Shrine.plugin :backgrounding # load the plugin globally 

Setup

Define background jobs that will promote and destroy attachments:

class PromoteJob
  include Sidekiq::Worker
 
  def perform(attacher_class, record_class, record_id, name, file_data)
    attacher_class = Object.const_get(attacher_class)
    record         = Object.const_get(record_class).find(record_id) # if using Active Record 
 
    attacher = attacher_class.retrieve(model: record, name: name, file: file_data)
    attacher.atomic_promote
  rescue Shrine::AttachmentChanged, ActiveRecord::RecordNotFound
    # attachment has changed or record has been deleted, nothing to do 
  end
end
class DestroyJob
  include Sidekiq::Worker
 
  def perform(attacher_class, data)
    attacher_class = Object.const_get(attacher_class)
 
    attacher = attacher_class.from_data(data)
    attacher.destroy
  end
end

Then, in your initializer, you can configure all uploaders to use these jobs:

Shrine::Attacher.promote_block do
  PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
end
Shrine::Attacher.destroy_block do
  DestroyJob.perform_async(self.class.name, data)
end

Alternatively, you can setup backgrounding only for specific uploaders:

class MyUploader < Shrine
  Attacher.promote_block do
    PromoteJob.perform_async(self.class.name, record.class.name, record.id, name, file_data)
  end
  Attacher.destroy_block do
    DestroyJob.perform_async(self.class.name, data)
  end
end

How it works

If backgrounding blocks are registered, they will be automatically called on Attacher#promote_cached and Attacher#destroy_previous (called by Attacher#finalize), and Attacher#destroy_attached.

attacher.assign(file)
attacher.finalize         # spawns promote job 
attacher.destroy_attached # spawns destroy job 

These methods are automatically called as part of the attachment flow if you're using Shrine::Attachment with a persistence plugin such as activerecord or sequel.

photo = Photo.new
photo.image = file
photo.save    # spawns promote job 
photo.destroy # spawns destroy job 

Atomic promotion

Inside the promote job, we use Attacher.retrieve and Attacher#atomic_promote for concurrency safety. These methods are provided by the atomic_helpers plugin, which is loaded automatically by your persistence plugin (activerecord, sequel).

attacher = Shrine::Attacher.retrieve(model: record, name: name, file: file_data)
attacher.atomic_promote

Without concurrency safety, promotion would look like this:

attacher = record.send(:"#{name}_attacher")
attacher.promote
attacher.persist

Registering backgrounding blocks

The blocks registered by Attacher.promote_block and Attacher#destroy_block are by default evaluated in context of a Shrine::Attacher instance. You can also use the explicit version by declaring an attacher argument:

Shrine::Attacher.promote_block do |attacher|
  PromoteJob.perform_async(
    attacher.class.name,
    attacher.record.class.name,
    attacher.record.id,
    attacher.name,
    attacher.file_data,
  )
end
 
Shrine::Attacher.destroy_block do |attacher|
  DestroyJob.perform_async(
    attacher.class.name,
    attacher.data,
  )
end

You can also register backgrounding blocks on attacher instances for more flexibility:

photo.image_attacher.promote_block do |attacher|
  PromoteJob.perform_async(
    attacher.class.name,
    attacher.record.class.name,
    attacher.record.id,
    attacher.name,
    attacher.file_data,
    current_user.id, # pass arguments known at the controller level 
  )
end
 
photo.image = file
photo.save # executes the promote block above 

Calling backgrounding blocks

If you want to call backgrounding blocks directly, you can do that by calling Attacher#promote_background and Attacher#destroy_background.

attacher.promote_background # calls promote block directly 
attacher.destroy_background # calls destroy block directly 

Any options passed to these methods will be forwarded to the background block:

attacher.promote_background(foo: "bar")
# with instance eval 
Shrine::Attacher.promote_block do |**options|
  options #=> { foo: "bar" } 
end
 
# without instance eval 
Shrine::Attacher.promote_block do |attacher, **options|
  options #=> { foo: "bar" } 
end

Disabling backgrounding

If you've registered backgrounding blocks, but want to temporarily disable them and make the execution synchronous, you can override them on the attacher level and call the default behaviour:

photo.image_attacher.promote_block { promote } # promote synchronously 
photo.image_attacher.destroy_block { destroy } # destroy synchronously 
 
# ... now promotion and deletion will be synchronous ... 

You can also do this on the class level if you want to disable backgrounding that was set up by a superclass:

class MyUploader < Shrine
  Attacher.promote_block { promote } # promote synchronously 
  Attacher.destroy_block { destroy } # destroy synchronously 
end
← SequelPresign Endpoint →
  • Setup
  • How it works
    • Atomic promotion
  • Registering backgrounding blocks
  • Calling backgrounding blocks
  • Disabling backgrounding
Shrine
Docs
GuidesPluginsExternalContributing
Community
DiscourseStack Overflow
More
BlogGitHubStar
Follow @shrine_rb
Copyright © 2022 Janko Marohnić