4f95c9de4e
* v3: cancel subtasks when parent task runs are cancelled * v3: recover from server rate limiting errors in a more reliable way - Changing from sliding window to token bucket in the API rate limiter, to help smooth out traffic - Adding spans to the API Client core & SDK functions - Added waiting spans when retrying in the API Client - Retrying in the API Client now respects the x-ratelimit-reset - Retrying ApiError’s in tasks now respects the x-ratelimit-reset - Added AbortTaskRunError that when thrown will stop retries - Added idempotency keys SDK functions and automatically injecting the run ID when inside a task - Added the ability to configure ApiRequestOptions (retries only for now) globally and on specific calls - Implement the maxAttempts TaskRunOption (it wasn’t doing anything before) * Adding some docs about the request options * Fix type error * Remove context propagation through graphile jobs * Remove logger * only select a subset of task run columns * limit columns selected in batchTrigger as well * added idempotency doc * allow scoped idempotency keys, and fixed an issue with the unique index on BatchTaskRun and TaskRun * Removed old cancel task run children code
118 lines
4.5 KiB
Plaintext
118 lines
4.5 KiB
Plaintext
---
|
|
title: "Idempotency"
|
|
description: "An API call or operation is “idempotent” if it has the same result when called more than once."
|
|
---
|
|
|
|
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.
|
|
|
|
## `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
|
|
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
|
|
|
|
export const myTask = task({
|
|
id: "my-task",
|
|
retry: {
|
|
maxAttempts: 4,
|
|
},
|
|
run: async (payload: any) => {
|
|
// By default, idempotency keys generated are unique to the run, to prevent retries from duplicating child tasks
|
|
const idempotencyKey = await idempotencyKeys.create("my-task-key");
|
|
|
|
// childTask will only be triggered once with the same idempotency key
|
|
await childTask.triggerAndWait(payload, { idempotencyKey });
|
|
|
|
// Do something else, that may throw an error and cause the task to be retried
|
|
},
|
|
});
|
|
```
|
|
|
|
You can use the `idempotencyKeys.create` SDK function to create an idempotency key before passing it to the `options` object.
|
|
|
|
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
|
|
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
|
|
|
|
export const myTask = task({
|
|
id: "my-task",
|
|
retry: {
|
|
maxAttempts: 4,
|
|
},
|
|
run: async (payload: any) => {
|
|
// This idempotency key will be the same for all runs of this task
|
|
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" });
|
|
},
|
|
});
|
|
```
|
|
|
|
If you are triggering a task from your backend code, you can use the `idempotencyKeys.create` SDK function to create an idempotency key.
|
|
|
|
```typescript
|
|
import { idempotencyKeys, tasks } from "@trigger.dev/sdk/v3";
|
|
|
|
// You can also pass an array of strings to create a idempotency key
|
|
const idempotencyKey = await idempotenceKeys.create([myUser.id, "my-task"]);
|
|
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
|
|
import { myTask } from "./trigger/myTasks";
|
|
|
|
// You can also pass an array of strings to create a idempotency key
|
|
await myTask.trigger({ some: "data" }, { idempotencyKey: myUser.id });
|
|
```
|
|
|
|
<Note>Make sure you provide sufficiently unique keys to avoid collisions.</Note>
|
|
|
|
You can pass the `idempotencyKey` when calling `batchTrigger` as well:
|
|
|
|
```typescript
|
|
import { tasks } from "@trigger.dev/sdk/v3";
|
|
|
|
await tasks.batchTrigger("my-task", [
|
|
{
|
|
payload: { some: "data" },
|
|
options: { idempotencyKey: await idempotenceKeys.create(myUser.id) },
|
|
},
|
|
]);
|
|
```
|
|
|
|
## 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
|
|
import { idempotencyKeys, task } from "@trigger.dev/sdk/v3";
|
|
import { createHash } from "node:crypto";
|
|
|
|
// Somewhere in your code
|
|
const idempotencyKey = await idempotencyKeys.create(hash(childPayload));
|
|
// childTask will only be triggered once with the same idempotency key
|
|
await tasks.trigger("child-task", { some: "payload" }, { idempotencyKey });
|
|
|
|
// Create a hash of the payload using Node.js crypto
|
|
// Ideally, you'd do a stable serialization of the payload before hashing, to ensure the same payload always results in the same hash
|
|
function hash(payload: any): string {
|
|
const hash = createHash("sha256");
|
|
hash.update(JSON.stringify(payload));
|
|
return hash.digest("hex");
|
|
}
|
|
```
|
|
|
|
## Important notes
|
|
|
|
Idempotency keys, even the ones scoped globally, are actually scoped to the task and the environment. This means that you cannot collide with keys from other environments (e.g. dev will never collide with prod), or to other projects and orgs.
|
|
|
|
If you use the same idempotency key for triggering different tasks, the tasks will not be idempotent, and both tasks will be triggered. There's currently no way to make multiple tasks idempotent with the same key.
|