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
- The exponential backoff example wrapped the factory in another function.
It failed to typecheck and threw "getNextRetryAt is not a function" on
the first retry.
- The fixed backoff example passed an options object; fixedBackoff() takes
a duration and threw "val.toLowerCase is not a function".
- The dedup section suggested ttl: 0 for no expiry, then said it is
rejected. It is rejected.
- The dedup section promised race-free Knex/Kysely dedup; the partial
unique index that makes it race-free only exists on PostgreSQL and
SQLite.
- The schedule options table now says .id() defaults to the job name, and
that scheduling the same job twice replaces the first schedule.
- Three JSDoc examples imported redis from the package root, which does
not export it.
A typed test mirrors the corrected backoff examples so a signature change
breaks the build.
- The dedup ID is automatically prefixed with the job name (`SendInvoiceJob::order-123`), so different job types can reuse the same key.
259
259
- The user-supplied `id` must be ≤ 400 characters, and the combined `<jobName>::<id>` key must be ≤ 510 characters (constrained by the Knex storage column). Both limits are validated at `.dedup()` time.
260
-
-`ttl` accepts a Duration (`'5s'`, `'1m'`) or milliseconds, and must be **positive** when provided. Use `0` or omit `ttl` if you want no expiry —`ttl: 0` is rejected to avoid an ambiguous "expired immediately vs no-expiry" interpretation across engines.
260
+
-`ttl` accepts a Duration (`'5s'`, `'1m'`) or milliseconds, and must be **positive** when provided. Omit `ttl` if you want no expiry.`ttl: 0` is rejected to avoid an ambiguous "expired immediately vs no-expiry" interpretation across engines.
261
261
-`extend` and `replace`**require**`ttl` — calling them without `ttl` throws.
262
262
-`replace` only applies to jobs in `pending` or `delayed` state. Jobs that are active (executing) or retained in history (`completed`/`failed` with retention) are left alone; the dispatch returns `{ deduped: 'skipped' }`.
263
263
-`replace` swaps the **payload only** — priority, queue, delay, groupId, and stored dedup options of the existing job are retained. To change those, use a different dedup id or wait for the TTL to expire.
264
264
-`extend` resets the TTL clock but never changes the window length. The window length is fixed to the `ttl` from the first dispatch that created the dedup slot. Later dispatches that pass a different `ttl` only reset the clock; their `ttl` value is ignored. To resize the window, let the slot expire and start over with a new dispatch.
265
265
-`extend` works in **all states** — even when the existing job is `active` (executing) or retained in history. Unlike `replace` (which is no-op on non-replaceable states), `extend` always refreshes the dedup TTL window. Use this when you want the dedup slot to keep blocking new dispatches for the lifetime of a long-running job.
266
266
-`extend` requires the **first** dispatch to have set a `ttl`. If the slot was created without a `ttl`, later `extend` dispatches have no window to refresh and return `{ deduped: 'skipped' }` instead of `'extended'`.
267
267
-`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.
268
-
-Atomic and race-free:
268
+
-Atomicity:
269
269
-**Redis**: a single Lua script per dispatch performs the dedup-key lookup, state check (pending/delayed ZSCORE), payload swap, and TTL refresh atomically.
270
-
-**Knex/Kysely**: transactional `SELECT ... FOR UPDATE` + insert/update inside a transaction. A savepoint catches unique-constraint violations under concurrent inserts and returns `{ deduped: 'skipped' }` pointing at the winner.
270
+
-**Knex/Kysely**: transactional `SELECT ... FOR UPDATE` + insert/update inside a transaction. On PostgreSQL and SQLite, a partial unique index makes concurrent first dispatches race-free: a savepoint catches the unique-constraint violation and returns `{ deduped: 'skipped' }` pointing at the winner. MySQL has no partial unique index, see the caveat below.
271
271
-**SyncAdapter**: executes inline, no dedup support.
0 commit comments