Reliable Fetch Strategy
Relevant source files
The following files were used as context for generating this wiki page:
The Reliable Fetch Strategy is an optional fetching mechanism introduced in sidekiq-unique-jobs v9 designed to provide enhanced crash recovery and lock-aware job acknowledgment. It replaces the default Sidekiq fetch mechanism to ensure that jobs are not lost if a worker process crashes during execution. By utilizing Redis LMOVE (or BLMOVE) operations, the strategy moves jobs atomically from a global queue to a per-process "working list," where they remain until the job is successfully completed and acknowledged.
This strategy is particularly critical for unique jobs because it ensures that the associated uniqueness locks are handled correctly during both normal execution and crash recovery scenarios. It integrates directly with the gem's locking system to validate locks at fetch time and clean them up during acknowledgment.
Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:1-20, README.md:124-135, CLAUDE.md:214-219
Architecture and Components
The Reliable Fetch Strategy consists of several integrated components that manage the lifecycle of a job from the moment it is pulled from the Redis queue until it is acknowledged as complete.
Core Components
| Component | Description |
|---|---|
SidekiqUniqueJobs::Fetch::Reliable | The main class responsible for orchestrating the fetch loop, heartbeats, and orphan recovery. |
UnitOfWork | A wrapper around a fetched job that manages its specific acknowledgment or requeue logic. |
Working List | A per-process Redis list where jobs reside while being processed. |
Heartbeat | A Redis key with a TTL that indicates a specific worker process is still alive. |
| Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:25-227, spec/sidekiq_unique_jobs/fetch/reliable_spec.rb:29-33 |
Fetch Logic Flow
The strategy employs a combination of non-blocking and blocking operations to retrieve work from Redis efficiently without spinning the CPU.
The strategy uses a Lua script for non-blocking fetches to perform LMOVE and lock validation in a single atomic operation. The final queue in the list is fetched using BLMOVE to allow the process to wait for new work without busy-waiting.
Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:64-96, lib/sidekiq_unique_jobs/fetch/reliable.rb:113-130
Atomic Operations and Data Safety
The primary advantage of the Reliable Fetch Strategy is the use of atomic Redis operations to prevent job loss.
Job Transfer (LMOVE)
Instead of RPOP, which removes the job from Redis entirely, Reliable Fetch uses LMOVE (or BLMOVE) to move the job from queue:name to uniquejobs:working:IDENTITY. If the Sidekiq process crashes before the job is done, the job remains in the per-process working list.
Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:11-15, spec/sidekiq_unique_jobs/fetch/reliable_spec.rb:34-40
Lock-Aware Acknowledgment
When a job completes, UnitOfWork#acknowledge is called. This does not just remove the job from the working list; it also triggers a Lua script to atomically:
- Remove the job from the per-process working list.
- Remove the JID from the
digest:LOCKEDhash. - Remove the digest from the global
uniquejobs:digestsZSET if the lock type requires it.
Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:207-226, spec/sidekiq_unique_jobs/fetch/reliable_spec.rb:42-63
Crash Recovery and Orphan Management
Reliable Fetch handles worker crashes through a combination of heartbeats and startup scans.
Heartbeat Mechanism
Each Reliable Fetch instance starts a background thread that periodically refreshes a heartbeat key in Redis.
- Interval: 20 seconds.
- TTL: 60 seconds.
- Key Pattern:
uniquejobs:heartbeat:HOSTNAME:PID:RANDOM_HEX.
Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:41-47, lib/sidekiq_unique_jobs/fetch/reliable.rb:157-179
Orphan Recovery Sequence
On startup, the process scans for working lists belonging to other processes.
Configuration and Setup
To enable the Reliable Fetch Strategy, it must be explicitly configured in the Sidekiq server block.
Server Implementation
Sidekiq.configure_server do |config|
config[:fetch_class] = SidekiqUniqueJobs::Fetch::Reliable
end
Sources: README.md:124-128
Key Constants and Parameters
| Constant | Value | Description |
|---|---|---|
TIMEOUT | 2 | Polling timeout in seconds for blocking operations. |
HEARTBEAT_INTERVAL | 20 | Frequency of heartbeat updates in seconds. |
HEARTBEAT_TTL | 60 | Time before a process is considered "dead" by others. |
Key.working | uniquejobs:working:ID | Namespace for the per-process job storage. |
| Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:36-47, spec/sidekiq_unique_jobs/fetch/reliable_spec.rb:112-117 |
Shutdown Behavior
During a graceful shutdown, the strategy ensures that jobs currently in the working list are returned to the global Redis queues so they can be picked up by other workers.
- Bulk Requeue: The
bulk_requeuemethod is called, which uses a Redis pipeline toRPUSHjobs back to their original queues andLREMthem from the per-process working list. - Lock Preservation: Unlike
acknowledge, therequeueaction (used during shutdown) does not delete the uniqueness locks. This ensures that the jobs remain "unique" even while waiting to be re-processed.
Sources: lib/sidekiq_unique_jobs/fetch/reliable.rb:99-119, spec/sidekiq_unique_jobs/fetch/reliable_spec.rb:83-107