Keyed debounce
Replace pending work under a stable key while updates continue arriving.
Debounce keeps one pending job with the latest accepted payload. PostgreSQL serializes concurrent replacements, so every caller observes one stable job identity.
const result = await queue.enqueueWithResult("search.reindex", { documentId, revision }, {
debounce: { key: documentId, scope: "search-index", windowMs: quietPeriodMs, schedule: "reset" },
});Choose reset to start a fresh quiet period after every replacement. Choose preserve when the
first request fixes the run time and later requests should update only the payload.
result.outcome is accepted for a new job and replaced for a pending update. Once a worker owns
the job, it becomes terminal, or its window elapses, PostgreSQL returns non_replaceable and keeps
the accepted payload unchanged.
Only scheduled or ready jobs can be replaced. A refused replacement includes a stable reason
so callers can distinguish lifecycle movement from an incompatible request.
Debounce cannot share a request with idempotency, throttle, or dependencies because each mechanism assigns a different meaning to a repeated key or immutable edge.
The result's jobId identifies the retained pending job. When replacement depends on earlier work,
prerequisiteJobId identifies that immutable dependency instead of the debounce candidate.
Next
- Throttle — reuse equivalent work without replacing it
- Idempotent enqueue — replay an identical request
- Job dependencies — keep accepted work blocked
Exact debounce outcomes, limits, and lifecycle events: architecture reference.