Creating Custom Lock Types
Relevant source files
The following files were used as context for generating this wiki page:
Custom lock types allow developers to extend the sidekiq-unique-jobs framework by defining specialized uniqueness behaviors beyond the built-in strategies like until_executed or while_executing. This extensibility is achieved by inheriting from the base lock architecture and registering the new type within the global configuration.
Custom locks integrate with the Sidekiq middleware stack, utilizing the existing Locksmith for Redis communication and the conflict resolution system to handle duplicate job attempts.
Sources: CLAUDE.md:33-45, spec/sidekiq_unique_jobs/configuration_spec.rb:7-40
Architecture and Base Implementation
All custom lock types must inherit from SidekiqUniqueJobs::Lock::BaseLock. This abstract base class provides the necessary infrastructure for interacting with the job hash, configuration, and Redis.
The BaseLock Class
The BaseLock class defines two primary methods that must be implemented by any custom subclass:
lock: Defines the logic for attempting to acquire a lock.execute: Defines how the job should be executed within the context of the lock.
It also provides internal helper methods for conflict resolution via call_strategy(origin:) and callback handling via unlock_and_callback.
Sources: lib/sidekiq_unique_jobs/lock/base_lock.rb:49-62, CLAUDE.md:46-48
The diagram shows the inheritance relationship between the abstract base class and a custom implementation, along with its core dependencies.
Sources: lib/sidekiq_unique_jobs/lock/base_lock.rb:7-80
Custom Lock Components
When creating a custom lock, several components must be coordinated to ensure proper Sidekiq integration.
Initialization and Configuration
Custom locks receive a Sidekiq job hash (item), a callback proc, and an optional Redis pool during initialization. The LockConfig object extracts relevant settings such as lock_ttl, lock_timeout, and on_conflict strategies specifically for that job.
Sources: lib/sidekiq_unique_jobs/lock/base_lock.rb:39-47, lib/sidekiq_unique_jobs/lock_config.rb:63-74
Lock Management (Locksmith)
The Locksmith serves as the interface between the lock logic and Redis. It handles the execution of Lua scripts to perform atomic operations. Custom locks typically delegate the actual Redis interaction to the locksmith instance provided by the base class.
Sources: lib/sidekiq_unique_jobs/locksmith.rb:41-65, lib/sidekiq_unique_jobs/lock/base_lock.rb:69-71
| Method | Role in Custom Lock |
|---|---|
lock | Should attempt to acquire the lock and call call_strategy(origin: :client) on failure. |
execute | Wraps the job execution block, often calling locksmith.execute. |
unlock_and_callback | Standardized method to release the lock and trigger after_unlock hooks. |
Sources: lib/sidekiq_unique_jobs/lock/base_lock.rb:54-106, CLAUDE.md:46-48
Implementation Flow
A custom lock implementation typically follows a sequence of checking for uniqueness on the client side (enqueue) or server side (execution).
This diagram illustrates the logic flow when a custom lock is invoked during the client middleware phase.
Sources: lib/sidekiq_unique_jobs/lock/base_lock.rb:98-115, lib/sidekiq_unique_jobs/locksmith.rb:52-64
Registration and Usage
Once a custom lock class is defined, it must be registered with the global configuration before it can be used in sidekiq_options.
Registering the Lock
Registration is performed via the SidekiqUniqueJobs::Config#add_lock method. This adds the lock to the internal registry, making it available to the middleware.
Sources: spec/sidekiq_unique_jobs/config_spec.rb:108-115, spec/sidekiq_unique_jobs/configuration_spec.rb:62-65
# 1. Define the custom lock
class MyCustomLock < SidekiqUniqueJobs::Lock::BaseLock
def lock
# implementation
end
def execute
# implementation
end
end
# 2. Register in initializer
SidekiqUniqueJobs.configure do |config|
config.add_lock :my_custom, MyCustomLock
end
# 3. Use in worker
class MyJob
include Sidekiq::Job
sidekiq_options lock: :my_custom
end
Sources: spec/sidekiq_unique_jobs/configuration_spec.rb:23-35, spec/sidekiq_unique_jobs/config_spec.rb:113-115
Conflict Handling
Custom locks leverage the on_conflict configuration. When a custom lock fails to acquire, it should invoke the configured strategy (e.g., :log, :raise, :reschedule). The LockConfig object handles the normalization of these strategies for both client and server contexts.
Sources: lib/sidekiq_unique_jobs/lock_config.rb:118-132, lib/sidekiq_unique_jobs/lock/base_lock.rb:118-130
Summary
Creating custom lock types involves inheriting from SidekiqUniqueJobs::Lock::BaseLock, implementing the core lock and execute methods, and registering the class via config.add_lock. This approach allows for highly specific uniqueness requirements while maintaining compatibility with the gem's internal metrics, Redis management, and Sidekiq middleware integration.
Sources: lib/sidekiq_unique_jobs/lock/base_lock.rb:7-30, spec/sidekiq_unique_jobs/config_spec.rb:104-120