diff --git a/llms-full.txt b/llms-full.txt index 55526efc..f6fc6aef 100644 --- a/llms-full.txt +++ b/llms-full.txt @@ -113101,19 +113101,53 @@ If no slots are available, new steps enter the **waitlist** until resources free Workflow parallelism waitlist - -Upstash Workflow does not support per-step level configuration. Meaning that you can attach a flow-control configuration -to the workflow run and all the steps will inherit to the same limits. -Following the analogy above, you cannot enforce parallelism limit on "green" steps natively. +## Step-level Flow Control + +The flow control configuration of a run applies to all of its steps. To limit a single `context.run` step more +strictly, pass a flow control configuration as the third argument of `context.run`: + +```typescript +export const { POST } = serve(async (context) => { + await context.run("prepare", () => { ... }); + + await context.run( + "charge customer", + () => chargeCustomer(), + { + flowControl: { key: "payment-provider", parallelism: 3 }, + } + ); + + await context.run("notify", () => { ... }); +}); +``` -The context.call and context.invoke steps are exception this to this rule and accept their own flow control configuration: +Only the `charge customer` step is bound by the `payment-provider` key. The other steps keep the configuration +the run was triggered with. Since the key is shared, at most 3 `charge customer` steps run at the same time +across all runs using that key, no matter how many runs are active. -* [context.call](/docs/workflow/basics/context/call) – lets you run external HTTP requests under a separate key, so you can throttle third‑party API calls independently of your workflow logic. -* [context.invoke](/docs/workflow/basics/context/invoke) – starts a new workflow run with its own flow control configuration. This allows the invoked workflow to run under different limits than the parent workflow, giving you more precise control. +Following the analogy above, this lets you enforce a parallelism limit on the "green" steps only. -If you want to throttle a specific `context.run` step, the recommended approach is to **extract it into a separate workflow** and call it using `context.invoke()` with its own flow control configuration with a stricter limits. +The step-level configuration replaces the run's flow control for that step as a whole: a step with its own +flow control configuration does not also count against the run's flow control key. + + +When the step with flow control is reached right after a `context.run`, `context.sleep` or +`context.sleepUntil` step, its configuration is attached to the request which delivers it, at no extra cost. +In other cases (for example, when it is the first step of a run or comes after a `context.call`), +the SDK publishes an additional hidden request to apply the configuration. This request is not shown in the +workflow logs. + +Steps in a parallel group carry their own configuration and never need the extra request. +`context.call` and `context.invoke` accept their own flow control configuration too: + +* [context.call](/docs/workflow/steps/call) – lets you run external HTTP requests under a separate key, so you can throttle third‑party API calls independently of your workflow logic. +* [context.invoke](/docs/workflow/steps/invoke) – starts a new workflow run with its own flow control configuration. + +Inside a step, `context.flowControl` returns the flow control configuration applied to the current delivery. + ## Configuration You can configure flow control when starting a workflow run: @@ -113618,6 +113652,27 @@ const { workflowRunId } = await client.trigger({ }) ``` +### Configure Retries for a Single Step + +The retry configuration of a run applies to all of its steps. You can override it for a single `context.run` +step by passing `retries` and `retryDelay` as the third argument: + +```typescript Configure Retries for a Single Step +export const { POST } = serve(async (context) => { + await context.run( + "call flaky service", + () => callFlakyService(), + { + retries: 5, + retryDelay: "1000", + } + ); +}); +``` + +Any field left unset falls back to the configuration the run was triggered with. +The step-level options can be combined with [step-level flow control](/docs/workflow/features/flow-control#step-level-flow-control). + # Prevent Retries Source: https://upstash.com/docs/workflow/features/retries/prevent-retries @@ -114326,13 +114381,13 @@ These steps accept their own configuration options, allowing fine-grained contro If not specified, they fall back to their default values. For details, see: -* [context.call](/docs/workflow/basics/context/run) -* [context.invoke](/docs/workflow/basics/context/run) +* [context.call](/docs/workflow/steps/call) +* [context.invoke](/docs/workflow/steps/invoke) -Upstash Workflow does not support step level configuration. The configuration applies to all steps executed by a workflow run. - -If you want to specifically throttle a step, there is a workaround by splitting step to another workflow and using `context.invoke()`. +A single `context.run` step can override the run's flow control and retry configuration by passing +`flowControl`, `retries` or `retryDelay` as its third argument. See +[step-level flow control](/docs/workflow/features/flow-control#step-level-flow-control). # Handle Failed Runs @@ -117693,6 +117748,19 @@ The context object provides: Defaults to an empty array when no label was set. + + The flow control configuration QStash applied to the current request: `key`, `parallelism`, `rate` and `period`. + Reflects [step-level flow control](/docs/workflow/features/flow-control#step-level-flow-control) when the step sets its own. + + + + The retry limit QStash applied to the current request. + + + + The retry delay expression QStash applied to the current request. + + Deprecated. Use `labels` instead. When a run has multiple labels, this only returns the first one. @@ -118265,6 +118333,29 @@ It returns a `Promise`, so you can decide how steps execute: The business logic to run inside this step. + + Flow control and retry configuration for this step only. Fields left unset fall back to the + configuration the run was triggered with. + + See [step-level flow control](/docs/workflow/features/flow-control#step-level-flow-control) and + [retries](/docs/workflow/features/retries#configure-retries-for-a-single-step) for details. + + + + Flow control configuration with `key`, `rate`, `parallelism` and `period`, the same shape as in + [client.trigger](/docs/workflow/basics/client/trigger). Replaces the run's flow control for this step. + + + + How many times the step is retried when it fails. + + + + Delay between retries in milliseconds. Can use expressions like `"1000 * (1 + retried)"`. + + + + ## Response Each step can return a JSON-serializable value—anything from simple primitives to complex objects. diff --git a/workflow/features/flow-control.mdx b/workflow/features/flow-control.mdx index f6501df1..96406926 100644 --- a/workflow/features/flow-control.mdx +++ b/workflow/features/flow-control.mdx @@ -84,19 +84,53 @@ If no slots are available, new steps enter the **waitlist** until resources free Workflow parallelism waitlist - -Upstash Workflow does not support per-step level configuration. Meaning that you can attach a flow-control configuration -to the workflow run and all the steps will inherit to the same limits. -Following the analogy above, you cannot enforce parallelism limit on "green" steps natively. +## Step-level Flow Control + +The flow control configuration of a run applies to all of its steps. To limit a single `context.run` step more +strictly, pass a flow control configuration as the third argument of `context.run`: + +```typescript +export const { POST } = serve(async (context) => { + await context.run("prepare", () => { ... }); + + await context.run( + "charge customer", + () => chargeCustomer(), + { + flowControl: { key: "payment-provider", parallelism: 3 }, + } + ); + + await context.run("notify", () => { ... }); +}); +``` + +Only the `charge customer` step is bound by the `payment-provider` key. The other steps keep the configuration +the run was triggered with. Since the key is shared, at most 3 `charge customer` steps run at the same time +across all runs using that key, no matter how many runs are active. -The context.call and context.invoke steps are exception this to this rule and accept their own flow control configuration: +Following the analogy above, this lets you enforce a parallelism limit on the "green" steps only. -- [context.call](/workflow/basics/context/call) – lets you run external HTTP requests under a separate key, so you can throttle third‑party API calls independently of your workflow logic. -- [context.invoke](/workflow/basics/context/invoke) – starts a new workflow run with its own flow control configuration. This allows the invoked workflow to run under different limits than the parent workflow, giving you more precise control. +The step-level configuration replaces the run's flow control for that step as a whole: a step with its own +flow control configuration does not also count against the run's flow control key. -If you want to throttle a specific `context.run` step, the recommended approach is to **extract it into a separate workflow** and call it using `context.invoke()` with its own flow control configuration with a stricter limits. + +When the step with flow control is reached right after a `context.run`, `context.sleep` or +`context.sleepUntil` step, its configuration is attached to the request which delivers it, at no extra cost. +In other cases (for example, when it is the first step of a run or comes after a `context.call`), +the SDK publishes an additional hidden request to apply the configuration. This request is not shown in the +workflow logs. + +Steps in a parallel group carry their own configuration and never need the extra request. +`context.call` and `context.invoke` accept their own flow control configuration too: + +- [context.call](/workflow/steps/call) – lets you run external HTTP requests under a separate key, so you can throttle third‑party API calls independently of your workflow logic. +- [context.invoke](/workflow/steps/invoke) – starts a new workflow run with its own flow control configuration. + +Inside a step, `context.flowControl` returns the flow control configuration applied to the current delivery. + ## Configuration diff --git a/workflow/features/retries.mdx b/workflow/features/retries.mdx index af2a62f9..479a0126 100644 --- a/workflow/features/retries.mdx +++ b/workflow/features/retries.mdx @@ -69,3 +69,24 @@ const { workflowRunId } = await client.trigger({ retryDelay: "(1 + retried) * 1000" }) ``` + +### Configure Retries for a Single Step + +The retry configuration of a run applies to all of its steps. You can override it for a single `context.run` +step by passing `retries` and `retryDelay` as the third argument: + +```typescript Configure Retries for a Single Step +export const { POST } = serve(async (context) => { + await context.run( + "call flaky service", + () => callFlakyService(), + { + retries: 5, + retryDelay: "1000", + } + ); +}); +``` + +Any field left unset falls back to the configuration the run was triggered with. +The step-level options can be combined with [step-level flow control](/workflow/features/flow-control#step-level-flow-control). diff --git a/workflow/howto/configure.mdx b/workflow/howto/configure.mdx index 464c8ad9..7234d7d1 100644 --- a/workflow/howto/configure.mdx +++ b/workflow/howto/configure.mdx @@ -32,11 +32,11 @@ These steps accept their own configuration options, allowing fine-grained contro If not specified, they fall back to their default values. For details, see: -- [context.call](/workflow/basics/context/run) -- [context.invoke](/workflow/basics/context/run) +- [context.call](/workflow/steps/call) +- [context.invoke](/workflow/steps/invoke) -Upstash Workflow does not support step level configuration. The configuration applies to all steps executed by a workflow run. - -If you want to specifically throttle a step, there is a workaround by splitting step to another workflow and using `context.invoke()`. - \ No newline at end of file +A single `context.run` step can override the run's flow control and retry configuration by passing +`flowControl`, `retries` or `retryDelay` as its third argument. See +[step-level flow control](/workflow/features/flow-control#step-level-flow-control). + diff --git a/workflow/steps.mdx b/workflow/steps.mdx index f113ef0a..32711543 100644 --- a/workflow/steps.mdx +++ b/workflow/steps.mdx @@ -72,6 +72,19 @@ The context object provides: Defaults to an empty array when no label was set. + + The flow control configuration QStash applied to the current request: `key`, `parallelism`, `rate` and `period`. + Reflects [step-level flow control](/workflow/features/flow-control#step-level-flow-control) when the step sets its own. + + + + The retry limit QStash applied to the current request. + + + + The retry delay expression QStash applied to the current request. + + Deprecated. Use `labels` instead. When a run has multiple labels, this only returns the first one. diff --git a/workflow/steps/run.mdx b/workflow/steps/run.mdx index 1f99fbfe..2e5f7e3d 100644 --- a/workflow/steps/run.mdx +++ b/workflow/steps/run.mdx @@ -18,6 +18,29 @@ It returns a `Promise`, so you can decide how steps execute: The business logic to run inside this step. + + Flow control and retry configuration for this step only. Fields left unset fall back to the + configuration the run was triggered with. + + See [step-level flow control](/workflow/features/flow-control#step-level-flow-control) and + [retries](/workflow/features/retries#configure-retries-for-a-single-step) for details. + + + + Flow control configuration with `key`, `rate`, `parallelism` and `period`, the same shape as in + [client.trigger](/workflow/basics/client/trigger). Replaces the run's flow control for this step. + + + + How many times the step is retried when it fails. + + + + Delay between retries in milliseconds. Can use expressions like `"1000 * (1 + retried)"`. + + + + ## Response Each step can return a JSON-serializable value—anything from simple primitives to complex objects.