Middleware Architecture
Relevant source files
The following files were used as context for generating this wiki page:
Introduction
The Middleware Architecture in sidekiq-unique-jobs is the core mechanism that intercepts job lifecycles to enforce uniqueness. It consists of two primary components: the Client Middleware, which intercepts jobs during the enqueuing/push phase, and the Server Middleware, which intercepts jobs during the execution phase. Together, these components ensure that jobs are only processed according to the configured lock strategies, preventing duplicate enqueuing or concurrent execution based on unique digests.
Sources: CLAUDE.md:37-41, lib/sidekiq_unique_jobs/middleware/client.rb, lib/sidekiq_unique_jobs/middleware/server.rb
Core Components and Structure
The architecture relies on a shared base module and specific implementations for Sidekiq's client and server stacks.
Middleware Base Module
The SidekiqUniqueJobs::Middleware module provides shared logic for both client and server implementations. It handles the initial job preparation via SidekiqUniqueJobs::Job.prepare(item) and wraps the execution in a logging context. It also checks if uniqueness is disabled or if the job has been rescheduled before proceeding with lock logic.
Sources: lib/sidekiq_unique_jobs/middleware.rb:33-47
Implementation Classes
| Class | Type | Responsibility |
|---|---|---|
SidekiqUniqueJobs::Middleware::Client | Client | Intercepts job enqueuing to prevent duplicates in Redis. |
SidekiqUniqueJobs::Middleware::Server | Server | Intercepts job execution to prevent concurrent processing. |
Sources: CLAUDE.md:37-41, lib/sidekiq_unique_jobs/middleware/client.rb:9-11, lib/sidekiq_unique_jobs/middleware/server.rb:9-11
Data Flow and Logic
The middleware utilizes a lock_instance (provided by Locksmith) to manage the state of the job in Redis.
Enqueue Flow (Client)
When a job is pushed, the Client middleware attempts to acquire a lock. If the lock is successful, the job is allowed to proceed to the Sidekiq queue; otherwise, the conflict strategy is triggered (e.g., logging or rejecting the job).
Execution Flow (Server)
The Server middleware wraps the actual worker performance. It ensures the lock is handled according to the specific lock type (e.g., until_executed holds the lock until the block finishes, while until_executing might have already released it).
Job Preparation and Logging
Before any lock operation, the middleware prepares the job hash. The SidekiqUniqueJobs::Job.prepare method adds metadata including lock_digest, lock_expiration, and lock_prefix.
Sources: lib/sidekiq_unique_jobs/job.rb:18-34, lib/sidekiq_unique_jobs/middleware.rb:41
Contextual Logging
The Middleware::Logging module provides context-aware logging. It detects the logging environment (Sidekiq version) and injects job-specific metadata like the lock_digest and lock_type into the log context.
def logging_context
middleware = is_a?(SidekiqUniqueJobs::Middleware::Client) ? :client : :server
digest = item[LOCK_DIGEST]
lock_type = item[LOCK]
if logger_context_hash?
{ "uniquejobs" => middleware, lock_type => digest }
else
"uniquejobs-#{middleware} #{"DIG-#{digest}" if digest}"
end
end
Sources: lib/sidekiq_unique_jobs/logging/middleware_context.rb:33-43, lib/sidekiq_unique_jobs/logging.rb:159-173
Middleware Configuration
Middleware must be manually added to the Sidekiq configuration chain. This is typically done in a Sidekiq initializer.
| Stack | Configuration Method | Middleware Class |
|---|---|---|
| Client | config.client_middleware | SidekiqUniqueJobs::Middleware::Client |
| Server | config.server_middleware | SidekiqUniqueJobs::Middleware::Server |
Sources: README.md:24-43, CLAUDE.md:41
Conclusion
The Middleware Architecture serves as the gatekeeper for uniqueness in the sidekiq-unique-jobs ecosystem. By prepending common logic and providing specialized client/server hooks, it ensures that Redis locks are consistently applied, managed, and released throughout the job's lifecycle, while maintaining high observability through context-aware logging.