You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/idempotency.mdx
+69-2Lines changed: 69 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -31,8 +31,7 @@ sequenceDiagram
31
31
32
32
Other common use cases include:
33
33
34
-
-**Preventing duplicate emails** - Ensure a confirmation email is only sent once, even if the parent task retries
35
-
-**Avoiding double-charging customers** - Prevent duplicate payment processing during retries
34
+
-**Coordinating external side effects** - Use Trigger.dev idempotency to prevent duplicate task triggers, and use the external provider's idempotency mechanism when available to make retries of side-effecting requests safe
36
35
-**One-time setup tasks** - Ensure initialization or migration tasks only run once
37
36
-**Deduplicating webhook processing** - Handle the same webhook event only once, even if it's delivered multiple times
38
37
@@ -63,6 +62,74 @@ export const myTask = task({
63
62
64
63
You can use the `idempotencyKeys.create` SDK function to create an idempotency key before passing it to the `options` object.
65
64
65
+
<Warning>
66
+
Trigger.dev idempotency keys deduplicate task-trigger requests. They do not make arbitrary side effects inside `run()` exactly-once.
67
+
68
+
For external side effects, such as payments, also use the external provider's idempotency mechanism. A task can retry or crash after the provider accepts a request but before the task completes.
69
+
</Warning>
70
+
71
+
## Coordinating payment operations
72
+
73
+
For a payment operation, use a deterministic business-operation ID to derive idempotency keys for both layers. Trigger.dev deduplicates child task triggers, while your payment provider deduplicates requests for the same operation.
// Stripe uses this key to make retries of the same request idempotent.
95
+
idempotencyKey: payload.operationId,
96
+
}
97
+
);
98
+
},
99
+
});
100
+
```
101
+
102
+
```ts trigger/initiate-refund.ts
103
+
import { task } from"@trigger.dev/sdk";
104
+
import { refundPayment } from"./refund";
105
+
106
+
exportconst initiateRefund =task({
107
+
id: "initiate-refund",
108
+
run: async (payload: {
109
+
refundId:string;
110
+
paymentIntentId:string;
111
+
amount:number;
112
+
}) => {
113
+
// Use an ID that identifies this business operation, not a random value.
114
+
const operationId =`refund:${payload.refundId}`;
115
+
116
+
awaitrefundPayment.trigger(
117
+
{
118
+
paymentIntentId: payload.paymentIntentId,
119
+
amount: payload.amount,
120
+
operationId,
121
+
},
122
+
{
123
+
// Trigger.dev deduplicates this child task trigger when this task retries.
124
+
idempotencyKey: operationId,
125
+
}
126
+
);
127
+
},
128
+
});
129
+
```
130
+
131
+
This does not provide a universal exactly-once guarantee for arbitrary distributed side effects. Trigger.dev deduplicates task triggers, while Stripe uses the idempotency key to safely retry the same refund request.
132
+
66
133
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`:
0 commit comments