Locksmith Lifecycle Management
Relevant source files
The following files were used as context for generating this wiki page:
Locksmith Lifecycle Management refers to the orchestration of Redis-based locks within the sidekiq-unique-jobs gem. The system ensures job uniqueness by managing the transition of locks from acquisition through execution to release or expiration. The central component, SidekiqUniqueJobs::Locksmith, interfaces with Redis via optimized Lua scripts to provide atomic operations that prevent race conditions during the Sidekiq job lifecycle.
Sources: lib/sidekiq_unique_jobs/locksmith.rb, CLAUDE.md:31-38
Architecture and Components
The lifecycle management system relies on three primary data structures in Redis: a digest string key, a LOCKED hash containing job-specific metadata, and a digests sorted set (ZSET) used for tracking and orphan cleanup. The Locksmith acts as the coordinator between the Sidekiq middleware and these Redis entities.
Sources: autoresearch.md:92-108, lib/sidekiq_unique_jobs/lock.rb:58-63
Key Classes
| Class | Responsibility |
|---|---|
Locksmith | Manages high-level lock actions (lock, execute, unlock) using Lua scripts. |
Lock | Provides a read/write interface to lock information and metadata stored in Redis. |
LockConfig | Normalizes configuration (TTL, timeout, limit) from job options and global defaults. |
Key | Handles the generation and namespacing of Redis keys based on the unique digest. |
Sources: lib/sidekiq_unique_jobs/locksmith.rb:8-53, lib/sidekiq_unique_jobs/lock.rb:10-15, lib/sidekiq_unique_jobs/lock_config.rb:11-53
Data Flow Overview
The following diagram illustrates the interaction between the Locksmith, LockConfig, and Redis during a standard lock acquisition and release cycle.
Lock Acquisition Lifecycle
Lock acquisition is the first phase of the lifecycle, typically triggered by the Client middleware during enqueuing. The Locksmith uses the lock method to attempt acquisition.
Atomicity and Lua Integration
To minimize Redis round-trips and ensure consistency, acquisition is performed via a lock Lua script. The script performs the following operations atomically:
- Checks the current lock count against the
lock_limitusingHLEN. - Creates the digest key.
- Adds the digest to the
uniquejobs:digestsZSET. - Stores job metadata in the
{digest}:LOCKEDhash usingHSET.
Sources: autoresearch.md:92-100, lib/sidekiq_unique_jobs/locksmith.rb:71-85
Lock Configuration Parameters
Acquisition behavior is governed by the LockConfig extracted from the Sidekiq job hash.
| Parameter | Type | Default | Description |
|---|---|---|---|
lock_limit | Integer | 1 | Concurrent jobs allowed for the same digest. |
lock_timeout | Integer | 0 | Seconds to wait for a lock; 0 is non-blocking. |
lock_ttl | Integer | nil | Time-to-live for the lock in seconds. |
lock_info | Boolean | false | Whether to store extended metadata for debugging. |
Sources: lib/sidekiq_unique_jobs/lock_config.rb:55-64, README.md:73-84
Execution and Conflict Resolution
Once a lock is acquired, the Locksmith#execute method provides a wrapper for the job's logic. If the lock cannot be acquired, the system triggers conflict resolution strategies defined in LockConfig.
Execution Flow
Lock Release and Cleanup
The final phase of the lifecycle is the release of the lock, which can occur via manual unlocking, job completion, or expiration.
Unlocking Logic
The unlock method invokes a Lua script that cleans up Redis resources. In v9, this process has been optimized to batch UNLINK operations.
- Selective Unlock: Removes only the specific
job_idfrom theLOCKEDhash. - Full Cleanup: If no job IDs remain in the hash, the script removes the digest from the ZSET and deletes the hash key entirely.
- Force Delete: The
delete!method bypasses standard checks (likeuntil_expiredPTTL) to immediately purge all keys associated with a digest.
Sources: lib/sidekiq_unique_jobs/locksmith.rb:107-133, lib/sidekiq_unique_jobs/lock.rb:65-79, autoresearch.md:102-108
Reflection and Metrics
Throughout the lifecycle, the Locksmith emits reflection events and records metrics via LockMetrics. This allows for observability into lock success, failure, and release rates.
reflect(:locked, item)reflect(:unlocked, item)LockMetrics.record(:lock_failed, item)
Sources: lib/sidekiq_unique_jobs/locksmith.rb:74-84, lib/sidekiq_unique_jobs/locksmith.rb:156-160
Implementation Detail: Metadata Storage
When a lock is acquired, the Locksmith dumps a JSON payload into the Redis LOCKED hash. This metadata is crucial for the Web UI and debugging.
# lib/sidekiq_unique_jobs/locksmith.rb:180-192
def lock_metadata
dump_json(
WORKER => item[CLASS],
QUEUE => item[QUEUE],
LIMIT => item[LOCK_LIMIT],
TIMEOUT => item[LOCK_TIMEOUT],
TTL => item[LOCK_TTL],
TYPE => config.type,
LOCK_ARGS => item[LOCK_ARGS],
TIME => now_f,
AT => item[AT],
)
end
Sources: lib/sidekiq_unique_jobs/locksmith.rb:180-192, lib/sidekiq_unique_jobs/lock.rb:58-63
Summary
Locksmith Lifecycle Management ensures that every Sidekiq job adhering to uniqueness constraints follows a strict operational path. By centralizing logic in the Locksmith and utilizing atomic Lua scripts, the system maintains high performance while preventing duplicate executions. The transition from acquisition (enqueuing) to release (completion or expiration) is governed by granular configuration and monitored through a robust reflection system.