From 7df552ad35d4e3dd61eec4c51a9a2573990a364d Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Mon, 17 Feb 2025 17:41:38 +0000 Subject: [PATCH 1/8] Added consideration regarding using promise.race and promise.any with Workflows --- .../workflows/build/rules-of-workflows.mdx | 97 ++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index 145f5d830aee2fc..8a789fce34808d0 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -243,7 +243,7 @@ export class MyWorkflow extends WorkflowEntrypoint { ### Name steps deterministically -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. +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. ```ts @@ -283,6 +283,101 @@ export class MyWorkflow extends WorkflowEntrypoint { ``` +### Take care with `Promise.race()` and `Promise.any()` + +Workflows allow 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. + +Due to the nature Workflows's instance lifecycle, and given that a step inside a Promise will run until they finished, 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). + + +```ts + +// helper sleep method +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +export class MyWorkflow extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep) { + // 🔴 Bad: The `Promise.race` is not surround by a `step.do` which may cause undeterministic caching behavior. + const race_return = await Promise.race( + [ + step.do( + 'Promise first race', + async () => { + await sleep(1000); + return "first"; + } + ), + step.do( + 'Promise second race', + async () => { + return "second"; + } + ), + ] + ); + + await step.sleep("Sleep step", "2 hours"); + + return await step.do( + 'Another step', + async () => { + // This step will return `first`, even though the `Promise.race` first returned `second`. + return race_return; + }, + ); + } +} +``` + + +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. + + +```ts + +// helper sleep method +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); + +export class MyWorkflow extends WorkflowEntrypoint { + async run(event: WorkflowEvent, step: WorkflowStep) { + // ✅ Good: The `Promise.race` is surround by a `step.do`, ensuring deterministic caching behavior. + const race_return = await step.do( + 'Promise step', + async () => { + return await Promise.race( + [ + step.do( + 'Promise first race', + async () => { + await sleep(1000); + return "first"; + } + ), + step.do( + 'Promise second race', + async () => { + return "second"; + } + ), + ] + ); + } + ); + + await step.sleep("Sleep step", "2 hours"); + + return await step.do( + 'Another step', + async () => { + // This step will return `second`, as the `Promise.race` was surround by `step.do` method. + return race_return; + }, + ); + } +} +``` + + ### Instance IDs are unique 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. From 3b9440aa2d82939f004a451286a927196f9fb61c Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 18 Feb 2025 14:22:56 +0000 Subject: [PATCH 2/8] Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay --- src/content/docs/workflows/build/rules-of-workflows.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index 8a789fce34808d0..24aa1012bfb3ece 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -285,7 +285,7 @@ export class MyWorkflow extends WorkflowEntrypoint { ### Take care with `Promise.race()` and `Promise.any()` -Workflows allow 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. +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. Due to the nature Workflows's instance lifecycle, and given that a step inside a Promise will run until they finished, 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). From cb6b3dcaebc80fc7a9960a76a017b86e890b580c Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 18 Feb 2025 14:23:13 +0000 Subject: [PATCH 3/8] Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay --- src/content/docs/workflows/build/rules-of-workflows.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index 24aa1012bfb3ece..61e4b2d6116f614 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -287,7 +287,7 @@ export class MyWorkflow extends WorkflowEntrypoint { 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. -Due to the nature Workflows's instance lifecycle, and given that a step inside a Promise will run until they finished, 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). +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). ```ts From fb528b97090a73bd63c441c7e91810061ec380a8 Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 18 Feb 2025 14:23:20 +0000 Subject: [PATCH 4/8] Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay --- src/content/docs/workflows/build/rules-of-workflows.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index 61e4b2d6116f614..c329ef9c43161a0 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -330,7 +330,7 @@ export class MyWorkflow extends WorkflowEntrypoint { ``` -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. +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. ```ts From 7237a416b5eda52ff4ead8a0f999ce592fd029d0 Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 18 Feb 2025 14:23:37 +0000 Subject: [PATCH 5/8] Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay --- src/content/docs/workflows/build/rules-of-workflows.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index c329ef9c43161a0..a9953a61b61b0af 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -297,7 +297,7 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { - // 🔴 Bad: The `Promise.race` is not surround by a `step.do` which may cause undeterministic caching behavior. + // 🔴 Bad: The `Promise.race` is not surrounded by a `step.do`, which may cause undeterministic caching behavior. const race_return = await Promise.race( [ step.do( From a65fc20a1072a83216081cdbe881e8023ec02173 Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 18 Feb 2025 14:23:45 +0000 Subject: [PATCH 6/8] Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay --- src/content/docs/workflows/build/rules-of-workflows.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index a9953a61b61b0af..4ba8d378af366ef 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -340,7 +340,7 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); export class MyWorkflow extends WorkflowEntrypoint { async run(event: WorkflowEvent, step: WorkflowStep) { - // ✅ Good: The `Promise.race` is surround by a `step.do`, ensuring deterministic caching behavior. + // ✅ Good: The `Promise.race` is surrounded by a `step.do`, ensuring deterministic caching behavior. const race_return = await step.do( 'Promise step', async () => { From 9f7dd1d01e74d6722f0f9d19696a5d8f42f8f279 Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 18 Feb 2025 14:23:54 +0000 Subject: [PATCH 7/8] Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: ToriLindsay --- src/content/docs/workflows/build/rules-of-workflows.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index 4ba8d378af366ef..0d40031bfe6f1ee 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -369,7 +369,7 @@ export class MyWorkflow extends WorkflowEntrypoint { return await step.do( 'Another step', async () => { - // This step will return `second`, as the `Promise.race` was surround by `step.do` method. + // This step will return `second` because the `Promise.race` was surround by the `step.do` method. return race_return; }, ); From 38b5a2562cc56fe4b390f3b7761f8b12c52d73e8 Mon Sep 17 00:00:00 2001 From: Diogo Ferreira Date: Tue, 18 Feb 2025 14:24:36 +0000 Subject: [PATCH 8/8] Update src/content/docs/workflows/build/rules-of-workflows.mdx Co-authored-by: hyperlint-ai[bot] <154288675+hyperlint-ai[bot]@users.noreply.github.com> --- src/content/docs/workflows/build/rules-of-workflows.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/workflows/build/rules-of-workflows.mdx b/src/content/docs/workflows/build/rules-of-workflows.mdx index 0d40031bfe6f1ee..9af4259837fa67f 100644 --- a/src/content/docs/workflows/build/rules-of-workflows.mdx +++ b/src/content/docs/workflows/build/rules-of-workflows.mdx @@ -243,7 +243,7 @@ export class MyWorkflow extends WorkflowEntrypoint { ### Name steps deterministically -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. +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. ```ts