Skip to main content

Persistence

This is an internal plugin that provides uniform persistence interface across different persistence plugins (e.g. activerecord, sequel).

For these activerecord and sequel, atomic persistence is implemented in terms of database locks, eg "SELECT... FOR UPDATE". For more discussion of concurrency challenges, see the atomic_helpers documentation.

Atomic promotion

If you're promoting cached file to permanent storage asynchronously, and want to handle the possibility of attachment changing during promotion, you can use Attacher#atomic_promote:

# in your controller
attacher.attach_cached(io)
attacher.cached? #=> true
# in a background job
attacher.atomic_promote # promotes cached file and persists
attacher.stored? #=> true

After the cached file is uploaded to permanent storage, the record is reloaded in order to check whether the attachment hasn't changed, and if it hasn't the attachment is persisted. If the attachment has changed, Shrine::AttachmentChanged exception is raised.

If you want to execute code after the attachment change check but before persistence, you can pass a block:

attacher.atomic_promote do |reloaded_attacher|
  # run code after attachment change check but before persistence
end

You can pass :reload and :persist options to change how the record is reloaded and pesisted. See the atomic_helpers plugin docs for more details.

Any other options are forwarded to Attacher#promote:

attacher.atomic_promote(metadata: true) # re-extract metadata

Atomic persistence

If you're updating something based on the attached file asynchronously, you might want to handle the possibility of the attachment changing in the meanwhile. You can do that with Attacher#atomic_persist:

# in a background job
attacher.refresh_metadata! # refresh_metadata plugin
attacher.atomic_persist # persists attachment data

The record is first reloaded in order to check whether the attachment hasn't changed, and if it hasn't the attachment is persisted. If the attachment has changed, Shrine::AttachmentChanged exception is raised.

If you want to execute code after the attachment change check but before persistence, you can pass a block. For instance, one way to allow concurrent changes to metadata, perhaps in different background workers, without overwriting each other might be:

attacher.atomic_persist do |reloaded_attacher|
  # run code after attachment change check but before persistence
  attacher.file.metadata.merge!(reloaded_attacher.file.metadata)
  attacher.file.metadata["some_key"] = "changed_value"
end

You can pass :reload and :persist options to change how the record is reloaded and pesisted. See the atomic_helpers plugin docs for more details.

Simple Persistence

To simply save attachment changes to the underlying record, use Attacher#persist:

attacher.attach(io)
attacher.persist # saves the underlying record