# frozen_string_literal: true module SidekiqUniqueJobs # # Gathers all configuration for a lock # which helps reduce the amount of instance variables # # @author Mikael Henriksson # class LockConfig # # @!attribute [r] type # @return [Symbol] the type of lock attr_reader :type # # @!attribute [r] job # @return [Symbol] the job class attr_reader :job # # @!attribute [r] limit # @return [Integer] the number of simultaneous locks attr_reader :limit # # @!attribute [r] timeout # @return [Integer, nil] the time to wait for a lock attr_reader :timeout # # @!attribute [r] ttl # @return [Integer, nil] the time (in seconds) to live after successful attr_reader :ttl # # @!attribute [r] ttl # @return [Integer, nil] the time (in milliseconds) to live after successful attr_reader :pttl # # @!attribute [r] lock_info # @return [Boolean] indicate wether to use lock_info or not attr_reader :lock_info # # @!attribute [r] on_conflict # @return [Symbol, Hash] the strategies to use as conflict resolution attr_reader :on_conflict # # @!attribute [r] errors # @return [Array] a collection of configuration errors attr_reader :errors # # Instantiate a new lock_config based on sidekiq options in worker # # @param [Hash] options sidekiq_options for worker # # @return [LockConfig] # def self.from_worker(options) new(options.deep_stringify_keys) end def initialize(job_hash = {}) @type = job_hash[LOCK]&.to_sym @job = SidekiqUniqueJobs.safe_constantize(job_hash[CLASS]) @limit = job_hash.fetch(LOCK_LIMIT, 1)&.to_i @timeout = job_hash.fetch(LOCK_TIMEOUT, 0)&.to_i @ttl = job_hash.fetch(LOCK_TTL) { job_hash.fetch(LOCK_EXPIRATION, nil) }.to_i @pttl = ttl * 1_000 @lock_info = job_hash.fetch(LOCK_INFO) { SidekiqUniqueJobs.config.lock_info } @on_conflict = job_hash.fetch(ON_CONFLICT, nil) @errors = job_hash.fetch(ERRORS) { {} } @on_client_conflict = job_hash[ON_CLIENT_CONFLICT] @on_server_conflict = job_hash[ON_SERVER_CONFLICT] end def lock_info? lock_info end # # Indicate if timeout was set # # # @return [true,false] # def wait_for_lock? timeout.nil? || timeout.positive? end # # Is the configuration valid? # # # @return [true,false] # def valid? errors.empty? end # # Return a nice descriptive message with all validation errors # # # @return [String] # def errors_as_string return if valid? @errors_as_string ||= begin error_msg = +"\t" error_msg << errors.map { |key, val| "#{key}: :#{val}" }.join("\n\t") error_msg end end # the strategy to use as conflict resolution from sidekiq client def on_client_conflict @on_client_conflict ||= if on_conflict.is_a?(Hash) on_conflict["client"] || on_conflict[:client] else on_conflict end end # the strategy to use as conflict resolution from sidekiq server def on_server_conflict @on_server_conflict ||= if on_conflict.is_a?(Hash) on_conflict["server"] || on_conflict[:server] else on_conflict end end end end