Skip to content

Docs: Batch Trigger upgrades #1505

New issue

Have a question about this project? No Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “No Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? No Sign in to your account

Merged
merged 2 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
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
37 changes: 14 additions & 23 deletions docs/guides/examples/scrape-hacker-news.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@ import LocalDevelopment from "/snippets/local-development-extensions.mdx";
import ScrapingWarning from "/snippets/web-scraping-warning.mdx";

<div className="w-full h-full aspect-video">
<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/>
<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
/>
</div>

## Overview
Expand Down Expand Up @@ -125,12 +134,9 @@ export const summarizeHackerNews = schedules.task({
.batchTriggerAndWait(
articles.map((article) => ({
payload: { title: article.title!, link: article.link! },
idempotencyKey: article.link,
}))
)
.then((batch) =>
batch.runs.filter((run) => run.ok).map((run) => run.output)
);
.then((batch) => batch.runs.filter((run) => run.ok).map((run) => run.output));

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

```tsx summarize-hn-email.tsx
import {
Html,
Head,
Body,
Container,
Section,
Heading,
Text,
Link,
} from "@react-email/components";
import { Html, Head, Body, Container, Section, Heading, Text, Link } from "@react-email/components";

interface Article {
title: string;
link: string;
summary: string | null;
}

export const HNSummaryEmail: React.FC<{ articles: Article[] }> = ({
articles,
}) => (
export const HNSummaryEmail: React.FC<{ articles: Article[] }> = ({ articles }) => (
<Html>
<Head />
<Body style={{ fontFamily: "Arial, sans-serif", padding: "20px" }}>
Expand Down
66 changes: 53 additions & 13 deletions docs/idempotency.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ description: "An API call or operation is “idempotent” if it has the same re

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.

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

## `idempotencyKey` option

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:

```typescript
```ts
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";

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

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

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

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`:

```typescript
```ts
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";

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

// childTask will only be triggered once with the same idempotency key
await childTask.triggerAndWait(payload, { idempotencyKey });

// This is the same as the above
await childTask.triggerAndWait(payload, { idempotencyKey: "my-task-key" });
await childTask.trigger({ foo: "bar" }, { idempotencyKey });
},
});
```

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

```typescript
```ts
import { idempotencyKeys, tasks } from "@trigger.dev/sdk/v3";

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

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

```typescript
```ts
import { myTask } from "./trigger/myTasks";

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

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

```typescript
```ts
import { tasks } from "@trigger.dev/sdk/v3";

await tasks.batchTrigger("my-task", [
Expand All @@ -88,11 +92,47 @@ await tasks.batchTrigger("my-task", [
]);
```

## `idempotencyKeyTTL` option

By default idempotency keys are stored for 30 days. You can change this by passing the `idempotencyKeyTTL` option when triggering a task:

```ts
import { idempotencyKeys, task, wait } from "@trigger.dev/sdk/v3";

export const myTask = task({
id: "my-task",
retry: {
maxAttempts: 4,
},
run: async (payload: any) => {
const idempotencyKey = await idempotencyKeys.create("my-task-key");

// The idempotency key will expire after 60 seconds
await childTask.trigger({ foo: "bar" }, { idempotencyKey, idempotencyKeyTTL: "60s" });

await wait.for({ seconds: 61 });

// The idempotency key will have expired, so the childTask will be triggered again
await childTask.trigger({ foo: "bar" }, { idempotencyKey });

// Do something else, that may throw an error and cause the task to be retried
throw new Error("Something went wrong");
},
});
```

You can use the following units for the `idempotencyKeyTTL` option:

- `s` for seconds (e.g. `60s`)
- `m` for minutes (e.g. `5m`)
- `h` for hours (e.g. `2h`)
- `d` for days (e.g. `3d`)

## Payload-based idempotency

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.

```typescript
```ts
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
import { createHash } from "node:crypto";

Expand Down
87 changes: 48 additions & 39 deletions docs/limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ title: "Limits"
description: "There are some hard and soft limits that you might hit."
---

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

## Concurrency limits

| Pricing tier | Limit |
|:---------------- |:-------------------- |
| Free | 5 concurrent runs |
| Hobby | 25 concurrent runs |
| Pro | 100+ concurrent runs |
| Pricing tier | Limit |
| :----------- | :------------------- |
| Free | 5 concurrent runs |
| Hobby | 25 concurrent runs |
| Pro | 100+ concurrent runs |

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).

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

| Limit | Details |
|:----- |:------------------------- |
| :---- | :------------------------ |
| API | 1,500 requests per minute |

<RateLimitHitUseBatchTrigger/>
<RateLimitHitUseBatchTrigger />

## Queued tasks

The number of queued tasks by environment.

| Limit | Details |
|:------- |:------------------ |
| :------ | :----------------- |
| Dev | At most 500 |
| Staging | At most 10 million |
| Prod | At most 10 million |

## Schedules

| Pricing tier | Limit |
|:---------------- |:-------------------- |
| Free | 5 per project |
| Hobby | 100 per project |
| Pro | 1,000+ per project |
| Pricing tier | Limit |
| :----------- | :----------------- |
| Free | 5 per project |
| Hobby | 100 per project |
| Pro | 1,000+ per project |

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.

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.

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

## Task payloads and outputs

| Limit | Details |
| :--------------------- | :-------------------------------------------- |
| Single trigger payload | Must not exceed 3MB |
| Batch trigger payload | The total of all payloads must not exceed 1MB |
| Task outputs | Must not exceed 10MB |

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.

## Batch size

A single batch can have a maximum of 500 items.

<SoftLimit />

## Log retention

| Pricing tier | Limit |
|:---------------- |:--------- |
| Free | 1 day |
| Hobby | 7 days |
| Pro | 30 days |
| Pricing tier | Limit |
| :----------- | :------ |
| Free | 1 day |
| Hobby | 7 days |
| Pro | 30 days |

## Log size

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

#### Attribute Limits

- Span Attribute Count Limit: 256
- Log Attribute Count Limit: 256
- Span Attribute Value Length Limit: 1028 characters
- Log Attribute Value Length Limit: 1028 characters

#### Event and Link Limits

- Span Event Count Limit: 10
- Link Count Limit: 2
- Attributes per Link Limit: 10
- Attributes per Event Limit: 10

#### I/O Packet Length Limit

128 KB (131,072 bytes)

#### Attribute Clipping Behavior

- Attributes exceeding the value length limit (1028 characters) are discarded.
- If the total number of attributes exceeds 256, additional attributes are not included.

#### Attribute Value Size Calculation

- Strings: Actual length of the string
- Numbers: 8 bytes
- Booleans: 4 bytes
Expand All @@ -93,25 +112,15 @@ We limit the size of logs to prevent oversized data potentially causing issues.

</Expandable>

## Task payloads and outputs

| Limit | Details |
|:--- |:--- |
| Single trigger payload | Must not exceed 10MB |
| Batch trigger payload | The total of all payloads must not exceed 10MB |
| Task outputs | Must not exceed 10MB |

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.

## Alerts

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).

| Pricing tier | Limit |
|:---------------- |:----------------------- |
| Free | 1 alert destination |
| Hobby | 3 alert destinations |
| Pro | 100+ alert destinations |
| Pricing tier | Limit |
| :----------- | :---------------------- |
| Free | 1 alert destination |
| Hobby | 3 alert destinations |
| Pro | 100+ alert destinations |

## Machines

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

## Team members

| Pricing tier | Limit |
|:---------------- |:----------------- |
| Free | 5 team members |
| Hobby | 5 team members |
| Pro | 25+ team members |
| Pricing tier | Limit |
| :----------- | :--------------- |
| Free | 5 team members |
| Hobby | 5 team members |
| Pro | 25+ team members |
3 changes: 2 additions & 1 deletion docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@
"realtime/streams",
"realtime/react-hooks",
"realtime/subscribe-to-run",
"realtime/subscribe-to-runs-with-tag"
"realtime/subscribe-to-runs-with-tag",
"realtime/subscribe-to-batch"
]
},
{
Expand Down
Loading