Class: Sidekiq::NormalizeArgsMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/sidekiq/normalize_args_middleware.rb

Overview

Middleware to normalize hash arguments to use string keys consistently.

Sidekiq serializes job arguments to JSON, which always produces string keys.
However, the lock digest for sidekiq-unique-jobs is computed BEFORE serialization,
using the original Ruby hash. This means:

perform_async(delivery_id: 123) # symbol key
perform_async("delivery_id" => 123) # string key

Could produce different lock digests, causing duplicate jobs to slip through.

This middleware normalizes hash arguments to string keys on both client and server:

  • Client: Ensures consistent lock digest computation
  • Server: Ensures workers receive consistent hash format

Workers can still use symbol access via with_indifferent_access, but this
removes the ambiguity at the source.

THIS IS THE SINGLE NORMALIZATION PATH FOR ENQUEUED JOBS. If a worker is only
ever reached via perform_async/perform_in/perform_at, do NOT re-normalize
inside it — no options.symbolize_keys, no options.with_indifferent_access,
no options['x'] || options[:x] pairs. #perform already receives a
HashWithIndifferentAccess, so options[:force] and options['force'] both
resolve. A local copy of this logic reads as though the middleware is absent,
which is how PublicationVisionWorker got misdiagnosed as silently dropping
force: in 2026-07 (it never was).

IT DOES NOT COVER DIRECT Worker.new.perform(...) CALLS — no client push, no
server chain, so #perform gets exactly the Hash the caller built. Symbol keys
still read fine there; STRING keys do not. Fix the CALLER, not the worker: a
test hand-writing 'force' => true is asserting a shape production never
delivers, so pass symbol keys exactly like an enqueue site would. Direct
callers live in tests, rake tasks, and the dev-mode branches in
ItemsController#auto_translate_*.

Two more cases sit OUTSIDE the server chain:

  • self.lock_args(args) runs client-side, before the server half — read
    string keys there (see DeliveryShipConfirmWorker).
  • callers computing a digest by hand
    (SidekiqUniqueJobsJidLookup.active_jid_for_args) or searching stored args
    (BackgroundJobStatus.search) must pass .stringify_keys to match what
    the Client half wrote.

Covered by test/lib/sidekiq/normalize_args_middleware_test.rb, which also
guards that both halves stay registered in config/initializers/sidekiq.rb.

Defined Under Namespace

Classes: Client, Server