Skip to content

Commit e213140

Browse files
committed
update rules:
1 parent 2685a76 commit e213140

File tree

1 file changed

+54
-1
lines changed

1 file changed

+54
-1
lines changed

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

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ can be applied multiple times without changing the result beyond the initial app
1717
As an example, let us assume you have a Workflow that charges your customers, and you really do not want to charge them twice by accident. Before charging them, you should
1818
check if they were already charged:
1919

20+
<TypeScriptExample filename="index.ts">
2021
```ts
2122
export class MyWorkflow extends WorkflowEntrypoint {
2223
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
@@ -52,6 +53,7 @@ export class MyWorkflow extends WorkflowEntrypoint {
5253
}
5354
}
5455
```
56+
</TypeScriptExample>
5557

5658
:::note
5759

@@ -67,6 +69,7 @@ You can also think of it as a transaction, or a unit of work.
6769

6870
- ✅ Minimize the number of API/binding calls per step (unless you need multiple calls to prove idempotency).
6971

72+
<TypeScriptExample filename="index.ts">
7073
```ts
7174
export class MyWorkflow extends WorkflowEntrypoint {
7275
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
@@ -84,6 +87,7 @@ export class MyWorkflow extends WorkflowEntrypoint {
8487
}
8588
}
8689
```
90+
</TypeScriptExample>
8791

8892
Otherwise, your entire Workflow might not be as durable as you might think, and you may encounter some undefined behaviour. You can avoid them by following the rules below:
8993

@@ -92,6 +96,7 @@ Otherwise, your entire Workflow might not be as durable as you might think, and
9296
- 🔴 Do not make too many service calls in the same step (unless you need it to prove idempotency).
9397
- 🔴 Do not do too much CPU-intensive work inside a single step - sometimes the engine may have to restart, and it will start over from the beginning of that step.
9498

99+
<TypeScriptExample filename="index.ts">
95100
```ts
96101
export class MyWorkflow extends WorkflowEntrypoint {
97102
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
@@ -105,13 +110,15 @@ export class MyWorkflow extends WorkflowEntrypoint {
105110
}
106111
}
107112
```
113+
</TypeScriptExample>
108114

109115
### Do not rely on state outside of a step
110116

111117
Workflows may hibernate and lose all in-memory state. This will happen when engine detects that there is no pending work and can hibernate until it needs to wake-up (because of a sleep, retry, or event).
112118

113119
This means that you should not store state outside of a step:
114120

121+
<TypeScriptExample filename="index.ts">
115122
```ts
116123
function getRandomInt(min, max) {
117124
const minCeiled = Math.ceil(min);
@@ -152,9 +159,11 @@ export class MyWorkflow extends WorkflowEntrypoint {
152159
}
153160
}
154161
```
162+
</TypeScriptExample>
155163

156164
Instead, you should build top-level state exclusively comprised of `step.do` returns:
157165

166+
<TypeScriptExample filename="index.ts">
158167
```ts
159168
function getRandomInt(min, max) {
160169
const minCeiled = Math.ceil(min);
@@ -192,11 +201,13 @@ export class MyWorkflow extends WorkflowEntrypoint {
192201
}
193202
}
194203
```
204+
</TypeScriptExample>
195205

196206
### Do not mutate your incoming events
197207

198208
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.
199209

210+
<TypeScriptExample filename="index.ts">
200211
```ts
201212
interface MyEvent {
202213
user: string;
@@ -224,12 +235,13 @@ export class MyWorkflow extends WorkflowEntrypoint {
224235
// Will always be the same if this step is retried
225236
})
226237
```
238+
</TypeScriptExample>
227239
228240
### Name steps deterministically
229241
230242
Steps should be named deterministically (even if dynamic). 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.
231243
232-
244+
<TypeScriptExample filename="index.ts">
233245
```ts
234246
export class MyWorkflow extends WorkflowEntrypoint {
235247
async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
@@ -261,3 +273,44 @@ export class MyWorkflow extends WorkflowEntrypoint {
261273
})
262274
}
263275
```
276+
</TypeScriptExample>
277+
278+
### Instance IDs are unique
279+
280+
Workflow [instance IDs](/workflows/build/workers-api/#workflowinstance) are unique per Workflow. The ID is the 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.
281+
282+
It would also present a problem if you wanted to run multiple different Workflow instances with different [input parameters](/workflows/build/events-and-parameters/) for the same user ID, as you would immediately need to determine a new ID mapping.
283+
284+
If you need to associate multiple instances with a specific user, merchant or other "customer" ID in your system, consider using a composite ID or using randomly generated IDs and storing the mapping in a database like [D1](/d1/).
285+
286+
<TypeScriptExample filename="index.ts">
287+
```ts
288+
// This is in the same file as your Workflow definition
289+
export default {
290+
async fetch(req: Request, env: Env): Promise<Response> {
291+
// 🔴 Bad: Use an ID that isn't unique across future Workflow invocations
292+
let userId = getUserId(req) // Returns the userId
293+
let instance = await env.MY_WORKFLOW.create({
294+
id: userId,
295+
params: payload
296+
});
297+
298+
// ✅ Good: use a composite ID or an
299+
let instanceId = getTransactionId() // e.g. assuming transaction IDs are unique
300+
// or: compose a composite ID and store it in your database
301+
// so that you can track all instances associated with a specific user or merchant.
302+
let instanceId = `${getUserId(request}-${await crypto.randomUUID().slice(0, 6)}`
303+
let { result } = await addNewInstance(userId, instanceId)
304+
let instance = await env.MY_WORKFLOW.create({
305+
id: userId,
306+
params: payload
307+
});
308+
309+
return Response.json({
310+
id: instance.id,
311+
details: await instance.status(),
312+
});
313+
},
314+
};
315+
```
316+
</TypeScriptExample>

0 commit comments

Comments
 (0)