Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions src/content/docs/workflows/build/sleeping-and-retrying.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,6 @@ let someState = await step.do(
);
```

## Access step context

Workflow step callbacks receive a context object containing the current attempt number (1-indexed). This allows you to access which retry attempt is currently executing.

```ts
await step.do("my-step", async (ctx) => {
// ctx.attempt is 1 on first try, 2 on first retry, etc.
console.log(`Attempt ${ctx.attempt}`);
});
```

## Force a Workflow instance to fail

You can also force a Workflow instance to fail and _not_ retry by throwing a `NonRetryableError` from within the step.
Expand Down
107 changes: 107 additions & 0 deletions src/content/docs/workflows/build/step-context.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
title: Step context
pcx_content_type: concept
sidebar:
order: 5
---

Every `step.do` callback receives a **context object** (`WorkflowStepContext`) as its first argument. The context gives your step code runtime information about the step itself, the current retry attempt, and the resolved configuration for that step.

## WorkflowStepContext

```ts
type WorkflowStepContext = {
step: {
name: string;
count: number;
};
attempt: number;
config: WorkflowStepConfig;
};
```

### Properties

| Property | Type | Description |
| ------------ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `step.name` | `string` | The name you passed to `step.do`. |
| `step.count` | `number` | How many times `step.do` has been called with this name so far in the current Workflow run. Starts at `1` for the first call with a given name. |
| `attempt` | `number` | The current attempt number (1-indexed). `1` on the first try, `2` on the first retry, and so on. |
| `config` | [`WorkflowStepConfig`](/workflows/build/workers-api/#workflowstepconfig) | The resolved retry and timeout configuration for this step, including any defaults applied by the runtime. |

## Access the context

Pass a parameter to your `step.do` callback to receive the context object:

```ts
await step.do("my-step", async (ctx) => {
console.log(ctx.step.name); // "my-step"
console.log(ctx.step.count); // 1
console.log(ctx.attempt); // 1 on first try, 2 on first retry, etc.
console.log(ctx.config); // { retries: { limit: 5, ... }, timeout: "10 minutes" }
});
```

The context is also available when you pass a custom `WorkflowStepConfig`:

```ts
await step.do(
"call an API",
{
retries: {
limit: 10,
delay: "10 seconds",
backoff: "exponential",
},
timeout: "30 minutes",
},
async (ctx) => {
console.log(ctx.config.retries.limit); // 10
console.log(ctx.config.timeout); // "30 minutes"
},
);
```

## Examples

### Adjust behavior based on retry attempt

Use `ctx.attempt` to change how your step behaves on retries. For example, you might use a fallback endpoint after a certain number of retries:

```ts
await step.do(
"fetch data",
{ retries: { limit: 5, delay: "5 seconds", backoff: "linear" } },
async (ctx) => {
const url =
ctx.attempt <= 3
? "https://api.example.com/primary"
: "https://api.example.com/fallback";

const response = await fetch(url);
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
return await response.json();
},
);
```

### Log step metadata for observability

Use `ctx.step` to add structured metadata to your logs:

```ts
await step.do("process-order", async (ctx) => {
console.log(
JSON.stringify({
step: ctx.step.name,
stepCount: ctx.step.count,
attempt: ctx.attempt,
retryLimit: ctx.config.retries?.limit,
}),
);

// Your step logic here
});
```
27 changes: 24 additions & 3 deletions src/content/docs/workflows/build/workers-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ Refer to the [events and parameters](/workflows/build/events-and-parameters/) do
### step

{/* prettier-ignore */}
- <code>step.do(name: string, callback: (): RpcSerializable): Promise&lt;T&gt;</code>
- <code>step.do(name: string, config?: WorkflowStepConfig, callback: ():
- <code>step.do(name: string, callback: (ctx: WorkflowStepContext): RpcSerializable): Promise&lt;T&gt;</code>
- <code>step.do(name: string, config?: WorkflowStepConfig, callback: (ctx: WorkflowStepContext):
RpcSerializable): Promise&lt;T&gt;</code>
- `name` - the name of the step, up to 256 characters.
- `config` (optional) - an optional `WorkflowStepConfig` for configuring [step specific retry behaviour](/workflows/build/sleeping-and-retrying/).
- `callback` - an asynchronous function that optionally returns serializable state for the Workflow to persist. In JavaScript Workflows, this includes a fresh, unlocked `ReadableStream<Uint8Array>` for large binary output.
- `callback` - an asynchronous function that receives a [`WorkflowStepContext`](/workflows/build/step-context/) and optionally returns serializable state for the Workflow to persist. In JavaScript Workflows, this includes a fresh, unlocked `ReadableStream<Uint8Array>` for large binary output.

:::note[Returning state]

Expand Down Expand Up @@ -193,6 +193,27 @@ export type WorkflowStepConfig = {

Refer to the [documentation on sleeping and retrying](/workflows/build/sleeping-and-retrying/) to learn more about how Workflows are retried.

## WorkflowStepContext

```ts
export type WorkflowStepContext = {
step: {
name: string;
count: number;
};
attempt: number;
config: WorkflowStepConfig;
};
```

- The `WorkflowStepContext` is passed as the first argument to the `step.do` callback function. It provides runtime information about the current step.
- `step.name` - the name of the step as passed to `step.do`.
- `step.count` - how many times `step.do` has been called with this name in the current Workflow run (1-indexed).
- `attempt` - the current attempt number (1-indexed). `1` on the first try, `2` on the first retry, and so on.
- `config` - the resolved `WorkflowStepConfig` for this step, including any defaults applied by the runtime.

Refer to the [step context documentation](/workflows/build/step-context/) for usage examples.

## Workflow step limits

Each workflow on Workers Paid supports 10,000 steps by default. You can increase this up to 25,000 steps by configuring `steps` within the `limits` property of your Workflow definition in your Wrangler configuration:
Expand Down
12 changes: 12 additions & 0 deletions src/content/docs/workflows/python/python-workers-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,25 @@ class DemoWorkflowClass(WorkflowEntrypoint):

### Access step context (`ctx`)

If you define a `ctx` parameter, the [step context](/workflows/build/step-context/) is injected into that argument. The context is a dictionary with the following keys:

| Key | Type | Description |
| --------- | ------ | ------------------------------------------------------------------------------------------------------ |
| `step` | `dict` | Contains `name` (the step name) and `count` (how many times `step.do` has been called with this name). |
| `attempt` | `int` | The current attempt number (1-indexed). |
| `config` | `dict` | The resolved retry and timeout configuration for this step. |

```python
from workers import WorkflowEntrypoint

class CtxWorkflow(WorkflowEntrypoint):
async def run(self, event, step):
@step.do()
async def read_context(ctx):
print(ctx["step"]["name"]) # step name
print(ctx["step"]["count"]) # step count
print(ctx["attempt"]) # attempt number
print(ctx["config"]) # resolved step config
return ctx["attempt"]

return await read_context()
Expand Down
Loading