Skip to content

Commit 7eceabc

Browse files
authored
Update rules-of-workflows.mdx
1 parent 0aa3b63 commit 7eceabc

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

src/content/docs/workflows/build/rules-of-workflows.mdx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,80 @@ export class MyWorkflow extends WorkflowEntrypoint {
205205
```
206206
</TypeScriptExample>
207207

208+
### Workflow code re-executes after hibernation
209+
210+
When a Workflow hibernates and resumes, it's important to understand that **the entire workflow function runs again from the beginning**. However, completed steps return their cached results immediately without re-executing their logic.
211+
212+
This means:
213+
- All code outside of steps is re-executed
214+
- Variables defined outside steps are re-initialized
215+
- Non-deterministic values (like `Math.random()` or `Date.now()`) outside steps will produce different results
216+
- Only values returned from `step.do()` calls are preserved across hibernations
217+
218+
<TypeScriptExample filename="index.ts">
219+
```ts
220+
export class MyWorkflow extends WorkflowEntrypoint {
221+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
222+
// 🔴 This random value will be different after hibernation
223+
const randomOutside = Math.random() * 100;
224+
225+
const capturedValue = await step.do("capture-value", async () => {
226+
// ✅ This random value is preserved across hibernations
227+
const randomInside = Math.random() * 100;
228+
return randomInside;
229+
});
230+
231+
// Long sleep that causes hibernation
232+
await step.sleep("long-sleep", "3 hours");
233+
234+
await step.do("compare-values", async () => {
235+
console.log("Random outside:", randomOutside); // New value after hibernation!
236+
console.log("Captured value:", capturedValue); // Same value as before
237+
});
238+
}
239+
}
240+
```
241+
</TypeScriptExample>
242+
243+
This behavior is especially important for loops where iteration counters must be part of step returns:
244+
245+
<TypeScriptExample filename="index.ts">
246+
```ts
247+
export class MyWorkflow extends WorkflowEntrypoint {
248+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
249+
// 🔴 Bad: Loop counter will reset after hibernation
250+
let iteration = 0;
251+
while (iteration < 5) {
252+
await step.do(`process-${iteration}`, async () => {
253+
// After hibernation, iteration resets to 0 causing duplicate step names!
254+
return { processed: iteration };
255+
});
256+
iteration++;
257+
}
258+
}
259+
}
260+
```
261+
</TypeScriptExample>
262+
263+
<TypeScriptExample filename="index.ts">
264+
```ts
265+
export class MyWorkflow extends WorkflowEntrypoint {
266+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
267+
// ✅ Good: Loop counter preserved in step returns
268+
const initial = await step.do("init", async () => ({ iteration: 0 }));
269+
let state = initial;
270+
271+
while (state.iteration < 5) {
272+
state = await step.do(`process-${state.iteration}`, async () => {
273+
// state.iteration comes from previous step, survives hibernation
274+
return { iteration: state.iteration + 1 };
275+
});
276+
}
277+
}
278+
}
279+
```
280+
</TypeScriptExample>
281+
208282
### Do not mutate your incoming events
209283

210284
The `event` passed to your Workflow's `run` method is immutable: changes you make to the event are not persisted across steps and/or Workflow restarts.

0 commit comments

Comments
 (0)