Skip to content

Commit 89d1446

Browse files
committed
readme
1 parent 17b83fa commit 89d1446

File tree

1 file changed

+40
-18
lines changed

1 file changed

+40
-18
lines changed

README.md

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
# Convex Workflow
1+
# Convex Durable Workflows
22

33
[![npm version](https://badge.fury.io/js/@convex-dev%2Fworkflow.svg?)](https://badge.fury.io/js/@convex-dev%2Fworkflow)
44

55
<!-- START: Include on https://convex.dev/components -->
66

7+
The Workflow component enables you
8+
79
Have you ever wanted to run a series of functions reliably and durably, where
810
each can have its own retry behavior, the overall workflow will survive server
911
restarts, and you can have long-running workflows spanning months that can be
1012
canceled? Do you want to observe the status of a workflow reactively, as well as
1113
the results written from each step?
1214

13-
And do you want to do this with code, instead of a DSL?
15+
And do you want to do this with code, instead of a static configuration?
1416

1517
Welcome to the world of Convex workflows.
1618

@@ -32,23 +34,39 @@ import { components } from "./_generated/api";
3234

3335
export const workflow = new WorkflowManager(components.workflow);
3436

35-
export const exampleWorkflow = workflow.define({
37+
export const userOnboarding = workflow.define({
3638
args: {
37-
storageId: v.id("_storage"),
39+
userId: v.id("users"),
3840
},
39-
handler: async (step, args): Promise<number[]> => {
40-
const transcription = await step.runAction(
41-
internal.index.computeTranscription,
41+
handler: async (ctx, args): Promise<void> => {
42+
const status = await ctx.runMutation(
43+
internal.emails.sendVerificationEmail,
4244
{ storageId: args.storageId },
4345
);
4446

45-
const embedding = await step.runAction(
46-
internal.index.computeEmbedding,
47-
{ transcription },
48-
// Run this a month after the transcription is computed.
49-
{ runAfter: 30 * 24 * 60 * 60 * 1000 },
47+
if (status === "needsVerification") {
48+
// Waits until verification is completed asynchronously.
49+
await ctx.awaitEvent({ name: "verificationEmail" });
50+
}
51+
const result = await ctx.runAction(
52+
internal.llm.generateCustomContent,
53+
{ userId: args.userId },
54+
// Retry this on transient errors with the default retry policy.
55+
{ retry: true },
56+
);
57+
if (result.needsHumanInput) {
58+
// Run a whole workflow as a single step.
59+
await ctx.runWorkflow(internal.llm.refineContentWorkflow, {
60+
userId: args.userId,
61+
});
62+
}
63+
64+
await ctx.runMutation(
65+
internal.emails.sendFollowUpEmailMaybe,
66+
{ userId: args.userId },
67+
// Runs one day after the previous step.
68+
{ runAfter: 24 * 60 * 60 * 1000 },
5069
);
51-
return embedding;
5270
},
5371
});
5472
```
@@ -97,17 +115,19 @@ is designed to feel like a Convex action but with a few restrictions:
97115

98116
1. The workflow runs in the background, so it can't return a value.
99117
2. The workflow must be _deterministic_, so it should implement most of its logic
100-
by calling out to other Convex functions. We will be lifting some of these
101-
restrictions over time by implementing `Math.random()`, `Date.now()`, and
102-
`fetch` within our workflow environment.
118+
by calling out to other Convex functions. We restrict access to some
119+
non-deterministic functions like `Math.random()` and `fetch`. Others we
120+
patch, such as `console` for logging and `Date` for time.
103121

104122
Note: To help avoid type cycles, always annotate the return type of the `handler`
105123
with the return type of the workflow.
106124

107125
```ts
108126
export const exampleWorkflow = workflow.define({
109127
args: { name: v.string() },
128+
returns: v.string(),
110129
handler: async (step, args): Promise<string> => {
130+
// ^ Specify the return type of the handler
111131
const queryResult = await step.runQuery(
112132
internal.example.exampleQuery,
113133
args,
@@ -283,11 +303,13 @@ export const exampleWorkflow = workflow.define({
283303
});
284304
```
285305

286-
### Specifying how many workflows can run in parallel
306+
### Specifying step parallelism
287307

288308
You can specify how many steps can run in parallel by setting the
289309
`maxParallelism` workpool option. It has a reasonable default.
290-
On the free tier, you should not exceed 20.
310+
On the free tier, you should not exceed 20, otherwise your other scheduled
311+
functions may become delayed while competing for available functions with your
312+
workflow steps.
291313
On a Pro account, you should not exceed 100 across all your workflows and workpools.
292314
If you want to do a lot of work in parallel, you should employ batching, where
293315
each workflow operates on a batch of work, e.g. scraping a list of links instead

0 commit comments

Comments
 (0)