Skip to content

PATTERN Cited by 2 sources

Feature-flagged job enqueue rejection

A deploy-less kill-switch for async-job classes: install a client middleware on the async-job framework that, on every enqueue, derives a per-class feature-flag name from the worker class and checks a general feature-flag system; if the flag is on, short-circuit the enqueue so the job never reaches the job store.

The pattern

# pseudocode — see Sidekiq Ruby example below
class JobKillMiddleware
  def call(worker_class, job, queue, store)
    # convention-driven flag name
    flag_name = "disable_#{underscore(worker_class.to_s)}"
    return false if feature_flag_enabled?(flag_name)
    yield                                 # otherwise pass through
  end
end

Wiring: install the middleware on the client chain of the job framework (the chain that runs when code enqueues a job), not the server chain (which runs when workers execute a job). Enqueue-side interception prevents backlog accumulation.

Four load-bearing design choices

  1. Client middleware (producer side), not server middleware (consumer side). Prevents enqueue; no jobs pile up in the store to be drained later. Disabling a job class while other classes in the same queue keep running is just the flag being per-class, not per-queue.
  2. Naming convention derives the flag name from the class name. No registry, no catalog, no manual wiring. The flag disable_invoice_job disables InvoiceJob without anyone having to configure that mapping. Operators can always answer "what's the flag for this job?" by running the class-name transform.
  3. General feature-flag system as the config channel. No bespoke kill-switch infrastructure. Whatever feature- flag system the application already uses for release engineering provides the distribution channel, the storage, and the invocation surface. One fewer subsystem to build and operate.
  4. Short-circuit by returning falsy. The job-framework middleware contract is "return falsy to stop the job from progressing." No exception; no retry storm; no entry in the store. The producer call (Worker.perform_async(...)) just returns false / nil as if the job were silently dropped, which it is — intentionally.

Canonical Sidekiq implementation (PlanetScale, 2022)

From sources/2026-04-21-planetscale-how-to-kill-sidekiq-jobs-in-ruby-on-rails:

module SidekiqMiddleware
  class SidekiqJobsFlipper
    def call(worker_class, job, queue, redis_pool)
      # return false/nil to stop the job from going to redis
      klass = worker_class.to_s
      if Flipper.enabled?("disable_#{klass.underscore.to_sym}")
        return false
      end
      yield
    end
  end
end

Wired in config/initializers/sidekiq.rb:

Sidekiq.configure_server do |config|
  config.client_middleware do |chain|
    chain.add(SidekiqMiddleware::SidekiqJobsFlipper)
  end
end

Operator invocation: Flipper.enable("disable_invoice_job") from a Rails console. Reverse: Flipper.disable(...).

When to use it

  • Production is running, some job class is misbehaving, and you need to stop enqueueing more of that class now without a deploy.
  • An incoming deploy is known to make a specific job class unsafe; flip the flag off pre-deploy, deploy, un-flip post-verification.
  • Running a one-off data backfill that shouldn't trigger side-effect jobs; flag off the side-effect-job class, run the backfill, flag on.
  • Canary rollout by job class: disable a new job class in production until ready, then enable.

When not to use it

  • When jobs are already in the queue. This pattern gates future enqueues; it doesn't drain existing jobs. Combine with a manual queue flush for incident response.
  • When you need argument-level granularity. Cannot disable InvoiceJob for account_id=42 specifically; all-or-nothing per class. Build a flag-check inside perform instead.
  • When you need an audit trail of attempts. The middleware silently drops enqueues; there's no record of the attempt unless you add explicit logging.
  • When callers depend on perform_async's job-id return value. Middleware returning false makes the caller see false instead of a job-id string — this may break callers that use the job-id for tracking.

Relationship to other patterns

Seen in

  • sources/2026-04-21-planetscale-how-to-kill-sidekiq-jobs-in-ruby-on-railscanonical wiki instance. PlanetScale's application- tier Rails backend installs SidekiqJobsFlipper on the Sidekiq client middleware chain, keyed by disable_<classname> Flipper flags, for deploy-less per-class job suspension in production.
  • sources/2026-04-21-planetscale-how-we-made-planetscales-background-jobs-self-healingcanonical composition-with-scheduler instance. The same Flipper-gated middleware is used to disable scheduler jobs during an incident: "This has come in useful during an incident where we've wanted control over a specific job type." Scheduler jobs are themselves Sidekiq jobs, so subject to the same deploy-less operational lever as any other job. Shows the pattern operating at two altitudes simultaneously in the same system — disable a work job or disable the scheduler that enqueues it.
Last updated · 378 distilled / 1,213 read