123456789_123456789_123456789_123456789_123456789_

Class: ActiveStorage::Service::MirrorService

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: ActiveStorage::Service
Defined in: activestorage/lib/active_storage/service/mirror_service.rb

Overview

Active Storage Mirror Service

Wraps a set of mirror services and provides a single ::ActiveStorage::Service object that will all have the files uploaded to them. A #primary service is designated to answer calls to:

Class Method Summary

::ActiveStorage::Service - Inherited

.configure

Configure an Active Storage service by name from a set of configurations, typically loaded from a YAML file.

.build

Override in subclasses that stitch together multiple services and hence need to build additional services using the configurator.

::ActiveSupport::Autoload - Extended

Instance Attribute Summary

Instance Method Summary

::ActiveStorage::Service - Inherited

#compose

Concatenate multiple files into a single “composed” file.

#delete

Delete the file at the key.

#delete_prefixed

Delete files at keys starting with the prefix.

#download

Return the content of the file at the key.

#download_chunk

Return the partial content in the byte range of the file at the key.

#exist?

Return true if a file exists at the key.

#headers_for_direct_upload

Returns a ::Hash of headers for #url_for_direct_upload requests.

#open,
#update_metadata

Update metadata for the file identified by key in the service.

#upload

Upload the io to the key specified.

#url

Returns the URL for the file at the key.

#url_for_direct_upload

Returns a signed, temporary URL that a direct upload file can be PUT to on the key.

#content_disposition_with, #custom_metadata_headers, #instrument, #private_url, #public_url, #service_name

Constructor Details

.new(primary:, mirrors:) ⇒ MirrorService

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 31

def initialize(primary:, mirrors:)
  @primary, @mirrors = primary, mirrors
  @executor = Concurrent::ThreadPoolExecutor.new(
    min_threads: 1,
    max_threads: mirrors.size,
    max_queue: 0,
    fallback_policy: :caller_runs,
    idle_time: 60
  )
end

Class Method Details

.build(primary:, mirrors:, name:, configurator:, **options)

This method is for internal use only.

Stitch together from named services.

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 22

def self.build(primary:, mirrors:, name:, configurator:, **options) # :nodoc:
  new(
    primary: configurator.build(primary),
    mirrors: mirrors.collect { |mirror_name| configurator.build mirror_name }
  ).tap do |service_instance|
    service_instance.name = name
  end
end

Instance Attribute Details

#compose (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

#download (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

#download_chunk (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

#headers_for_direct_upload (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

#mirrors (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 16

attr_reader :primary, :mirrors

#path_for (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

#primary (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 16

attr_reader :primary, :mirrors

#url (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

#url_for_direct_upload (readonly)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

Instance Method Details

#delete(key)

Delete the file at the key on all services.

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 52

def delete(key)
  perform_across_services :delete, key
end

#delete_prefixed(prefix)

Delete files at keys starting with the prefix on all services.

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 57

def delete_prefixed(prefix)
  perform_across_services :delete_prefixed, prefix
end

#each_service(&block) (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 80

def each_service(&block)
  [ primary, *mirrors ].each(&block)
end

#exist?Boolean

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 18

delegate :download, :download_chunk, :exist?, :url,
  :url_for_direct_upload, :headers_for_direct_upload, :path_for, :compose, to: :primary

#mirror(key, checksum:)

Copy the file at the key from the primary service to each of the mirrors where it doesn’t already exist.

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 66

def mirror(key, checksum:)
  instrument :mirror, key: key, checksum: checksum do
    if (mirrors_in_need_of_mirroring = mirrors.select { |service| !service.exist?(key) }).any?
      primary.open(key, checksum: checksum) do |io|
        mirrors_in_need_of_mirroring.each do |service|
          io.rewind
          service.upload key, io, checksum: checksum
        end
      end
    end
  end
end

#mirror_later(key, checksum:)

This method is for internal use only.
[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 61

def mirror_later(key, checksum:) # :nodoc:
  ActiveStorage::MirrorJob.perform_later key, checksum: checksum
end

#perform_across_services(method, *args) (private)

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 84

def perform_across_services(method, *args)
  tasks = each_service.collect do |service|
    Concurrent::Promise.execute(executor: @executor) do
      service.public_send method, *args
    end
  end
  tasks.each(&:value!)
end

#upload(key, io, checksum: nil, **options)

Upload the io to the key specified to all services. The upload to the primary service is done synchronously whereas the upload to the mirrors is done asynchronously. If a checksum is provided, all services will ensure a match when the upload has completed or raise an ::ActiveStorage::IntegrityError.

[ GitHub ]

  
# File 'activestorage/lib/active_storage/service/mirror_service.rb', line 45

def upload(key, io, checksum: nil, **options)
  io.rewind
  primary.upload key, io, checksum: checksum, **options
  mirror_later key, checksum: checksum
end