class SidekiqUniqueJobs::Lock::BaseLock

Abstract base class for locks

@abstract @author Mikael Henriksson <mikael@mhenrixon.com>

Attributes

attempt[R]

@!attribute [r] attempt

@return [Integer] the current locking attempt
callback[R]

@!attribute [r] callback

@return [Proc] the block to call after unlock
item[R]

@!attribute [r] item

@return [Hash<String, Object>] the Sidekiq job hash
lock_config[R]

@!attribute [r] lock_config

@return [LockConfig] a lock configuration
redis_pool[R]

@!attribute [r] redis_pool

@return [Sidekiq::RedisConnection, ConnectionPool, NilClass] the redis connection

Public Class Methods

new(item, callback, redis_pool = nil) click to toggle source

@param [Hash] item the Sidekiq job hash @param [Proc] callback the callback to use after unlock @param [Sidekiq::RedisConnection, ConnectionPool] redis_pool the redis connection

# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 38
def initialize(item, callback, redis_pool = nil)
  @item       = item
  @callback   = callback
  @redis_pool = redis_pool
  @attempt    = 0
  prepare_item # Used to ease testing
  @lock_config = LockConfig.new(item)
end
validate_options(options = {}) click to toggle source

Validates that the sidekiq_options for the worker is valid

@param [Hash] options the sidekiq_options given to the worker

@return [void]

# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 27
def self.validate_options(options = {})
  Validator.validate(options)
end

Public Instance Methods

execute() click to toggle source

Execute the job in the Sidekiq server processor @raise [NotImplementedError] needs to be implemented in child class

# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 62
def execute
  raise NotImplementedError, "##{__method__} needs to be implemented in #{self.class}"
end
lock() click to toggle source

Locks a sidekiq job

@note Will call a conflict strategy if lock can't be achieved.

@return [String, nil] the locked jid when properly locked, else nil.

@yield to the caller when given a block

# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 56
def lock
  raise NotImplementedError, "##{__method__} needs to be implemented in #{self.class}"
end
locksmith() click to toggle source

The lock manager/client

@api private @return [SidekiqUniqueJobs::Locksmith] the locksmith for this sidekiq job

# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 72
def locksmith
  @locksmith ||= SidekiqUniqueJobs::Locksmith.new(item, redis_pool)
end

Private Instance Methods

call_strategy(origin:) { || ... } click to toggle source

Call whatever strategry that has been configured

@param [Symbol] origin: the origin `:client` or `:server`

@return [void] the return value is irrelevant

@yieldparam [void] if a new job id was set and a block is given @yieldreturn [void] the yield is irrelevant, it only provides a mechanism in

one specific situation to yield back to the middleware.
# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 119
def call_strategy(origin:)
  new_job_id = nil
  strategy   = strategy_for(origin)
  @attempt  += 1

  strategy.call { new_job_id = lock if strategy.replace? && @attempt < 2 }
  yield if new_job_id && block_given?
end
callback_safely() click to toggle source
# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 134
def callback_safely
  callback&.call
  item[JID]
rescue StandardError
  reflect(:after_unlock_callback_failed, item)
  raise
end
client_strategy() click to toggle source
# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 154
def client_strategy
  @client_strategy ||=
    OnConflict.find_strategy(lock_config.on_client_conflict).new(item, redis_pool)
end
prepare_item() click to toggle source

Eases testing by allowing the lock implementation to add the missing keys to the job hash.

@return [void] the return value should be irrelevant

# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 101
def prepare_item
  return if item.key?(LOCK_DIGEST)

  # The below should only be done to ease testing
  # in production this will be done by the middleware
  SidekiqUniqueJobs::Job.prepare(item)
end
server_strategy() click to toggle source
# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 159
def server_strategy
  @server_strategy ||=
    OnConflict.find_strategy(lock_config.on_server_conflict).new(item, redis_pool)
end
strategy_for(origin) click to toggle source
# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 142
def strategy_for(origin)
  case origin
  when :client
    client_strategy
  when :server
    server_strategy
  else
    raise SidekiqUniqueJobs::InvalidArgument,
          "#origin needs to be either `:server` or `:client`"
  end
end
unlock_and_callback() click to toggle source
# File lib/sidekiq_unique_jobs/lock/base_lock.rb, line 128
def unlock_and_callback
  return callback_safely if locksmith.unlock

  reflect(:unlock_failed, item)
end