Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions docs/triggering.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```

<Note>
Debounce keys are scoped to the task identifier, so different tasks can use the same key without
conflicts.
</Note>

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;
},
});
```

<Note>
Idempotency keys take precedence over debounce keys. If both are provided and an idempotency match
is found, it wins.
</Note>

### `queue`

When you trigger a task you can override the concurrency limit. This is really useful if you sometimes have high priority runs.
Expand Down