From 351155cdbe810d0e2c79befec903b8732f47a628 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Thu, 18 Dec 2025 14:34:58 +0000 Subject: [PATCH] chore(docs): debounce options --- docs/triggering.mdx | 91 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/docs/triggering.mdx b/docs/triggering.mdx index 599fe67e99..2096e8bf34 100644 --- a/docs/triggering.mdx +++ b/docs/triggering.mdx @@ -831,6 +831,97 @@ export const myTask = task({ For more information, see our [Idempotency](/idempotency) documentation. +### `debounce` + +You can debounce task triggers to consolidate multiple trigger calls into a single delayed run. When a run with the same debounce key already exists in the delayed state, subsequent triggers "push" the existing run's execution time later rather than creating new runs. + +This is useful for scenarios like: + +- Real-time document indexing where you want to wait for the user to finish typing +- Aggregating webhook events from the same source +- Rate limiting expensive operations while still processing the final request + +```ts +// First trigger creates a new run, delayed by 5 seconds +await myTask.trigger({ some: "data" }, { debounce: { key: "user-123", delay: "5s" } }); + +// If triggered again within 5 seconds, the existing run is pushed later +await myTask.trigger({ updated: "data" }, { debounce: { key: "user-123", delay: "5s" } }); + +// The run only executes after 5 seconds of no new triggers +// Note: The first payload is used (first trigger wins) +``` + + + Debounce keys are scoped to the task identifier, so different tasks can use the same key without + conflicts. + + +The `debounce` option accepts: + +- `key` - A unique string to identify the debounce group (scoped to the task) +- `delay` - Duration string specifying how long to delay (e.g., "5s", "1m", "30s") +- `mode` - Optional. Controls which trigger's data is used: `"leading"` (default) or `"trailing"` + +**How it works:** + +1. First trigger with a debounce key creates a new delayed run +2. Subsequent triggers with the same key (while the run is still delayed) push the execution time further +3. Once no new triggers occur within the delay duration, the run executes +4. After the run starts executing, a new trigger with the same key will create a new run + +**Leading vs Trailing mode:** + +By default, debounce uses **leading mode** - the run executes with data from the **first** trigger. + +With **trailing mode**, each subsequent trigger updates the run's data (payload, metadata, tags, maxAttempts, maxDuration, and machine), so the run executes with data from the **last** trigger: + +```ts +// Leading mode (default): runs with first payload +await myTask.trigger({ count: 1 }, { debounce: { key: "user-123", delay: "5s" } }); +await myTask.trigger({ count: 2 }, { debounce: { key: "user-123", delay: "5s" } }); +// After 5 seconds, runs with { count: 1 } + +// Trailing mode: runs with last payload +await myTask.trigger( + { count: 1 }, + { debounce: { key: "user-123", delay: "5s", mode: "trailing" } } +); +await myTask.trigger( + { count: 2 }, + { debounce: { key: "user-123", delay: "5s", mode: "trailing" } } +); +// After 5 seconds, runs with { count: 2 } +``` + +Use **trailing mode** when you want to process the most recent data, such as: + +- Saving the latest version of a document after edits stop +- Processing the final state after a series of rapid updates + +**With `triggerAndWait`:** + +When using `triggerAndWait` with debounce, the parent run blocks on the existing debounced run if one exists: + +```ts +export const parentTask = task({ + id: "parent-task", + run: async (payload: string) => { + // Both will wait for the same run + const result = await childTask.triggerAndWait( + { data: payload }, + { debounce: { key: "shared-key", delay: "3s" } } + ); + return result; + }, +}); +``` + + + Idempotency keys take precedence over debounce keys. If both are provided and an idempotency match + is found, it wins. + + ### `queue` When you trigger a task you can override the concurrency limit. This is really useful if you sometimes have high priority runs.