@trigger.dev/sdk 4.7.3 → 4.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commonjs/v3/ai.d.ts +26 -0
- package/dist/commonjs/v3/ai.js +7 -5
- package/dist/commonjs/v3/ai.js.map +1 -1
- package/dist/commonjs/v3/concurrencyLimits.d.ts +37 -7
- package/dist/commonjs/v3/concurrencyLimits.js +38 -70
- package/dist/commonjs/v3/concurrencyLimits.js.map +1 -1
- package/dist/commonjs/v3/index.d.ts +1 -1
- package/dist/commonjs/v3/index.js.map +1 -1
- package/dist/commonjs/v3/query.d.ts +434 -1
- package/dist/commonjs/v3/query.js +115 -0
- package/dist/commonjs/v3/query.js.map +1 -1
- package/dist/commonjs/v3/runs.d.ts +22 -1
- package/dist/commonjs/v3/runs.js +6 -3
- package/dist/commonjs/v3/runs.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/esm/v3/ai.d.ts +26 -0
- package/dist/esm/v3/ai.js +7 -5
- package/dist/esm/v3/ai.js.map +1 -1
- package/dist/esm/v3/concurrencyLimits.d.ts +37 -7
- package/dist/esm/v3/concurrencyLimits.js +38 -70
- package/dist/esm/v3/concurrencyLimits.js.map +1 -1
- package/dist/esm/v3/index.d.ts +1 -1
- package/dist/esm/v3/index.js.map +1 -1
- package/dist/esm/v3/query.d.ts +434 -1
- package/dist/esm/v3/query.js +115 -0
- package/dist/esm/v3/query.js.map +1 -1
- package/dist/esm/v3/runs.d.ts +22 -1
- package/dist/esm/v3/runs.js +6 -3
- package/dist/esm/v3/runs.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/docs/ai-chat/actions.mdx +6 -1
- package/docs/ai-chat/backend.mdx +47 -0
- package/docs/ai-chat/patterns/redacting-telemetry.mdx +117 -0
- package/docs/ai-chat/patterns/sub-agents.mdx +33 -0
- package/docs/ai-chat/pending-messages.mdx +2 -0
- package/docs/ai-chat/reference.mdx +53 -2
- package/docs/ai-chat/transcript-storage.mdx +1 -1
- package/docs/apikeys.mdx +9 -7
- package/docs/billing-limits.mdx +1 -1
- package/docs/cli-env-commands.mdx +5 -4
- package/docs/cli-init-commands.mdx +27 -1
- package/docs/cli-login-commands.mdx +20 -0
- package/docs/cli-projects-commands.mdx +13 -1
- package/docs/cli-runs-commands.mdx +3 -2
- package/docs/cli-tasks-commands.mdx +95 -0
- package/docs/cli-update-commands.mdx +9 -3
- package/docs/cli-whoami-commands.mdx +12 -0
- package/docs/concurrency.mdx +32 -0
- package/docs/config/config-file.mdx +58 -0
- package/docs/config/extensions/audioWaveform.mdx +4 -1
- package/docs/config/extensions/overview.mdx +2 -0
- package/docs/config/extensions/prismaExtension.mdx +6 -7
- package/docs/config/extensions/puppeteer.mdx +3 -5
- package/docs/config/extensions/pythonExtension.mdx +9 -1
- package/docs/config/extensions/syncEnvVars.mdx +6 -0
- package/docs/deploy-environment-variables.mdx +5 -1
- package/docs/deployment/atomic-deployment.mdx +5 -3
- package/docs/deployment/dev-branches.mdx +5 -5
- package/docs/deployment/overview.mdx +9 -8
- package/docs/deployment/preview-branches.mdx +7 -4
- package/docs/errors-retrying.mdx +4 -4
- package/docs/github-actions.mdx +3 -3
- package/docs/how-it-works.mdx +5 -5
- package/docs/introduction.mdx +6 -0
- package/docs/limits.mdx +32 -8
- package/docs/logging.mdx +1 -1
- package/docs/machines.mdx +1 -1
- package/docs/manual-setup.mdx +1 -1
- package/docs/mcp-introduction.mdx +8 -7
- package/docs/mcp-tools.mdx +113 -3
- package/docs/migrating-from-v3.mdx +1 -1
- package/docs/observability/dashboards.mdx +64 -0
- package/docs/observability/query.mdx +6 -2
- package/docs/private-networking/aws-console-setup.mdx +1 -1
- package/docs/private-networking/overview.mdx +1 -1
- package/docs/quick-start.mdx +3 -40
- package/docs/realtime/auth.mdx +1 -1
- package/docs/realtime/backend/input-streams.mdx +159 -0
- package/docs/realtime/backend/streams.mdx +25 -10
- package/docs/realtime/how-it-works.mdx +1 -1
- package/docs/realtime/react-hooks/overview.mdx +1 -1
- package/docs/realtime/react-hooks/streams.mdx +2 -2
- package/docs/realtime/react-hooks/subscribe.mdx +19 -3
- package/docs/realtime/react-hooks/triggering.mdx +2 -2
- package/docs/realtime/react-hooks/use-wait-token.mdx +1 -1
- package/docs/realtime/run-object.mdx +47 -24
- package/docs/replaying.mdx +26 -1
- package/docs/reports.mdx +4 -2
- package/docs/run-tests.mdx +2 -2
- package/docs/run-usage.mdx +3 -3
- package/docs/runs/heartbeats.mdx +2 -2
- package/docs/runs/list-best-practices.mdx +234 -0
- package/docs/runs/max-duration.mdx +4 -0
- package/docs/runs/metadata.mdx +7 -10
- package/docs/runs.mdx +18 -8
- package/docs/self-hosting/env/supervisor.mdx +3 -0
- package/docs/self-hosting/env/webapp.mdx +5 -0
- package/docs/self-hosting/overview.mdx +26 -0
- package/docs/tags.mdx +6 -0
- package/docs/tasks/scheduled.mdx +11 -5
- package/docs/triggering.mdx +8 -0
- package/docs/troubleshooting-alerts.mdx +102 -3
- package/docs/troubleshooting.mdx +1 -1
- package/docs/upgrading-packages.mdx +3 -3
- package/docs/vercel-integration.mdx +13 -3
- package/docs/versioning.mdx +1 -1
- package/docs/wait-for-token.mdx +3 -3
- package/docs/webhooks/channels.mdx +158 -0
- package/docs/webhooks/connect.mdx +84 -0
- package/docs/webhooks/deliveries.mdx +103 -0
- package/docs/webhooks/filters.mdx +144 -0
- package/docs/webhooks/human-in-the-loop.mdx +162 -0
- package/docs/webhooks/linear.mdx +190 -0
- package/docs/webhooks/overview.mdx +119 -0
- package/docs/webhooks/session-routing.mdx +145 -0
- package/docs/webhooks/slack.mdx +397 -0
- package/docs/webhooks/sources.mdx +221 -0
- package/docs/webhooks/waiters.mdx +194 -0
- package/package.json +3 -2
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Using runs.list() efficiently"
|
|
3
|
+
sidebarTitle: "runs.list() best practices"
|
|
4
|
+
description: "When to use runs.list(), the patterns to avoid, and what to use instead."
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`runs.list()` (and `GET /api/v1/runs`) is built for browsing, reporting, and bulk operations. It is not a coordination tool. Each call searches your environment's runs in the requested time window, so its cost depends on how many runs you created in that window much more than on the page size you ask for.
|
|
8
|
+
|
|
9
|
+
A call with `limit: 4` in a busy environment can still search millions of runs to find those 4. That's fine once in a while. On a hot path, like before every trigger or in a loop every second, it slows your own code down and counts against your [API rate limit](/limits).
|
|
10
|
+
|
|
11
|
+
## Quick reference
|
|
12
|
+
|
|
13
|
+
| You want to… | Avoid | Use instead |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| Avoid piling up runs for the same work | `runs.list()` then `trigger()` | [`debounce`](/triggering#debounce) to merge a burst into one run, a [concurrency limit](/concurrency) so runs queue instead of overlapping, or an [`idempotencyKey`](/idempotency) for one run per item |
|
|
16
|
+
| Collapse a burst of triggers into one run | A fixed `delay` on every trigger | [`debounce`](/triggering#debounce) |
|
|
17
|
+
| Avoid duplicate runs for the same work | A random `idempotencyKey` per trigger | A key derived from the work itself |
|
|
18
|
+
| Wait for a run you triggered | Polling `runs.list()` or `runs.retrieve()` | [`triggerAndWait()`](/triggering#yourtask-triggerandwait) inside a task, or [`runs.subscribeToRun()`](/realtime/backend/subscribe) |
|
|
19
|
+
| Follow a group of runs | Polling `runs.list({ tag })` | [`runs.subscribeToRunsWithTag()`](/realtime/backend/subscribe) |
|
|
20
|
+
| Know how backed up a queue is | Counting `QUEUED` runs with `runs.list()` | [`queues.retrieve()`](/queues#retrieving-a-queue) |
|
|
21
|
+
|
|
22
|
+
## Anti-patterns
|
|
23
|
+
|
|
24
|
+
### Checking for active runs before triggering
|
|
25
|
+
|
|
26
|
+
This pattern tries to make sure only one run handles a piece of work at a time:
|
|
27
|
+
|
|
28
|
+
```ts Avoid
|
|
29
|
+
import { runs, tasks } from "@trigger.dev/sdk";
|
|
30
|
+
|
|
31
|
+
// Called every time something changes for this account
|
|
32
|
+
export async function onAccountChanged(accountId: string) {
|
|
33
|
+
const active = await runs.list({
|
|
34
|
+
status: ["QUEUED", "EXECUTING"],
|
|
35
|
+
taskIdentifier: ["sync-account"],
|
|
36
|
+
tag: [`account:${accountId}`],
|
|
37
|
+
limit: 1,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (active.data.length > 0) return;
|
|
41
|
+
|
|
42
|
+
await tasks.trigger(
|
|
43
|
+
"sync-account",
|
|
44
|
+
{ accountId },
|
|
45
|
+
{ tags: [`account:${accountId}`] }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
It has three problems:
|
|
51
|
+
|
|
52
|
+
- **It races.** Two calls can both see "nothing running" and both trigger.
|
|
53
|
+
- **It's expensive.** Every change runs a search, and the search gets slower as you create more runs.
|
|
54
|
+
- **It doesn't collapse bursts.** 100 changes in a second still mean 100 searches.
|
|
55
|
+
|
|
56
|
+
Let Trigger.dev do the coordination instead.
|
|
57
|
+
|
|
58
|
+
**To collapse bursts into one run, use `debounce`.** While a debounced run is still waiting for its delay, triggers with the same key push it back instead of creating new runs:
|
|
59
|
+
|
|
60
|
+
```ts Use debounce
|
|
61
|
+
import { tasks } from "@trigger.dev/sdk";
|
|
62
|
+
|
|
63
|
+
export async function onAccountChanged(accountId: string) {
|
|
64
|
+
await tasks.trigger(
|
|
65
|
+
"sync-account",
|
|
66
|
+
{ accountId },
|
|
67
|
+
{
|
|
68
|
+
debounce: {
|
|
69
|
+
key: `account-${accountId}`,
|
|
70
|
+
delay: "10s",
|
|
71
|
+
// The run uses the payload of the last trigger
|
|
72
|
+
mode: "trailing",
|
|
73
|
+
// Never push it back past 1m after the first trigger
|
|
74
|
+
maxDelay: "1m",
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
<Tip>
|
|
82
|
+
With debounce, one run handles many changes. Have the task read the pending work from your own
|
|
83
|
+
database when it starts, instead of relying on a single trigger's payload.
|
|
84
|
+
</Tip>
|
|
85
|
+
|
|
86
|
+
**To never run two at once for the same key, use a per-key concurrency limit.** Extra runs wait in the queue instead of running in parallel:
|
|
87
|
+
|
|
88
|
+
```ts Use a concurrency limit
|
|
89
|
+
import { task } from "@trigger.dev/sdk";
|
|
90
|
+
|
|
91
|
+
export const syncAccount = task({
|
|
92
|
+
id: "sync-account",
|
|
93
|
+
concurrency: { perKey: 1 },
|
|
94
|
+
run: async ({ accountId }: { accountId: string }) => {
|
|
95
|
+
// ...
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// When triggering
|
|
100
|
+
await syncAccount.trigger({ accountId }, { concurrencyKey: accountId });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The `concurrency` option requires SDK 4.7.0 or later. See [Concurrency](/concurrency) for details.
|
|
104
|
+
|
|
105
|
+
Debounce only merges triggers into a run that hasn't started yet. Once that run is queued or executing, the next trigger creates a new run. Combine both options so bursts collapse and a new run never overlaps one that's still going.
|
|
106
|
+
|
|
107
|
+
**For exactly one run per piece of work, use an `idempotencyKey`.** See [Using a random idempotency key](#using-a-random-idempotency-key) below.
|
|
108
|
+
|
|
109
|
+
### Using a fixed delay to space out triggers
|
|
110
|
+
|
|
111
|
+
Adding the same `delay` to every trigger doesn't collapse anything. Each trigger still creates its own run. It just starts later:
|
|
112
|
+
|
|
113
|
+
```ts Avoid
|
|
114
|
+
// 100 changes still create 100 runs, each one 30s after its trigger
|
|
115
|
+
await tasks.trigger("sync-account", { accountId }, { delay: "30s" });
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Use [`debounce`](#checking-for-active-runs-before-triggering) with the same key instead, so triggers within the delay join one run.
|
|
119
|
+
|
|
120
|
+
### Using a random idempotency key
|
|
121
|
+
|
|
122
|
+
An idempotency key only dedupes triggers that send the same key. A fresh random value on every trigger never matches, so every call creates a new run:
|
|
123
|
+
|
|
124
|
+
```ts Avoid
|
|
125
|
+
await tasks.trigger("sync-account", payload, {
|
|
126
|
+
// Never matches another trigger, so it dedupes nothing
|
|
127
|
+
idempotencyKey: `sync-${crypto.randomUUID()}`,
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Build the key from the thing that should only happen once:
|
|
132
|
+
|
|
133
|
+
```ts Your backend
|
|
134
|
+
import { tasks } from "@trigger.dev/sdk";
|
|
135
|
+
|
|
136
|
+
// One run per change, even if this code runs again within 30 days
|
|
137
|
+
// (the default key lifetime, set with idempotencyKeyTTL)
|
|
138
|
+
await tasks.trigger("process-change", payload, {
|
|
139
|
+
idempotencyKey: `change-${change.id}`,
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
<Note>
|
|
144
|
+
Inside a task, a raw string key is scoped to the current run. To dedupe across runs, create it
|
|
145
|
+
with `idempotencyKeys.create(key, { scope: "global" })`. See [Idempotency](/idempotency) for
|
|
146
|
+
scopes and TTLs.
|
|
147
|
+
</Note>
|
|
148
|
+
|
|
149
|
+
### Polling for a run's status
|
|
150
|
+
|
|
151
|
+
```ts Avoid
|
|
152
|
+
const handle = await tasks.trigger("generate-report", payload);
|
|
153
|
+
|
|
154
|
+
while (true) {
|
|
155
|
+
const run = await runs.retrieve(handle.id);
|
|
156
|
+
if (run.isCompleted) break;
|
|
157
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Inside a task, use [`triggerAndWait()`](/triggering#yourtask-triggerandwait). You don't pay for compute during waits longer than 5 seconds.
|
|
162
|
+
|
|
163
|
+
From your backend, subscribe to the run. The loop ends when the run finishes:
|
|
164
|
+
|
|
165
|
+
```ts Use realtime
|
|
166
|
+
import { runs, tasks } from "@trigger.dev/sdk";
|
|
167
|
+
|
|
168
|
+
const handle = await tasks.trigger("generate-report", payload);
|
|
169
|
+
|
|
170
|
+
for await (const run of runs.subscribeToRun(handle.id)) {
|
|
171
|
+
console.log(run.status);
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Polling runs by tag to follow progress
|
|
176
|
+
|
|
177
|
+
Calling `runs.list({ tag })` on a timer to see what a user's or a job's runs are doing repeats the same search every time. Subscribe once instead:
|
|
178
|
+
|
|
179
|
+
```ts Use realtime
|
|
180
|
+
import { runs } from "@trigger.dev/sdk";
|
|
181
|
+
|
|
182
|
+
const tag = `user:${userId}`;
|
|
183
|
+
|
|
184
|
+
for await (const run of runs.subscribeToRunsWithTag(tag)) {
|
|
185
|
+
console.log(run.id, run.status);
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
This iterator never completes on its own, so break out of the loop when you're done. In a frontend, use the [React hooks](/realtime/react-hooks/overview).
|
|
190
|
+
|
|
191
|
+
<Note>
|
|
192
|
+
Tag subscriptions only include runs created in the last 24 hours. You can narrow that with the
|
|
193
|
+
`createdAt` option, but not extend it. To follow a specific older run, use
|
|
194
|
+
`runs.subscribeToRun(runId)`.
|
|
195
|
+
</Note>
|
|
196
|
+
|
|
197
|
+
### Counting runs to measure a backlog
|
|
198
|
+
|
|
199
|
+
Listing `QUEUED` runs to count them gets slower as the backlog grows, which is exactly when you need the number. It can also miss runs: queued runs created before the [time window](#when-you-do-need-runs-list) aren't returned. The queue already tracks the count:
|
|
200
|
+
|
|
201
|
+
```ts Use queues.retrieve()
|
|
202
|
+
import { queues } from "@trigger.dev/sdk";
|
|
203
|
+
|
|
204
|
+
// A task's default queue is named after the task ID
|
|
205
|
+
const queue = await queues.retrieve({
|
|
206
|
+
type: "task",
|
|
207
|
+
name: "sync-account",
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
console.log(queue.queued, queue.running);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
For a queue shared between tasks, use `{ type: "custom", name: "your-queue-name" }`.
|
|
214
|
+
|
|
215
|
+
## When you do need runs.list()
|
|
216
|
+
|
|
217
|
+
`runs.list()` is the right tool for dashboards, reports, audits, and finding runs to act on in bulk. To keep it fast:
|
|
218
|
+
|
|
219
|
+
- **Pass a time window.** Use the narrowest `period` (or `from`/`to`) that covers what you need, like `period: "1h"`.
|
|
220
|
+
- **Filter by `taskIdentifier`** as well as status or tag.
|
|
221
|
+
- **Stop paginating once you have what you need.** Don't page through every result by default.
|
|
222
|
+
- **Keep it off hot paths.** Don't call it per event, per trigger, or in a tight loop inside a task. If several places need the same answer, cache it.
|
|
223
|
+
|
|
224
|
+
<Warning>
|
|
225
|
+
If you don't pass `period`, `from`, or `to`, only runs created in the last 7 days are searched.
|
|
226
|
+
The window applies to every status, so runs created before it aren't returned even if they're
|
|
227
|
+
still queued or executing. Passing only `to` removes the lower bound, so the search covers every
|
|
228
|
+
run up to that date.
|
|
229
|
+
</Warning>
|
|
230
|
+
|
|
231
|
+
<Note>
|
|
232
|
+
If a query would be too expensive to complete, the API returns a `422` asking you to narrow the
|
|
233
|
+
time window. The SDK doesn't retry it.
|
|
234
|
+
</Note>
|
|
@@ -4,6 +4,10 @@ sidebarTitle: "Max duration"
|
|
|
4
4
|
description: "Set a maximum duration for a task to run."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
Runs have no maximum duration. You set a default `maxDuration` for your project and can override it per task or per run, or set it to `timeout.None` to let a run execute indefinitely.
|
|
8
|
+
|
|
9
|
+
Trigger is built for long running AI agents and workflows and we have seen Trigger.dev users with runs lasting more than six months.
|
|
10
|
+
|
|
7
11
|
The `maxDuration` parameter sets a maximum compute time limit for tasks. When a task exceeds this duration, it will be automatically stopped. This helps prevent runaway tasks and manage compute resources effectively.
|
|
8
12
|
|
|
9
13
|
You must set a default maxDuration in your `trigger.config.ts` file, which will apply to all tasks unless overridden:
|
package/docs/runs/metadata.mdx
CHANGED
|
@@ -74,7 +74,7 @@ This means it's safe to call these methods anywhere in your code, and they will
|
|
|
74
74
|
undefined.
|
|
75
75
|
</Note>
|
|
76
76
|
|
|
77
|
-
These methods also work inside any task lifecycle hook, either attached to the specific task or
|
|
77
|
+
These methods also work inside any task lifecycle hook, either attached to the specific task or registered globally with `tasks.*` (for example in your `init.ts` file).
|
|
78
78
|
|
|
79
79
|
<CodeGroup>
|
|
80
80
|
|
|
@@ -86,7 +86,7 @@ export const myTask = task({
|
|
|
86
86
|
run: async (payload: { message: string }) => {
|
|
87
87
|
// Your run function work here
|
|
88
88
|
},
|
|
89
|
-
|
|
89
|
+
onStartAttempt: async () => {
|
|
90
90
|
metadata.set("progress", 0.5);
|
|
91
91
|
},
|
|
92
92
|
onSuccess: async () => {
|
|
@@ -95,14 +95,11 @@ export const myTask = task({
|
|
|
95
95
|
});
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
```ts
|
|
99
|
-
import {
|
|
98
|
+
```ts init.ts
|
|
99
|
+
import { tasks, metadata } from "@trigger.dev/sdk";
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
onStart: async () => {
|
|
104
|
-
metadata.set("progress", 0.5);
|
|
105
|
-
},
|
|
101
|
+
tasks.onStartAttempt(async () => {
|
|
102
|
+
metadata.set("progress", 0.5);
|
|
106
103
|
});
|
|
107
104
|
```
|
|
108
105
|
|
|
@@ -731,4 +728,4 @@ See the [API reference](/management/runs/retrieve) for more information.
|
|
|
731
728
|
|
|
732
729
|
## Size limit
|
|
733
730
|
|
|
734
|
-
The maximum size of the metadata object is 256KB. If
|
|
731
|
+
The maximum size of the metadata object is 256KB. If an update would exceed this limit, the server rejects it (HTTP 413) and the update is not applied. Inside a task, metadata updates are sent in the background, so the SDK logs a `Failed to flush metadata` error instead of throwing from `metadata.set()` or `metadata.flush()`. If you are self-hosting, you can change the limit with the `TASK_RUN_METADATA_MAXIMUM_SIZE` environment variable (bytes, default 262144). For example, to raise it to 512KB, set `TASK_RUN_METADATA_MAXIMUM_SIZE=524288`.
|
package/docs/runs.mdx
CHANGED
|
@@ -60,7 +60,7 @@ to complete successfully due to an error in the task code.
|
|
|
60
60
|
failed because it exceeded its `maxDuration`.
|
|
61
61
|
|
|
62
62
|
<Icon icon="fire" iconType="solid" color="#E11D48" size={17} /> **Crashed**: The worker process crashed
|
|
63
|
-
during execution (likely due to an Out of Memory error) and won’t be retried.
|
|
63
|
+
during execution (likely due to an Out of Memory error) and won’t be retried, unless `retry.outOfMemory.machine` is set to retry on a larger machine. See [Machines](/machines#retrying-with-a-larger-machine).
|
|
64
64
|
|
|
65
65
|
<Icon icon="bug" iconType="solid" color="#E11D48" size={17} /> **System failure**: An unrecoverable system
|
|
66
66
|
error has occurred.
|
|
@@ -98,7 +98,7 @@ import { runs } from "@trigger.dev/sdk";
|
|
|
98
98
|
|
|
99
99
|
const run = await runs.retrieve("run_1234");
|
|
100
100
|
|
|
101
|
-
if (run.
|
|
101
|
+
if (run.isSuccess) {
|
|
102
102
|
console.log("Run completed successfully");
|
|
103
103
|
}
|
|
104
104
|
```
|
|
@@ -111,7 +111,7 @@ These helpers are also available when subscribing to Realtime run updates:
|
|
|
111
111
|
import { runs } from "@trigger.dev/sdk";
|
|
112
112
|
|
|
113
113
|
for await (const run of runs.subscribeToRun("run_1234")) {
|
|
114
|
-
if (run.
|
|
114
|
+
if (run.isSuccess) {
|
|
115
115
|
console.log("Run completed successfully!");
|
|
116
116
|
break;
|
|
117
117
|
}
|
|
@@ -129,7 +129,7 @@ await yourTask.trigger({ foo: "bar" }, { idempotencyKey: "unique-key" });
|
|
|
129
129
|
```
|
|
130
130
|
|
|
131
131
|
- If a run with the same idempotency key is already in progress, the new trigger will be ignored.
|
|
132
|
-
- If the run
|
|
132
|
+
- If the run completed successfully (or was canceled), the existing run is returned. If it failed or expired, the key is released and triggering again creates a new run.
|
|
133
133
|
|
|
134
134
|
See our [Idempotency docs](/idempotency) for more information.
|
|
135
135
|
|
|
@@ -253,6 +253,12 @@ Similar to `triggerAndWait()`, the `batchTriggerAndWait()` function lets you bat
|
|
|
253
253
|
|
|
254
254
|
#### runs.list()
|
|
255
255
|
|
|
256
|
+
<Warning>
|
|
257
|
+
`runs.list()` is for browsing, reporting, and bulk operations, not for coordinating runs. Don't
|
|
258
|
+
call it before every trigger or poll it in a loop. See [runs.list() best
|
|
259
|
+
practices](/runs/list-best-practices) for what to use instead.
|
|
260
|
+
</Warning>
|
|
261
|
+
|
|
256
262
|
List runs in a specific environment. You can filter the runs by status, created at, task identifier, version, and more:
|
|
257
263
|
|
|
258
264
|
```ts
|
|
@@ -290,7 +296,7 @@ import { runs } from "@trigger.dev/sdk";
|
|
|
290
296
|
const response = await runs.list({
|
|
291
297
|
status: ["QUEUED", "EXECUTING"], // Filter by status
|
|
292
298
|
taskIdentifier: ["my-task", "my-other-task"], // Filter by task identifier
|
|
293
|
-
from: new Date(
|
|
299
|
+
from: new Date(Date.now() - 24 * 60 * 60 * 1000), // Filter by created at (last 24 hours)
|
|
294
300
|
to: new Date(),
|
|
295
301
|
version: "20241127.2", // Filter by deployment version,
|
|
296
302
|
tag: ["tag1", "tag2"], // Filter by tags
|
|
@@ -327,7 +333,7 @@ If you have just triggered a run, you can pass the entire response object to `re
|
|
|
327
333
|
import { runs, tasks } from "@trigger.dev/sdk";
|
|
328
334
|
import type { myTask } from "./trigger/myTask";
|
|
329
335
|
|
|
330
|
-
const response = await tasks.trigger<typeof myTask>({ foo: "bar" });
|
|
336
|
+
const response = await tasks.trigger<typeof myTask>("my-task", { foo: "bar" });
|
|
331
337
|
const run = await runs.retrieve(response);
|
|
332
338
|
|
|
333
339
|
console.log(run.payload.foo); // string
|
|
@@ -346,14 +352,18 @@ await runs.cancel(runId);
|
|
|
346
352
|
|
|
347
353
|
#### runs.replay()
|
|
348
354
|
|
|
349
|
-
Replay a run:
|
|
355
|
+
Replay a run, optionally overriding its payload or options for the new run:
|
|
350
356
|
|
|
351
357
|
```ts
|
|
352
358
|
import { runs } from "@trigger.dev/sdk";
|
|
353
359
|
|
|
354
360
|
await runs.replay(runId);
|
|
361
|
+
|
|
362
|
+
await runs.replay(runId, { payload: { userId: "user_456" }, delay: "10m" });
|
|
355
363
|
```
|
|
356
364
|
|
|
365
|
+
See [Replaying](/replaying#replaying-using-the-sdk) for every option.
|
|
366
|
+
|
|
357
367
|
#### runs.reschedule()
|
|
358
368
|
|
|
359
369
|
Updates a delayed run with a new delay. Only valid when the run is in the DELAYED state.
|
|
@@ -392,5 +402,5 @@ For more on real-time updates, see the [Realtime](/realtime) documentation.
|
|
|
392
402
|
|
|
393
403
|
### Triggering runs for undeployed tasks
|
|
394
404
|
|
|
395
|
-
It's possible to trigger a run for a task that hasn't been deployed yet. The run will enter the "
|
|
405
|
+
It's possible to trigger a run for a task that hasn't been deployed yet. The run will enter the "Pending version" state until the task is deployed. Once deployed, the run will be queued and executed normally.
|
|
396
406
|
This feature is particularly useful in CI/CD pipelines where you want to trigger tasks before the deployment is complete.
|
|
@@ -14,6 +14,7 @@ mode: "wide"
|
|
|
14
14
|
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Yes | — | OTel exporter endpoint. Point at: `<webapp-url>/otel` |
|
|
15
15
|
| **Worker instance** | | | |
|
|
16
16
|
| `TRIGGER_WORKER_INSTANCE_NAME` | No | random UUID | Worker instance name. Set to `spec.nodeName` on k8s. |
|
|
17
|
+
| `TRIGGER_WORKER_CELL_NAME` | No | — | Name of the worker cluster this supervisor serves. Stamped on each run it starts. |
|
|
17
18
|
| `TRIGGER_WORKER_HEARTBEAT_INTERVAL_SECONDS` | No | 30 | Worker heartbeat interval (seconds). |
|
|
18
19
|
| **Workload API settings** | | | |
|
|
19
20
|
| `TRIGGER_WORKLOAD_API_ENABLED` | No | true | Enable workload API. Runs use this to perform actions. |
|
|
@@ -63,6 +64,8 @@ mode: "wide"
|
|
|
63
64
|
| **Failed pod handler** | | | |
|
|
64
65
|
| `FAILED_POD_HANDLER_ENABLED` | No | true | Enable failed pod handler. |
|
|
65
66
|
| `FAILED_POD_HANDLER_RECONNECT_INTERVAL_MS` | No | 1000 (1s) | Failed pod handler reconnect interval (ms). |
|
|
67
|
+
| `FAILED_POD_HANDLER_WATCH_TIMEOUT_SECONDS` | No | 300 (5m) | How long the server keeps each failed pod watch open (s). |
|
|
68
|
+
| `RUNNER_KILL_REPORTS_ENABLED` | No | true | Fail a run straight away when its pod is killed, as out of memory only if Kubernetes reports `OOMKilled`. |
|
|
66
69
|
| **Debug** | | | |
|
|
67
70
|
| `DEBUG` | No | false | Enable debug logs. |
|
|
68
71
|
| `SEND_RUN_DEBUG_LOGS` | No | false | Send run debug logs to the platform. |
|
|
@@ -44,6 +44,9 @@ mode: "wide"
|
|
|
44
44
|
| `WHITELISTED_EMAILS` | No | — | Whitelisted emails regex. |
|
|
45
45
|
| `BLOCKED_EMAIL_DOMAINS` | No | — | Comma-separated email domains that can't sign in or sign up, including their subdomains. |
|
|
46
46
|
| `LOGIN_RATE_LIMITS_ENABLED` | No | true | Enable rate limiting on magic-link login. |
|
|
47
|
+
| `MAGIC_LINK_SAME_BROWSER_REQUIRED` | No | true | Only accept a magic link opened in the browser that requested it. Set to false to allow opening it on another device.|
|
|
48
|
+
| `TURNSTILE_SITE_KEY` | No | — | Cloudflare Turnstile site key. With `TURNSTILE_SECRET_KEY`, adds a bot check to the magic-link sign-in form. |
|
|
49
|
+
| `TURNSTILE_SECRET_KEY` | No | — | Cloudflare Turnstile secret key, used to verify the check on the server. Turnstile is off unless both keys are set.|
|
|
47
50
|
| `AUTH_GITHUB_CLIENT_ID` | No | — | GitHub client ID. |
|
|
48
51
|
| `AUTH_GITHUB_CLIENT_SECRET` | No | — | GitHub client secret. |
|
|
49
52
|
| `CSP_IMG_SRC_ALLOWLIST` | No | — | Extra hosts the dashboard may load images from, e.g. an SSO or avatar host serving profile images. A comma-separated list of exact origins (`https://sso.example.com`); a wildcard host such as `https://*.example.com` is refused. |
|
|
@@ -98,6 +101,8 @@ mode: "wide"
|
|
|
98
101
|
| `DEPLOY_REGISTRY_NAMESPACE` | No | trigger | Deploy registry namespace. |
|
|
99
102
|
| `DEPLOY_REGISTRY_ECR_DEFAULT_REPOSITORY_POLICY` | No | — | Raw IAM policy JSON applied via SetRepositoryPolicy to every ECR repo created by the webapp. Use to grant cross-account pull access to EKS workers when the ECR account is separate from the cluster account. |
|
|
100
103
|
| `DEPLOY_IMAGE_PLATFORM` | No | linux/amd64 | Deploy image platform, same values as docker `--platform` flag. |
|
|
104
|
+
| `DEPLOY_BASE_IMAGES` | No | — | Base images every deploy must build on, as comma-separated `runtime=image@sha256:<digest>`. See [custom base images](/self-hosting/overview#custom-base-images). |
|
|
105
|
+
| `DEPLOY_BUILD_BASE_IMAGES` | No | — | Build-stage images, in the same format as `DEPLOY_BASE_IMAGES`. See [custom base images](/self-hosting/overview#custom-base-images). |
|
|
101
106
|
| `DEPLOY_TIMEOUT_MS` | No | 480000 (8m) | Deploy timeout (ms). |
|
|
102
107
|
| `DEPLOY_QUEUE_TIMEOUT_MS` | No | 900000 (15m) | Deploy queue timeout (ms). |
|
|
103
108
|
| **Object store (S3)** | | | |
|
|
@@ -100,6 +100,32 @@ All fields are optional. Partial overrides are supported:
|
|
|
100
100
|
}
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
## Custom base images
|
|
104
|
+
|
|
105
|
+
Deploys build on the published `triggerdotdev/node` and `triggerdotdev/bun` Debian images. To require a different base for every deploy to your instance, such as a FIPS-validated or hardened Node image, set `DEPLOY_BASE_IMAGES` on the webapp (and optionally `DEPLOY_BUILD_BASE_IMAGES` for the build stage):
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
DEPLOY_BASE_IMAGES="node-26=registry.example.com/node-fips:26@sha256:<64-character-digest>"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Entries are comma-separated `runtime=image@sha256:<digest>`. The runtimes are `node-22`, `node-24`, `node-26` and `bun`, and every image must be pinned by digest. An invalid value prevents the webapp from starting. Projects with `runtime: "node"` in their config resolve to the current default Node runtime (`node-24` today), so set that key for them. With the Helm chart, set the variables through `webapp.extraEnvVars`.
|
|
112
|
+
|
|
113
|
+
Runtimes with an entry build on that image; the others keep the published images. Deploys from CLI versions that cannot apply the images are rejected with an error asking to upgrade, and so are `--native-build`, `--local-bundle` and `--from-bundle` deploys. This is a self-hosting setting and does not apply to Trigger.dev Cloud.
|
|
114
|
+
|
|
115
|
+
You own a custom base image. It must provide:
|
|
116
|
+
|
|
117
|
+
- `node` on `PATH` at the runtime's major version (for `bun`, both `bun` and `node`, because the final stage starts the app with `dumb-init node`)
|
|
118
|
+
- `busybox`, `ca-certificates`, `dumb-init`, `git` and `openssl`
|
|
119
|
+
- a `node` user (a `bun` user for the Bun runtime)
|
|
120
|
+
- glibc, so native modules built in the build stage load at runtime
|
|
121
|
+
|
|
122
|
+
A `DEPLOY_BUILD_BASE_IMAGES` image needs everything the base image provides, plus `python3`, `make` and `g++`. Build extensions that add image instructions are replayed on it. Without an entry, the build stage uses the published build image, except for projects whose build extensions add image instructions: those build from your base image and install the toolchain with `apt-get`, so that base must be Debian-based. To avoid the published images entirely, set both variables.
|
|
123
|
+
|
|
124
|
+
<Warning>
|
|
125
|
+
`image.pkgs` and build extensions that run `apt-get` (such as `aptGet` and `playwright`) assume a
|
|
126
|
+
Debian base. On other distributions, install those packages in your base image instead.
|
|
127
|
+
</Warning>
|
|
128
|
+
|
|
103
129
|
## Community support
|
|
104
130
|
|
|
105
131
|
It's dangerous to go alone! Join the self-hosting channel on our [Discord server](https://discord.gg/NQTxt5NA7s).
|
package/docs/tags.mdx
CHANGED
|
@@ -118,3 +118,9 @@ for await (const run of runs.list({ tag: "user_123456", status: ["COMPLETED"] })
|
|
|
118
118
|
console.log(run.id, run.taskIdentifier, run.finishedAt, run.tags);
|
|
119
119
|
}
|
|
120
120
|
```
|
|
121
|
+
|
|
122
|
+
<Tip>
|
|
123
|
+
To follow runs with a tag as they change, use
|
|
124
|
+
[`runs.subscribeToRunsWithTag()`](/realtime/backend/subscribe) instead of polling `runs.list()`.
|
|
125
|
+
See [runs.list() best practices](/runs/list-best-practices).
|
|
126
|
+
</Tip>
|
package/docs/tasks/scheduled.mdx
CHANGED
|
@@ -45,7 +45,7 @@ export const firstScheduledTask = schedules.task({
|
|
|
45
45
|
//this can be undefined if you didn't provide one
|
|
46
46
|
console.log(payload.externalId); //is a string or undefined
|
|
47
47
|
|
|
48
|
-
//the next
|
|
48
|
+
//the next 10 dates this task is scheduled to run
|
|
49
49
|
console.log(payload.upcoming); //is an array of Date objects
|
|
50
50
|
},
|
|
51
51
|
});
|
|
@@ -58,14 +58,14 @@ You can see from the comments that the payload has several useful properties:
|
|
|
58
58
|
- `timezone` - the timezone the schedule was registered with, defaults to "UTC". In IANA format, e.g. "America/New_York".
|
|
59
59
|
- `scheduleId` - the id of the schedule that triggered the task
|
|
60
60
|
- `externalId` - the external id you (optionally) provided when creating the schedule
|
|
61
|
-
- `upcoming` - the next
|
|
61
|
+
- `upcoming` - the next 10 times the task is scheduled to run
|
|
62
62
|
|
|
63
63
|
<Note>
|
|
64
64
|
This task will NOT get triggered on a schedule until you attach a schedule to it. Read on for how
|
|
65
65
|
to do that.
|
|
66
66
|
</Note>
|
|
67
67
|
|
|
68
|
-
Like all tasks they
|
|
68
|
+
Like all tasks they have a [maxDuration](/runs/max-duration), they should be placed inside a [/trigger folder](/config/config-file), and you [can configure them](/tasks/overview#defining-a-task).
|
|
69
69
|
|
|
70
70
|
You can set a [TTL](/runs#time-to-live-ttl) on a scheduled task to automatically expire runs that aren't dequeued in time. This is useful when a schedule fires while the previous run is still executing - rather than queueing up stale runs, they'll expire:
|
|
71
71
|
|
|
@@ -142,7 +142,7 @@ To use imperative schedules you need to do two things:
|
|
|
142
142
|
|
|
143
143
|
## Spreading runs with windows
|
|
144
144
|
|
|
145
|
-
|
|
145
|
+
When many schedules share the same cron pattern, such as a daily `0 9 * * *`, they all fire at the same time and load your downstream systems as well as ours. A **window** spreads those runs out: each schedule is assigned a stable time within the window after its cron time, so the load is smoothed while each schedule keeps firing at a predictable, repeatable moment. New schedules use a [60-minute window by default](#default-window).
|
|
146
146
|
|
|
147
147
|
The assigned time is deterministic. A given schedule always lands at the same offset for a given interval, so runs don't jump around between occurrences. And you can always see exactly when the next run will start.
|
|
148
148
|
|
|
@@ -151,7 +151,7 @@ Set a `window` as either:
|
|
|
151
151
|
- **An absolute duration** in whole minutes or hours, up to 24 hours: `"30m"`, `"2h"`, `"24h"`. Absolute windows are capped at the next cron time, so a run is never delayed past its following occurrence.
|
|
152
152
|
- **A percentage** of the interval between runs: `"30%"`, `"100%"`. A `"50%"` window on an hourly schedule spreads runs across the first 30 minutes of each hour.
|
|
153
153
|
|
|
154
|
-
Set `"0m"` (or `"0%"`) for no spreading — the run fires at its exact cron time.
|
|
154
|
+
Set `"0m"` (or `"0%"`) for no spreading — the run fires at its exact cron time. This also overrides the [default window](#default-window).
|
|
155
155
|
|
|
156
156
|
Declarative schedules set the window on the `cron` object:
|
|
157
157
|
|
|
@@ -184,6 +184,12 @@ const createdSchedule = await schedules.create({
|
|
|
184
184
|
is the assigned time the run will actually start.
|
|
185
185
|
</Note>
|
|
186
186
|
|
|
187
|
+
### Default window
|
|
188
|
+
|
|
189
|
+
New schedules get a default 60-minute window unless you set one, so each run starts at a stable time within the hour after its cron time. Like any absolute window, it's capped at the next cron time: a schedule that runs every 15 minutes is spread across those 15 minutes.
|
|
190
|
+
|
|
191
|
+
To opt a new schedule out of the default window, set `window: "0m"`.
|
|
192
|
+
|
|
187
193
|
### Free-plan minimum window
|
|
188
194
|
|
|
189
195
|
Schedules created while an organization is on a free plan run no more than once per hour and use a minimum 60-minute window.
|
package/docs/triggering.mdx
CHANGED
|
@@ -746,6 +746,12 @@ await myTask.batchTrigger([{ payload: { some: "data" }, options: { delay: "1h" }
|
|
|
746
746
|
manually, convert Dates to ISO strings first (e.g., `date.toISOString()`).
|
|
747
747
|
</Note>
|
|
748
748
|
|
|
749
|
+
<Tip>
|
|
750
|
+
A `delay` doesn't merge triggers: each trigger still creates its own run. To collapse a burst of
|
|
751
|
+
triggers into one run, use [`debounce`](#debounce). See [runs.list() best
|
|
752
|
+
practices](/runs/list-best-practices#using-a-fixed-delay-to-space-out-triggers).
|
|
753
|
+
</Tip>
|
|
754
|
+
|
|
749
755
|
### `ttl`
|
|
750
756
|
|
|
751
757
|
You can set a TTL (time to live) when triggering a task, which will automatically expire the run if it hasn't started within the specified time. This is useful for ensuring that a run doesn't get stuck in the queue for too long.
|
|
@@ -814,6 +820,8 @@ export const myTask = task({
|
|
|
814
820
|
});
|
|
815
821
|
```
|
|
816
822
|
|
|
823
|
+
Build the key from the work being done, like an order or event ID. A random value on every trigger never matches, so it never dedupes. See [runs.list() best practices](/runs/list-best-practices#using-a-random-idempotency-key).
|
|
824
|
+
|
|
817
825
|
For more information, see our [Idempotency](/idempotency) documentation.
|
|
818
826
|
|
|
819
827
|
### `idempotencyKeyTTL`
|