# frozen_string_literal: truerequire"active_support/core_ext/module/delegation"moduleActiveStorage# = 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:# * +download+# * +exists?+# * +url+# * +url_for_direct_upload+# * +headers_for_direct_upload+classService::MirrorService<Serviceattr_reader:primary,:mirrorsdelegate:download,:download_chunk,:exist?,:url,:url_for_direct_upload,:headers_for_direct_upload,:path_for,:compose,to: :primary# Stitch together from named services.defself.build(primary:,mirrors:,name:,configurator:,**options)# :nodoc:new(primary: configurator.build(primary),mirrors: mirrors.collect{|mirror_name|configurator.buildmirror_name}).tapdo|service_instance|service_instance.name=nameendenddefinitialize(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# 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.defupload(key,io,checksum: nil,**options)io.rewindprimary.uploadkey,io,checksum: checksum,**optionsmirror_laterkey,checksum: checksumend# Delete the file at the +key+ on all services.defdelete(key)perform_across_services:delete,keyend# Delete files at keys starting with the +prefix+ on all services.defdelete_prefixed(prefix)perform_across_services:delete_prefixed,prefixenddefmirror_later(key,checksum:)# :nodoc:ActiveStorage::MirrorJob.perform_laterkey,checksum: checksumend# Copy the file at the +key+ from the primary service to each of the mirrors where it doesn't already exist.defmirror(key,checksum:)instrument:mirror,key: key,checksum: checksumdoif(mirrors_in_need_of_mirroring=mirrors.select{|service|!service.exist?(key)}).any?primary.open(key,checksum: checksum)do|io|mirrors_in_need_of_mirroring.eachdo|service|io.rewindservice.uploadkey,io,checksum: checksumendendendendendprivatedefeach_service(&block)[primary,*mirrors].each(&block)enddefperform_across_services(method,*args)tasks=each_service.collectdo|service|Concurrent::Promise.execute(executor: @executor)doservice.public_sendmethod,*argsendendtasks.each(&:value!)endendend