You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
> Without `.dedup()`, jobs use auto-generated UUIDs and are never deduplicated. The `.dedup()` method is only available on single dispatch, not `dispatchMany`.
165
+
### Debounce (replace payload + reset TTL)
166
+
167
+
```typescript
168
+
// Within the 2s window, the latest payload overwrites the previous pending job.
// jobId: the UUID of the job (the existing one when deduped)
185
+
```
186
+
187
+
### How it works
188
+
189
+
- The dedup ID is automatically prefixed with the job name (`SendInvoiceJob::order-123`), so different job types can reuse the same key.
190
+
-`ttl` accepts a Duration (`'5s'`, `'1m'`) or milliseconds.
191
+
-`extend` and `replace`**require**`ttl` — calling them without `ttl` throws.
192
+
-`replace` only applies to jobs in `pending` or `delayed` state. Active (executing) jobs are left alone; the dispatch returns `{ deduped: 'skipped' }`.
193
+
-`replace` swaps the **payload only** — priority, queue, delay, and groupId of the existing job are retained. To change those, use a different dedup id or wait for the TTL to expire.
194
+
-`retryJob` does not touch the dedup entry — a retried job continues to occupy the dedup slot. TTL runs on wall-clock time, so long-running retries may outlive the TTL window. Use a generous TTL or no TTL if retries must stay deduped.
195
+
- Atomic and race-free:
196
+
-**Redis**: `SET + PEXPIRE` under a Lua script with `HSETNX`-style guards.
197
+
-**Knex**: transactional `SELECT ... FOR UPDATE` + insert/update inside a transaction.
198
+
-**SyncAdapter**: executes inline, no dedup support.
199
+
200
+
### Caveats
163
201
164
-
> [!TIP]
165
-
> When job retention is enabled (`removeOnComplete: false`), completed jobs remain in storage. A re-dispatch with the same dedup ID will be silently skipped since the record still exists. With the default retention (`true`), completed jobs are removed immediately, so re-dispatch with the same ID succeeds normally.
202
+
- Without `.dedup()`, jobs use auto-generated UUIDs and are never deduplicated.
203
+
-`.dedup()` is only available on single dispatch. `dispatchMany` / `pushManyOn` reject jobs with a `dedup` field.
204
+
- Scheduled jobs (`.schedule()`) do not support dedup — each cron/interval fire is an independent dispatch.
205
+
- With no `ttl`, dedup persists until the job is removed (completed/failed without retention). When retention keeps the record, re-dispatch stays blocked until the record is pruned.
206
+
- With `ttl`, dedup expires after the window — a new job (new UUID) is created. The old job still runs.
207
+
- Knex concurrent race: two `pushOn` calls with the same dedup id firing at the exact same instant on Postgres (READ COMMITTED) can both succeed (rare). Serialize at the app layer if strict guarantees are required, or use Redis.
166
208
167
209
## Job History & Retention
168
210
@@ -569,7 +611,7 @@ import * as boringqueue from '@boringnode/queue'
569
611
570
612
const instrumentation =newQueueInstrumentation({
571
613
messagingSystem: 'boringqueue', // default
572
-
executionSpanLinkMode: 'link', // or 'parent'
614
+
executionSpanLinkMode: 'link', // or 'parent'
573
615
})
574
616
575
617
instrumentation.enable()
@@ -582,19 +624,19 @@ The instrumentation patches `QueueManager.init()` to automatically inject its wr
582
624
583
625
The instrumentation uses standard [OTel messaging semantic conventions](https://opentelemetry.io/docs/specs/semconv/messaging/messaging-spans/) where they map cleanly, plus a few queue-specific custom attributes.
0 commit comments