Skip to content

Commit f4a18fe

Browse files
authored
Docs: Batch Trigger upgrades (#1505)
* WIP batch docs * More batch trigger v2 docs
1 parent 0acd052 commit f4a18fe

File tree

8 files changed

+633
-206
lines changed

8 files changed

+633
-206
lines changed

docs/guides/examples/scrape-hacker-news.mdx

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,16 @@ import LocalDevelopment from "/snippets/local-development-extensions.mdx";
88
import ScrapingWarning from "/snippets/web-scraping-warning.mdx";
99

1010
<div className="w-full h-full aspect-video">
11-
<iframe width="100%" height="100%" src="https://www.youtube.com/embed/6azvzrZITKY?si=muKtsBiS9TJGGKWg" title="YouTube video player" frameborder="0" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen/>
11+
<iframe
12+
width="100%"
13+
height="100%"
14+
src="https://www.youtube.com/embed/6azvzrZITKY?si=muKtsBiS9TJGGKWg"
15+
title="YouTube video player"
16+
frameborder="0"
17+
allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
18+
referrerpolicy="strict-origin-when-cross-origin"
19+
allowfullscreen
20+
/>
1221
</div>
1322

1423
## Overview
@@ -125,12 +134,9 @@ export const summarizeHackerNews = schedules.task({
125134
.batchTriggerAndWait(
126135
articles.map((article) => ({
127136
payload: { title: article.title!, link: article.link! },
128-
idempotencyKey: article.link,
129137
}))
130138
)
131-
.then((batch) =>
132-
batch.runs.filter((run) => run.ok).map((run) => run.output)
133-
);
139+
.then((batch) => batch.runs.filter((run) => run.ok).map((run) => run.output));
134140

135141
// Send email using Resend
136142
await resend.emails.send({
@@ -165,11 +171,7 @@ export const scrapeAndSummarizeArticle = task({
165171
// Prevent all assets from loading, images, stylesheets etc
166172
await page.setRequestInterception(true);
167173
page.on("request", (request) => {
168-
if (
169-
["script", "stylesheet", "image", "media", "font"].includes(
170-
request.resourceType()
171-
)
172-
) {
174+
if (["script", "stylesheet", "image", "media", "font"].includes(request.resourceType())) {
173175
request.abort();
174176
} else {
175177
request.continue();
@@ -218,26 +220,15 @@ To prevent the main example from becoming too cluttered, we'll create a separate
218220
Notice how this file is imported into the main task code and passed to Resend to send the email.
219221

220222
```tsx summarize-hn-email.tsx
221-
import {
222-
Html,
223-
Head,
224-
Body,
225-
Container,
226-
Section,
227-
Heading,
228-
Text,
229-
Link,
230-
} from "@react-email/components";
223+
import { Html, Head, Body, Container, Section, Heading, Text, Link } from "@react-email/components";
231224

232225
interface Article {
233226
title: string;
234227
link: string;
235228
summary: string | null;
236229
}
237230

238-
export const HNSummaryEmail: React.FC<{ articles: Article[] }> = ({
239-
articles,
240-
}) => (
231+
export const HNSummaryEmail: React.FC<{ articles: Article[] }> = ({ articles }) => (
241232
<Html>
242233
<Head />
243234
<Body style={{ fontFamily: "Arial, sans-serif", padding: "20px" }}>

docs/idempotency.mdx

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@ description: "An API call or operation is “idempotent” if it has the same re
55

66
We currently support idempotency at the task level, meaning that if you trigger a task with the same `idempotencyKey` twice, the second request will not create a new task run.
77

8+
<Warning>
9+
In version 3.3.0 and later, the `idempotencyKey` option is not available when using
10+
`triggerAndWait` or `batchTriggerAndWait`, due to a bug that would sometimes cause the parent task
11+
to become stuck. We are working on a fix for this issue.
12+
</Warning>
13+
814
## `idempotencyKey` option
915

1016
You can provide an `idempotencyKey` to ensure that a task is only triggered once with the same key. This is useful if you are triggering a task within another task that might be retried:
1117

12-
```typescript
18+
```ts
1319
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
1420

1521
export const myTask = task({
@@ -18,13 +24,14 @@ export const myTask = task({
1824
maxAttempts: 4,
1925
},
2026
run: async (payload: any) => {
21-
// By default, idempotency keys generated are unique to the run, to prevent retries from duplicating child tasks
27+
// This idempotency key will be unique to this task run, meaning the childTask will only be triggered once across all retries
2228
const idempotencyKey = await idempotencyKeys.create("my-task-key");
2329

2430
// childTask will only be triggered once with the same idempotency key
25-
await childTask.triggerAndWait(payload, { idempotencyKey });
31+
await childTask.trigger({ foo: "bar" }, { idempotencyKey });
2632

2733
// Do something else, that may throw an error and cause the task to be retried
34+
throw new Error("Something went wrong");
2835
},
2936
});
3037
```
@@ -33,7 +40,7 @@ You can use the `idempotencyKeys.create` SDK function to create an idempotency k
3340

3441
We automatically inject the run ID when generating the idempotency key when running inside a task by default. You can turn it off by passing the `scope` option to `idempotencyKeys.create`:
3542

36-
```typescript
43+
```ts
3744
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
3845

3946
export const myTask = task({
@@ -42,21 +49,18 @@ export const myTask = task({
4249
maxAttempts: 4,
4350
},
4451
run: async (payload: any) => {
45-
// This idempotency key will be the same for all runs of this task
52+
// This idempotency key will be globally unique, meaning only a single task run will be triggered with this key
4653
const idempotencyKey = await idempotencyKeys.create("my-task-key", { scope: "global" });
4754

4855
// childTask will only be triggered once with the same idempotency key
49-
await childTask.triggerAndWait(payload, { idempotencyKey });
50-
51-
// This is the same as the above
52-
await childTask.triggerAndWait(payload, { idempotencyKey: "my-task-key" });
56+
await childTask.trigger({ foo: "bar" }, { idempotencyKey });
5357
},
5458
});
5559
```
5660

5761
If you are triggering a task from your backend code, you can use the `idempotencyKeys.create` SDK function to create an idempotency key.
5862

59-
```typescript
63+
```ts
6064
import { idempotencyKeys, tasks } from "@trigger.dev/sdk/v3";
6165

6266
// You can also pass an array of strings to create a idempotency key
@@ -66,7 +70,7 @@ await tasks.trigger("my-task", { some: "data" }, { idempotencyKey });
6670

6771
You can also pass a string to the `idempotencyKey` option, without first creating it with `idempotencyKeys.create`.
6872

69-
```typescript
73+
```ts
7074
import { myTask } from "./trigger/myTasks";
7175

7276
// You can also pass an array of strings to create a idempotency key
@@ -77,7 +81,7 @@ await myTask.trigger({ some: "data" }, { idempotencyKey: myUser.id });
7781

7882
You can pass the `idempotencyKey` when calling `batchTrigger` as well:
7983

80-
```typescript
84+
```ts
8185
import { tasks } from "@trigger.dev/sdk/v3";
8286

8387
await tasks.batchTrigger("my-task", [
@@ -88,11 +92,47 @@ await tasks.batchTrigger("my-task", [
8892
]);
8993
```
9094

95+
## `idempotencyKeyTTL` option
96+
97+
By default idempotency keys are stored for 30 days. You can change this by passing the `idempotencyKeyTTL` option when triggering a task:
98+
99+
```ts
100+
import { idempotencyKeys, task, wait } from "@trigger.dev/sdk/v3";
101+
102+
export const myTask = task({
103+
id: "my-task",
104+
retry: {
105+
maxAttempts: 4,
106+
},
107+
run: async (payload: any) => {
108+
const idempotencyKey = await idempotencyKeys.create("my-task-key");
109+
110+
// The idempotency key will expire after 60 seconds
111+
await childTask.trigger({ foo: "bar" }, { idempotencyKey, idempotencyKeyTTL: "60s" });
112+
113+
await wait.for({ seconds: 61 });
114+
115+
// The idempotency key will have expired, so the childTask will be triggered again
116+
await childTask.trigger({ foo: "bar" }, { idempotencyKey });
117+
118+
// Do something else, that may throw an error and cause the task to be retried
119+
throw new Error("Something went wrong");
120+
},
121+
});
122+
```
123+
124+
You can use the following units for the `idempotencyKeyTTL` option:
125+
126+
- `s` for seconds (e.g. `60s`)
127+
- `m` for minutes (e.g. `5m`)
128+
- `h` for hours (e.g. `2h`)
129+
- `d` for days (e.g. `3d`)
130+
91131
## Payload-based idempotency
92132

93133
We don't currently support payload-based idempotency, but you can implement it yourself by hashing the payload and using the hash as the idempotency key.
94134

95-
```typescript
135+
```ts
96136
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
97137
import { createHash } from "node:crypto";
98138

docs/limits.mdx

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ title: "Limits"
33
description: "There are some hard and soft limits that you might hit."
44
---
55

6-
import RateLimitHitUseBatchTrigger from '/snippets/rate-limit-hit-use-batchtrigger.mdx';
6+
import RateLimitHitUseBatchTrigger from "/snippets/rate-limit-hit-use-batchtrigger.mdx";
77

88
## Concurrency limits
99

10-
| Pricing tier | Limit |
11-
|:---------------- |:-------------------- |
12-
| Free | 5 concurrent runs |
13-
| Hobby | 25 concurrent runs |
14-
| Pro | 100+ concurrent runs |
10+
| Pricing tier | Limit |
11+
| :----------- | :------------------- |
12+
| Free | 5 concurrent runs |
13+
| Hobby | 25 concurrent runs |
14+
| Pro | 100+ concurrent runs |
1515

1616
If you need more than 100 concurrent runs on the Pro tier, you can request more by contacting us via [email](https://trigger.dev/contact) or [Discord](https://trigger.dev/discord).
1717

@@ -20,44 +20,58 @@ If you need more than 100 concurrent runs on the Pro tier, you can request more
2020
Generally speaking each SDK call is an API call.
2121

2222
| Limit | Details |
23-
|:----- |:------------------------- |
23+
| :---- | :------------------------ |
2424
| API | 1,500 requests per minute |
2525

26-
<RateLimitHitUseBatchTrigger/>
26+
<RateLimitHitUseBatchTrigger />
2727

2828
## Queued tasks
2929

3030
The number of queued tasks by environment.
3131

3232
| Limit | Details |
33-
|:------- |:------------------ |
33+
| :------ | :----------------- |
3434
| Dev | At most 500 |
3535
| Staging | At most 10 million |
3636
| Prod | At most 10 million |
3737

3838
## Schedules
3939

40-
| Pricing tier | Limit |
41-
|:---------------- |:-------------------- |
42-
| Free | 5 per project |
43-
| Hobby | 100 per project |
44-
| Pro | 1,000+ per project |
40+
| Pricing tier | Limit |
41+
| :----------- | :----------------- |
42+
| Free | 5 per project |
43+
| Hobby | 100 per project |
44+
| Pro | 1,000+ per project |
4545

4646
When attaching schedules to tasks we strongly recommend you add them [in our dashboard](/tasks/scheduled#attaching-schedules-in-the-dashboard) if they're "static". That way you can control them easily per environment.
4747

4848
If you add them [dynamically using code](/management/schedules/create) make sure you add a `deduplicationKey` so you don't add the same schedule to a task multiple times. If you don't your task will get triggered multiple times, it will cost you more, and you will hit the limit.
4949

5050
If you're creating schedules for your user you will definitely need to request more schedules from us.
5151

52+
## Task payloads and outputs
53+
54+
| Limit | Details |
55+
| :--------------------- | :-------------------------------------------- |
56+
| Single trigger payload | Must not exceed 3MB |
57+
| Batch trigger payload | The total of all payloads must not exceed 1MB |
58+
| Task outputs | Must not exceed 10MB |
59+
60+
Payloads and outputs that exceed 512KB will be offloaded to object storage and a presigned URL will be provided to download the data when calling `runs.retrieve`. You don't need to do anything to handle this in your tasks however, as we will transparently upload/download these during operation.
61+
62+
## Batch size
63+
64+
A single batch can have a maximum of 500 items.
65+
5266
<SoftLimit />
5367

5468
## Log retention
5569

56-
| Pricing tier | Limit |
57-
|:---------------- |:--------- |
58-
| Free | 1 day |
59-
| Hobby | 7 days |
60-
| Pro | 30 days |
70+
| Pricing tier | Limit |
71+
| :----------- | :------ |
72+
| Free | 1 day |
73+
| Hobby | 7 days |
74+
| Pro | 30 days |
6175

6276
## Log size
6377

@@ -66,25 +80,30 @@ We limit the size of logs to prevent oversized data potentially causing issues.
6680
<Expandable title="log limits">
6781

6882
#### Attribute Limits
83+
6984
- Span Attribute Count Limit: 256
7085
- Log Attribute Count Limit: 256
7186
- Span Attribute Value Length Limit: 1028 characters
7287
- Log Attribute Value Length Limit: 1028 characters
7388

7489
#### Event and Link Limits
90+
7591
- Span Event Count Limit: 10
7692
- Link Count Limit: 2
7793
- Attributes per Link Limit: 10
7894
- Attributes per Event Limit: 10
7995

8096
#### I/O Packet Length Limit
97+
8198
128 KB (131,072 bytes)
8299

83100
#### Attribute Clipping Behavior
101+
84102
- Attributes exceeding the value length limit (1028 characters) are discarded.
85103
- If the total number of attributes exceeds 256, additional attributes are not included.
86104

87105
#### Attribute Value Size Calculation
106+
88107
- Strings: Actual length of the string
89108
- Numbers: 8 bytes
90109
- Booleans: 4 bytes
@@ -93,25 +112,15 @@ We limit the size of logs to prevent oversized data potentially causing issues.
93112

94113
</Expandable>
95114

96-
## Task payloads and outputs
97-
98-
| Limit | Details |
99-
|:--- |:--- |
100-
| Single trigger payload | Must not exceed 10MB |
101-
| Batch trigger payload | The total of all payloads must not exceed 10MB |
102-
| Task outputs | Must not exceed 10MB |
103-
104-
Payloads and outputs that exceed 512KB will be offloaded to object storage and a presigned URL will be provided to download the data when calling `runs.retrieve`. You don't need to do anything to handle this in your tasks however, as we will transparently upload/download these during operation.
105-
106115
## Alerts
107116

108117
An alert destination is a single email address, Slack channel, or webhook URL that you want to send alerts to. If you're on the Pro and need more than 100 alert destinations, you can request more by contacting us via [email](https://trigger.dev/contact) or [Discord](https://trigger.dev/discord).
109118

110-
| Pricing tier | Limit |
111-
|:---------------- |:----------------------- |
112-
| Free | 1 alert destination |
113-
| Hobby | 3 alert destinations |
114-
| Pro | 100+ alert destinations |
119+
| Pricing tier | Limit |
120+
| :----------- | :---------------------- |
121+
| Free | 1 alert destination |
122+
| Hobby | 3 alert destinations |
123+
| Pro | 100+ alert destinations |
115124

116125
## Machines
117126

@@ -121,8 +130,8 @@ See the [machine configurations](/machines#machine-configurations) for more deta
121130

122131
## Team members
123132

124-
| Pricing tier | Limit |
125-
|:---------------- |:----------------- |
126-
| Free | 5 team members |
127-
| Hobby | 5 team members |
128-
| Pro | 25+ team members |
133+
| Pricing tier | Limit |
134+
| :----------- | :--------------- |
135+
| Free | 5 team members |
136+
| Hobby | 5 team members |
137+
| Pro | 25+ team members |

docs/mint.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,8 @@
217217
"realtime/streams",
218218
"realtime/react-hooks",
219219
"realtime/subscribe-to-run",
220-
"realtime/subscribe-to-runs-with-tag"
220+
"realtime/subscribe-to-runs-with-tag",
221+
"realtime/subscribe-to-batch"
221222
]
222223
},
223224
{

0 commit comments

Comments
 (0)