Skip to content

Commit efa8c68

Browse files
bruxodasilvaToriLindsayhyperlint-ai[bot]
authored
Workflows: Added considerations regarding using promise.race and promise.any with Workflows (#20032)
* Added consideration regarding using promise.race and promise.any with Workflows * Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay <[email protected]> * Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay <[email protected]> * Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay <[email protected]> * Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay <[email protected]> * Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay <[email protected]> * Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay <[email protected]> * Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: hyperlint-ai[bot] <154288675+hyperlint-ai[bot]@users.noreply.github.com> --------- Co-authored-by: ToriLindsay <[email protected]> Co-authored-by: hyperlint-ai[bot] <154288675+hyperlint-ai[bot]@users.noreply.github.com>
1 parent ec2eb6c commit efa8c68

File tree

1 file changed

+96
-1
lines changed

1 file changed

+96
-1
lines changed

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

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export class MyWorkflow extends WorkflowEntrypoint {
243243

244244
### Name steps deterministically
245245

246-
Steps should be named deterministically (ie, not using the current date/time, randomness, etc). This ensures that their state is cached, and prevents the step from being rerun unnecessarily. Step names act as the "cache key" in your Workflow.
246+
Steps should be named deterministically (that is, not using the current date/time, randomness, etc). This ensures that their state is cached, and prevents the step from being rerun unnecessarily. Step names act as the "cache key" in your Workflow.
247247

248248
<TypeScriptExample filename="index.ts">
249249
```ts
@@ -283,6 +283,101 @@ export class MyWorkflow extends WorkflowEntrypoint {
283283
```
284284
</TypeScriptExample>
285285

286+
### Take care with `Promise.race()` and `Promise.any()`
287+
288+
Workflows allows the usage steps within the `Promise.race()` or `Promise.any()` methods as a way to achieve concurrent steps execution. However, some considerations must be taken.
289+
290+
Due to the nature of Workflows' instance lifecycle, and given that a step inside a Promise will run until it finishes, the step that is returned during the first passage may not be the actual cached step, as [steps are cached by their names](#name-steps-deterministically).
291+
292+
<TypeScriptExample filename="index.ts">
293+
```ts
294+
295+
// helper sleep method
296+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
297+
298+
export class MyWorkflow extends WorkflowEntrypoint {
299+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
300+
// 🔴 Bad: The `Promise.race` is not surrounded by a `step.do`, which may cause undeterministic caching behavior.
301+
const race_return = await Promise.race(
302+
[
303+
step.do(
304+
'Promise first race',
305+
async () => {
306+
await sleep(1000);
307+
return "first";
308+
}
309+
),
310+
step.do(
311+
'Promise second race',
312+
async () => {
313+
return "second";
314+
}
315+
),
316+
]
317+
);
318+
319+
await step.sleep("Sleep step", "2 hours");
320+
321+
return await step.do(
322+
'Another step',
323+
async () => {
324+
// This step will return `first`, even though the `Promise.race` first returned `second`.
325+
return race_return;
326+
},
327+
);
328+
}
329+
}
330+
```
331+
</TypeScriptExample>
332+
333+
To ensure consistency, we suggest to surround the `Promise.race()` or `Promise.any()` within a `step.do()`, as this will ensure caching consistency across multiple passages.
334+
335+
<TypeScriptExample filename="index.ts">
336+
```ts
337+
338+
// helper sleep method
339+
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
340+
341+
export class MyWorkflow extends WorkflowEntrypoint {
342+
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
343+
// ✅ Good: The `Promise.race` is surrounded by a `step.do`, ensuring deterministic caching behavior.
344+
const race_return = await step.do(
345+
'Promise step',
346+
async () => {
347+
return await Promise.race(
348+
[
349+
step.do(
350+
'Promise first race',
351+
async () => {
352+
await sleep(1000);
353+
return "first";
354+
}
355+
),
356+
step.do(
357+
'Promise second race',
358+
async () => {
359+
return "second";
360+
}
361+
),
362+
]
363+
);
364+
}
365+
);
366+
367+
await step.sleep("Sleep step", "2 hours");
368+
369+
return await step.do(
370+
'Another step',
371+
async () => {
372+
// This step will return `second` because the `Promise.race` was surround by the `step.do` method.
373+
return race_return;
374+
},
375+
);
376+
}
377+
}
378+
```
379+
</TypeScriptExample>
380+
286381
### Instance IDs are unique
287382

288383
Workflow [instance IDs](/workflows/build/workers-api/#workflowinstance) are unique per Workflow. The ID is the unique identifier that associates logs, metrics, state and status of a run to a specific an instance, even after completion. Allowing ID re-use would make it hard to understand if a Workflow instance ID referred to an instance that run yesterday, last week or today.

0 commit comments

Comments
 (0)