Skip to content

Commit 1feabd7

Browse files
authored
Docs: Adds a Reduce your spend docs page (#2330)
1 parent 134942b commit 1feabd7

File tree

4 files changed

+170
-0
lines changed

4 files changed

+170
-0
lines changed

docs/docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@
167167
"group": "Troubleshooting",
168168
"pages": [
169169
"troubleshooting",
170+
"how-to-reduce-your-spend",
170171
"troubleshooting-debugging-in-vscode",
171172
"upgrading-packages",
172173
"troubleshooting-uptime-status",

docs/how-to-reduce-your-spend.mdx

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
---
2+
title: "How to reduce your spend"
3+
description: "Tips and best practices to reduce your costs on Trigger.dev"
4+
---
5+
6+
## Check out your usage page regularly
7+
8+
Monitor your usage dashboard to understand your spending patterns. You can see:
9+
- Your most expensive tasks
10+
- Your total duration by task
11+
- Number of runs by task
12+
- Spikes in your daily usage
13+
14+
![Usage dashboard](./images/usage-dashboard.png)
15+
16+
You can view your usage page by clicking the "Organization" menu in the top left of the dashboard and then clicking "Usage".
17+
18+
## Create billing alerts
19+
20+
Configure billing alerts in your dashboard to get notified when you approach spending thresholds. This helps you:
21+
- Catch unexpected cost increases early
22+
- Identify runaway tasks before they become expensive
23+
24+
![Billing alerts](./images/billing-alerts-ui.png)
25+
26+
You can view your billing alerts page by clicking the "Organization" menu in the top left of the dashboard and then clicking "Settings".
27+
28+
## Reduce your machine sizes
29+
30+
The larger the machine, the more it costs per second. [View the machine pricing](https://trigger.dev/pricing#computePricing).
31+
32+
Start with the smallest machine that works, then scale up only if needed:
33+
34+
```ts
35+
// Default: small-1x (0.5 vCPU, 0.5 GB RAM)
36+
export const lightTask = task({
37+
id: "light-task",
38+
// No machine config needed - uses small-1x by default
39+
run: async (payload) => {
40+
// Simple operations
41+
},
42+
});
43+
44+
// Only use larger machines when necessary
45+
export const heavyTask = task({
46+
id: "heavy-task",
47+
machine: "medium-1x", // 1 vCPU, 2 GB RAM
48+
run: async (payload) => {
49+
// CPU/memory intensive operations
50+
},
51+
});
52+
```
53+
54+
You can also override machine size when triggering if you know certain payloads need more resources. [Read more about machine sizes](/machines).
55+
56+
## Avoid duplicate work using idempotencyKey
57+
58+
Idempotency keys prevent expensive duplicate work by ensuring the same operation isn't performed multiple times. This is especially valuable during task retries or when the same trigger might fire multiple times.
59+
60+
When you use an idempotency key, Trigger.dev remembers the result and skips re-execution, saving you compute costs:
61+
62+
```ts
63+
export const expensiveApiCall = task({
64+
id: "expensive-api-call",
65+
run: async (payload: { userId: string }) => {
66+
// This expensive operation will only run once per user
67+
await wait.for({ seconds: 30 }, {
68+
idempotencyKey: `user-processing-${payload.userId}`,
69+
idempotencyKeyTTL: "1h"
70+
});
71+
72+
const result = await processUserData(payload.userId);
73+
return result;
74+
},
75+
});
76+
```
77+
78+
You can use idempotency keys with various wait functions:
79+
80+
```ts
81+
// Skip waits during retries
82+
const token = await wait.createToken({
83+
idempotencyKey: `daily-report-${new Date().toDateString()}`,
84+
idempotencyKeyTTL: "24h",
85+
});
86+
87+
// Prevent duplicate child task execution
88+
await childTask.triggerAndWait(
89+
{ data: payload },
90+
{
91+
idempotencyKey: `process-${payload.id}`,
92+
idempotencyKeyTTL: "1h",
93+
}
94+
);
95+
```
96+
97+
The `idempotencyKeyTTL` controls how long the result is cached. Use shorter TTLs (like "1h") for time-sensitive operations, or longer ones (up to 30 days default) for expensive operations that rarely need re-execution. This prevents both unnecessary duplicate work and stale data issues.
98+
99+
## Do more work in parallel in a single task
100+
101+
Sometimes it's more efficient to do more work in a single task than split across many. This is particularly true when you're doing lots of async work such as API calls – most of the time is spent waiting, so it's an ideal candidate for doing calls in parallel inside the same task.
102+
103+
```ts
104+
export const processItems = task({
105+
id: "process-items",
106+
run: async (payload: { items: string[] }) => {
107+
// Process all items in one run
108+
for (const item of payload.items) {
109+
// Do async work in parallel
110+
// This works very well for API calls
111+
await processItem(item);
112+
}
113+
},
114+
});
115+
```
116+
117+
## Don't needlessly retry
118+
119+
When an error is thrown in a task, your run will be automatically reattempted based on your [retry settings](/tasks/overview#retry-options).
120+
121+
Try setting lower `maxAttempts` for less critical tasks:
122+
123+
```ts
124+
export const apiTask = task({
125+
id: "api-task",
126+
retry: {
127+
maxAttempts: 2, // Don't retry forever
128+
},
129+
run: async (payload) => {
130+
// API calls that might fail
131+
},
132+
});
133+
```
134+
135+
This is very useful for intermittent errors, but if there's a permanent error you don't want to retry because you will just keep failing and waste compute. Use [AbortTaskRunError](/errors-retrying#using-aborttaskrunerror) to prevent a retry:
136+
137+
```ts
138+
import { task, AbortTaskRunError } from "@trigger.dev/sdk/v3";
139+
140+
export const someTask = task({
141+
id: "some-task",
142+
run: async (payload) => {
143+
const result = await doSomething(payload);
144+
145+
if (!result.success) {
146+
// This is a known permanent error, so don't retry
147+
throw new AbortTaskRunError(result.error);
148+
}
149+
150+
return result
151+
},
152+
});
153+
```
154+
155+
156+
157+
## Use appropriate maxDuration settings
158+
159+
Set realistic maxDurations to prevent runs from executing for too long:
160+
161+
```ts
162+
export const boundedTask = task({
163+
id: "bounded-task",
164+
maxDuration: 300, // 5 minutes max
165+
run: async (payload) => {
166+
// Task will be terminated after 5 minutes
167+
},
168+
});
169+
```

docs/images/billing-alerts-ui.png

95.9 KB
Loading

docs/images/usage-dashboard.png

281 KB
Loading

0 commit comments

Comments
 (0)