Skip to content

Commit 205bdc3

Browse files
authored
docs(wait): separate compute billing from concurrency release (#4405)
The wait docs describe the 5 second compute-billing threshold as if it were also the suspension threshold. It isn't, and the gap is confusing when you're sizing a poll interval: - **Compute** stops being charged for any wait longer than 5 seconds. - **Concurrency** is only released once the machine has been snapshotted and shut down. For `wait.for` and `wait.until` that happens 60 seconds into the wait — a shorter wait stays `EXECUTING` and holds its concurrency slot for the whole wait, even though the compute is free. So `await wait.for({ seconds: 30 })` in a polling loop never releases its slot, which looks like a bug if the docs told you waits over 5 seconds checkpoint. ## Changes **`docs/snippets/paused-execution-free.mdx`** — rendered on `/wait`, `/wait-for` and `/wait-until`. Drops "we checkpoint and" from the billing sentence so it's purely about compute, then adds one paragraph for the concurrency half. **`docs/queue-concurrency.mdx`** — the "Waits and concurrency" section states flatly that waiting runs don't consume slots. Adds a short subsection for the time-based exception. **`docs/how-to-reduce-your-spend.mdx`** — "Waits longer than 5 seconds automatically checkpoint your task, meaning you don't pay for compute" → the compute claim only. Code comments follow, plus a pointer that waiting doesn't always free concurrency. **`docs/how-it-works.mdx`** — the Checkpoint-Resume walkthrough used `wait.for({ seconds: 30 })` as *the* example of a wait that suspends. Bumped to 5 minutes and noted the sub-60s exception. No behaviour change — docs only.
1 parent 38bf82a commit 205bdc3

4 files changed

Lines changed: 14 additions & 8 deletions

File tree

docs/how-it-works.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Here's how the Checkpoint-Resume System works:
148148

149149
2. **Subtask Handling**: If a task needs to trigger a subtask, it can do so and wait for its completion using `triggerAndWait`
150150

151-
3. **State Checkpointing**: While waiting for a subtask or during a programmed pause (e.g., `wait.for({ seconds: 30 })`), the system uses CRIU (Checkpoint/Restore In Userspace) to create a checkpoint of the task's entire state, including memory, CPU registers, and open file descriptors.
151+
3. **State Checkpointing**: While waiting for a subtask or during a long programmed pause (e.g., `wait.for({ minutes: 5 })`), the system uses CRIU (Checkpoint/Restore In Userspace) to create a checkpoint of the task's entire state, including memory, CPU registers, and open file descriptors. A `wait.for()` or `wait.until()` shorter than 60 seconds doesn't checkpoint — see [Wait](/wait).
152152

153153
4. **Resource Release**: After checkpointing, the parent task's resources are released, freeing up the execution environment.
154154

@@ -178,9 +178,9 @@ export const parentTask = task({
178178
console.log("Child task result:", result);
179179

180180
// This will also cause the task to be checkpointed and suspended
181-
await wait.for({ seconds: 30 });
181+
await wait.for({ minutes: 5 });
182182

183-
console.log("Resumed after 30 seconds");
183+
console.log("Resumed after 5 minutes");
184184

185185
return "Parent task completed";
186186
},

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,25 +170,25 @@ export const boundedTask = task({
170170

171171
## Use waitpoints instead of polling
172172

173-
Waits longer than 5 seconds automatically checkpoint your task, meaning you don't pay for compute while waiting. Use `wait.for()`, `wait.until()`, or `triggerAndWait()` instead of polling loops.
173+
You don't pay for compute during any wait longer than 5 seconds. Use `wait.for()`, `wait.until()`, or `triggerAndWait()` instead of polling loops.
174174

175175
```ts
176176
import { task, wait } from "@trigger.dev/sdk";
177177

178178
export const waitpointTask = task({
179179
id: "waitpoint-task",
180180
run: async (payload) => {
181-
// This wait is free - your task is checkpointed
181+
// You aren't charged for compute during this wait
182182
await wait.for({ minutes: 5 });
183183

184-
// Parent is also checkpointed while waiting for child tasks
184+
// The parent isn't charged either while it waits for a child task
185185
const result = await childTask.triggerAndWait({ data: payload });
186186
return result;
187187
},
188188
});
189189
```
190190

191-
[Read more about waitpoints](/wait-for).
191+
Waiting saves you compute, but it doesn't always free up concurrency — see [Wait](/wait).
192192

193193
## Use debounce to consolidate multiple triggers
194194

docs/queue-concurrency.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,10 @@ For example, if you have a queue with a `concurrencyLimit` of 1:
199199
- When the executing run reaches a waitpoint and checkpoints, it releases its slot
200200
- The next queued run can then begin execution
201201

202+
### Short time-based waits keep their slot
203+
204+
Checkpointing takes time, so a run doesn't checkpoint the moment it reaches a waitpoint. For [`wait.for()`](/wait-for) and [`wait.until()`](/wait-until) it happens 60 seconds into the wait, so anything shorter stays `EXECUTING` and holds its slot for the whole wait. If you're polling in a loop, use an interval comfortably above 60 seconds so the slot is actually released between polls.
205+
202206
### Waiting for a subtask on a different queue
203207

204208
When a parent task triggers and waits for a subtask on a different queue, the parent task will checkpoint and release its concurrency slot once it reaches the wait point. This prevents environment deadlocks where all concurrency slots would be occupied by waiting tasks.
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
In the Trigger.dev Cloud we automatically pause execution of tasks when they are waiting for
22
longer than a few seconds.
33

4-
When triggering and waiting for subtasks, the parent is checkpointed and while waiting does not count towards compute usage. When waiting for a time period (`wait.for` or `wait.until`), if the wait is longer than 5 seconds we checkpoint and it does not count towards compute usage.
4+
When triggering and waiting for subtasks, the parent is checkpointed and while waiting does not count towards compute usage. When waiting for a time period (`wait.for` or `wait.until`), a wait longer than 5 seconds does not count towards compute usage.
5+
6+
Free compute isn't the same as freed concurrency: the concurrency slot is only released once we've snapshotted the machine and shut it down. For `wait.for` and `wait.until` that happens 60 seconds into the wait, so anything shorter stays `EXECUTING` and holds its slot for the whole wait.

0 commit comments

Comments
 (0)