Getting started

# Quick start

From a plain Sidekiq worker to a deduplicated one in five minutes — write it, watch a duplicate get dropped, then change what happens instead.

## Before you start

This page assumes the middleware is already wired into your Sidekiq initializer. If you haven't done that yet, it's a one-time copy-paste — see [Installation](https://sidekiq-unique-jobs.zoolutions.llc/docs/installation). Nothing on this page works until the client and server middleware are registered.

## 1. Write a unique worker

One line of sidekiq_options opts a worker into uniqueness.

Take an ordinary worker and add a `lock:`. We'll use `:until_executed`, which holds the lock from the moment the job is enqueued until `perform` finishes — so no duplicate can slip in for the entire lifecycle of the job.

```ruby
class SendWelcomeEmailJob
  include Sidekiq::Job

  sidekiq_options lock: :until_executed

  def perform(user_id)
    user = User.find(user_id)
    WelcomeMailer.welcome(user).deliver_now
  end
end
```

That's the whole change. Uniqueness is keyed on the worker class, the queue, and the arguments — so `SendWelcomeEmailJob.perform_async(1)` and `SendWelcomeEmailJob.perform_async(2)` are two independent jobs, while a second `perform_async(1)` fired before the first has run is a duplicate.

## 2. Enqueue it twice

The second push, while the first is locked, is dropped.

Enqueue the same arguments twice in a row. The first call acquires the lock and returns a job ID; the second finds the lock already held and is discarded, returning `nil`.

```ruby
SendWelcomeEmailJob.perform_async(1) # => "a1b2c3..."  enqueued, lock acquired
SendWelcomeEmailJob.perform_async(1) # => nil          duplicate, dropped
SendWelcomeEmailJob.perform_async(2) # => "d4e5f6..."  different args, enqueued
```

Dropping the duplicate is the default because no `on_conflict` strategy was set — the conflict is logged and the extra job is discarded. Once the first job runs to completion the lock is released, and `perform_async(1)` will enqueue normally again.

> **Tip:** Uniqueness is enforced even in tests. Sidekiq::Testing.inline! runs jobs immediately with locking still in effect, so you can drive this exact behavior in a console or spec.

## 3. Change what happens on a conflict

Reschedule the duplicate instead of dropping it.

Dropping is not always what you want. Add `on_conflict:` to choose a different outcome. With `:reschedule`, the duplicate isn't thrown away — it's re-enqueued to run later, so it still happens once the lock clears.

```ruby
class SendWelcomeEmailJob
  include Sidekiq::Job

  sidekiq_options lock: :until_executed, on_conflict: :reschedule

  def perform(user_id)
    user = User.find(user_id)
    WelcomeMailer.welcome(user).deliver_now
  end
end
```

Now the second `perform_async(1)` is re-enqueued to run later rather than dropped. The other strategies cover the rest of the spectrum: `:raise` makes Sidekiq retry the job, `:reject` sends the duplicate to the Dead set, and `:replace` deletes the original and enqueues the new one. See [Conflict resolution](https://sidekiq-unique-jobs.zoolutions.llc/docs/conflict-resolution) for the full table.

## Where to go next

- **[Choosing a lock type](https://sidekiq-unique-jobs.zoolutions.llc/docs/choosing-a-lock-type)** — a decision table from "I want to prevent X" to the lock that does it. `:until_executed` is one of five.
- **[Use cases](https://sidekiq-unique-jobs.zoolutions.llc/docs/debounce-duplicate-enqueues)** — start with debouncing duplicate enqueues; each use-case page is a complete, working worker you can lift straight into your app.