@convex-dev/workpool 0.1.2 → 0.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +155 -17
  2. package/dist/commonjs/client/index.d.ts +123 -35
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +122 -15
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/commonjs/client/utils.d.ts +16 -0
  7. package/dist/commonjs/client/utils.d.ts.map +1 -0
  8. package/dist/commonjs/client/utils.js +2 -0
  9. package/dist/commonjs/client/utils.js.map +1 -0
  10. package/dist/commonjs/component/complete.d.ts +89 -0
  11. package/dist/commonjs/component/complete.d.ts.map +1 -0
  12. package/dist/commonjs/component/complete.js +80 -0
  13. package/dist/commonjs/component/complete.js.map +1 -0
  14. package/dist/commonjs/component/convex.config.d.ts.map +1 -1
  15. package/dist/commonjs/component/convex.config.js +0 -2
  16. package/dist/commonjs/component/convex.config.js.map +1 -1
  17. package/dist/commonjs/component/kick.d.ts +9 -0
  18. package/dist/commonjs/component/kick.d.ts.map +1 -0
  19. package/dist/commonjs/component/kick.js +97 -0
  20. package/dist/commonjs/component/kick.js.map +1 -0
  21. package/dist/commonjs/component/lib.d.ts +23 -32
  22. package/dist/commonjs/component/lib.d.ts.map +1 -1
  23. package/dist/commonjs/component/lib.js +91 -563
  24. package/dist/commonjs/component/lib.js.map +1 -1
  25. package/dist/commonjs/component/logging.d.ts +5 -3
  26. package/dist/commonjs/component/logging.d.ts.map +1 -1
  27. package/dist/commonjs/component/logging.js +13 -2
  28. package/dist/commonjs/component/logging.js.map +1 -1
  29. package/dist/commonjs/component/loop.d.ts +13 -0
  30. package/dist/commonjs/component/loop.d.ts.map +1 -0
  31. package/dist/commonjs/component/loop.js +482 -0
  32. package/dist/commonjs/component/loop.js.map +1 -0
  33. package/dist/commonjs/component/recovery.d.ts +24 -0
  34. package/dist/commonjs/component/recovery.d.ts.map +1 -0
  35. package/dist/commonjs/component/recovery.js +94 -0
  36. package/dist/commonjs/component/recovery.js.map +1 -0
  37. package/dist/commonjs/component/schema.d.ts +167 -93
  38. package/dist/commonjs/component/schema.d.ts.map +1 -1
  39. package/dist/commonjs/component/schema.js +56 -65
  40. package/dist/commonjs/component/schema.js.map +1 -1
  41. package/dist/commonjs/component/shared.d.ts +138 -0
  42. package/dist/commonjs/component/shared.d.ts.map +1 -0
  43. package/dist/commonjs/component/shared.js +77 -0
  44. package/dist/commonjs/component/shared.js.map +1 -0
  45. package/dist/commonjs/component/stats.d.ts +6 -3
  46. package/dist/commonjs/component/stats.d.ts.map +1 -1
  47. package/dist/commonjs/component/stats.js +23 -4
  48. package/dist/commonjs/component/stats.js.map +1 -1
  49. package/dist/commonjs/component/worker.d.ts +15 -0
  50. package/dist/commonjs/component/worker.d.ts.map +1 -0
  51. package/dist/commonjs/component/worker.js +73 -0
  52. package/dist/commonjs/component/worker.js.map +1 -0
  53. package/dist/esm/client/index.d.ts +123 -35
  54. package/dist/esm/client/index.d.ts.map +1 -1
  55. package/dist/esm/client/index.js +122 -15
  56. package/dist/esm/client/index.js.map +1 -1
  57. package/dist/esm/client/utils.d.ts +16 -0
  58. package/dist/esm/client/utils.d.ts.map +1 -0
  59. package/dist/esm/client/utils.js +2 -0
  60. package/dist/esm/client/utils.js.map +1 -0
  61. package/dist/esm/component/complete.d.ts +89 -0
  62. package/dist/esm/component/complete.d.ts.map +1 -0
  63. package/dist/esm/component/complete.js +80 -0
  64. package/dist/esm/component/complete.js.map +1 -0
  65. package/dist/esm/component/convex.config.d.ts.map +1 -1
  66. package/dist/esm/component/convex.config.js +0 -2
  67. package/dist/esm/component/convex.config.js.map +1 -1
  68. package/dist/esm/component/kick.d.ts +9 -0
  69. package/dist/esm/component/kick.d.ts.map +1 -0
  70. package/dist/esm/component/kick.js +97 -0
  71. package/dist/esm/component/kick.js.map +1 -0
  72. package/dist/esm/component/lib.d.ts +23 -32
  73. package/dist/esm/component/lib.d.ts.map +1 -1
  74. package/dist/esm/component/lib.js +91 -563
  75. package/dist/esm/component/lib.js.map +1 -1
  76. package/dist/esm/component/logging.d.ts +5 -3
  77. package/dist/esm/component/logging.d.ts.map +1 -1
  78. package/dist/esm/component/logging.js +13 -2
  79. package/dist/esm/component/logging.js.map +1 -1
  80. package/dist/esm/component/loop.d.ts +13 -0
  81. package/dist/esm/component/loop.d.ts.map +1 -0
  82. package/dist/esm/component/loop.js +482 -0
  83. package/dist/esm/component/loop.js.map +1 -0
  84. package/dist/esm/component/recovery.d.ts +24 -0
  85. package/dist/esm/component/recovery.d.ts.map +1 -0
  86. package/dist/esm/component/recovery.js +94 -0
  87. package/dist/esm/component/recovery.js.map +1 -0
  88. package/dist/esm/component/schema.d.ts +167 -93
  89. package/dist/esm/component/schema.d.ts.map +1 -1
  90. package/dist/esm/component/schema.js +56 -65
  91. package/dist/esm/component/schema.js.map +1 -1
  92. package/dist/esm/component/shared.d.ts +138 -0
  93. package/dist/esm/component/shared.d.ts.map +1 -0
  94. package/dist/esm/component/shared.js +77 -0
  95. package/dist/esm/component/shared.js.map +1 -0
  96. package/dist/esm/component/stats.d.ts +6 -3
  97. package/dist/esm/component/stats.d.ts.map +1 -1
  98. package/dist/esm/component/stats.js +23 -4
  99. package/dist/esm/component/stats.js.map +1 -1
  100. package/dist/esm/component/worker.d.ts +15 -0
  101. package/dist/esm/component/worker.d.ts.map +1 -0
  102. package/dist/esm/component/worker.js +73 -0
  103. package/dist/esm/component/worker.js.map +1 -0
  104. package/package.json +6 -5
  105. package/src/client/index.ts +232 -68
  106. package/src/client/utils.ts +45 -0
  107. package/src/component/README.md +73 -0
  108. package/src/component/_generated/api.d.ts +38 -66
  109. package/src/component/complete.test.ts +508 -0
  110. package/src/component/complete.ts +98 -0
  111. package/src/component/convex.config.ts +0 -3
  112. package/src/component/kick.test.ts +285 -0
  113. package/src/component/kick.ts +118 -0
  114. package/src/component/lib.test.ts +448 -0
  115. package/src/component/lib.ts +105 -667
  116. package/src/component/logging.ts +24 -12
  117. package/src/component/loop.test.ts +1204 -0
  118. package/src/component/loop.ts +637 -0
  119. package/src/component/recovery.test.ts +541 -0
  120. package/src/component/recovery.ts +96 -0
  121. package/src/component/schema.ts +61 -77
  122. package/src/component/setup.test.ts +5 -0
  123. package/src/component/shared.ts +141 -0
  124. package/src/component/stats.ts +26 -8
  125. package/src/component/worker.ts +81 -0
package/README.md CHANGED
@@ -6,6 +6,16 @@
6
6
 
7
7
  This Convex component pools actions and mutations to restrict parallel requests.
8
8
 
9
+ - Configure multiple pools with different parallelism.
10
+ - Retry failed actions (with backoff and jitter) for
11
+ [idempotent actions](#idempotency), fully configurable (respecting parallelism).
12
+ - An `onComplete` callback so you can build durable, reliable workflows. Called
13
+ when the work is finished, whether it succeeded, failed, or was canceled.
14
+
15
+ ## Use cases
16
+
17
+ ### Separating and throttling async workloads
18
+
9
19
  Suppose you have some important async work, like sending verification emails,
10
20
  and some less important async work, like scraping data from an API. If all of
11
21
  these are scheduled with `ctx.scheduler.runAfter`, they'll compete with each
@@ -16,16 +26,17 @@ To resolve this problem, you can separate work into different pools.
16
26
 
17
27
  ```ts
18
28
  const emailPool = new Workpool(components.emailWorkpool, {
19
- maxParallelism: 5,
29
+ maxParallelism: 10,
20
30
  });
21
31
  const scrapePool = new Workpool(components.scrapeWorkpool, {
22
- maxParallelism: 1,
32
+ maxParallelism: 5,
23
33
  });
24
34
 
25
- export const signUp = mutation({
35
+ export const userSignUp = mutation({
36
+ args: {...},
26
37
  handler: async (ctx, args) => {
27
38
  const userId = await ctx.db.insert("users", args);
28
- await emailPool.enqueueAction(internal.auth.sendEmailVerification, {
39
+ await emailPool.enqueueAction(ctx, internal.auth.sendEmailVerification, {
29
40
  userId,
30
41
  });
31
42
  },
@@ -34,12 +45,101 @@ export const signUp = mutation({
34
45
  export const downloadLatestWeather = mutation({
35
46
  handler: async (ctx, args) => {
36
47
  for (const city of allCities) {
37
- await scrapePool.enqueueAction(internal.weather.scrape, { city });
48
+ await scrapePool.enqueueAction(ctx, internal.weather.scrape, { city });
38
49
  }
39
50
  },
40
51
  });
41
52
  ```
42
53
 
54
+ ### Durable, reliable workflows meet workpool
55
+
56
+ #### Retry management
57
+
58
+ Imagine that the payment processor is a 3rd party API, and they temporarily have an
59
+ outage. Now imagine you implement your own action retrying logic for your busy app.
60
+ You'll find very quickly that your entire backend is overwhelmed with retrying actions.
61
+ This could bog down live traffic with background work, and/or cause you to exceed
62
+ rate limits with the payment provider.
63
+
64
+ Creating an upper bound on how much work will be done in parallel is a good way to
65
+ mitigate this risk. Actions that are currently backing off awaiting retry will not tie
66
+ up a thread in the workpool.
67
+
68
+ #### Completion handling
69
+
70
+ By handing off asynchronous work, it will be guaranteed to run, and with retries
71
+ you can account for temporary failures, while avoiding a "stampeding herd"
72
+ during third party outages.
73
+
74
+ With the `onComplete` callback, you can define how to proceed after each step,
75
+ whether that enqueues another job to the workpool, updates the database, etc.
76
+ It will always be called, whether the work was successful, failed, or was
77
+ canceled. See [below](#options-for-enqueueing-work) for more info.
78
+
79
+ Example:
80
+
81
+ ```ts
82
+ const pool = new Workpool(components.emailWorkpool, {
83
+ retryActionsByDefault: true,
84
+ defaultRetryBehavior: { maxAttempts: 3, initialBackoffMs: 1000, base: 2 },
85
+ });
86
+
87
+ //...
88
+
89
+ await pool.enqueueAction(ctx, internal.email.send, args, {
90
+ onComplete: internal.email.emailSent,
91
+ context: { emailType, userId },
92
+ retry: false, // don't retry this action, as we can't guarantee idempotency.
93
+ });
94
+
95
+ export const emailSent = internalMutation({
96
+ args: {
97
+ workId: workIdValidator,
98
+ context: v.object({ emailType: v.string(), userId: v.id("users") }),
99
+ result: runResultValidator,
100
+ },
101
+ handler: async (ctx, args) => {
102
+ if (args.result.kind === "canceled") return;
103
+ await ctx.db.insert("userEmailLog", {
104
+ userId: args.context.userId,
105
+ emailType: args.context.emailType,
106
+ result: args.result.kind === "success" ? args.result.returnValue : null,
107
+ error: args.result.kind === "failed" ? args.result.error : null,
108
+ });
109
+ if (args.result.kind === "failed") {
110
+ await pool.enqueueAction(ctx, internal.email.checkResendStatus, args, {
111
+ retry: { maxAttempts: 10, initialBackoffMs: 250, base: 2 }, // custom
112
+ onComplete: internal.email.handleEmailStatus,
113
+ context: args.context,
114
+ });
115
+ }
116
+ },
117
+ });
118
+ ```
119
+
120
+ #### Idempotency?
121
+
122
+ Idempotent actions are actions that can be run multiple times safely. This typically
123
+ means they don't cause any side effects that would be a problem if executed twice or more.
124
+
125
+ As an example of an unsafe, non-idempotent action, consider an action that charges
126
+ a user's credit card without providing a unique transaction id to the payment
127
+ processor. The first time the action is run, imagine that the API call succeeds to the
128
+ payment provider, but then the action throws an exception before the transaction is marked
129
+ finished in our Convex database. If the action is run twice, the user may be
130
+ double charged for the transaction!
131
+
132
+ If we alter this action to provide a consistent transaction id to the payment provider, they
133
+ can simply NOOP the second payment attempt. The this makes the action idempotent, and
134
+ it can safely be retried.
135
+
136
+ If you're creating complex workflows with many steps involving 3rd party APIs:
137
+
138
+ 1. You should ensure that each step is an idempotent Convex action.
139
+ 2. You should use this component to manage these actions so it all just works!
140
+
141
+ ### Optimize OCC errors
142
+
43
143
  With limited parallelism, you can reduce
44
144
  [OCC errors](https://docs.convex.dev/error#1)
45
145
  from mutations that read and write the same data.
@@ -56,7 +156,7 @@ const counterPool = new Workpool(components.counterWorkpool, {
56
156
  export const doSomethingAndCount = action({
57
157
  handler: async (ctx) => {
58
158
  const doSomething = await fetch("https://example.com");
59
- await counterPool.enqueueMutation(internal.counter.increment, {});
159
+ await counterPool.enqueueMutation(ctx, internal.counter.increment, {});
60
160
  },
61
161
  });
62
162
 
@@ -120,9 +220,9 @@ Then you have the following interface on `pool`:
120
220
 
121
221
  ```ts
122
222
  // Schedule functions to run in the background.
123
- const id = await pool.enqueueMutation(internal.foo.bar, args);
223
+ const id = await pool.enqueueMutation(ctx, internal.foo.bar, args);
124
224
  // Or for an action:
125
- const id = await pool.enqueueAction(internal.foo.baz, args);
225
+ const id = await pool.enqueueAction(ctx, internal.foo.baz, args);
126
226
 
127
227
  // Is it done yet? Did it succeed or fail?
128
228
  const status = await pool.status(id);
@@ -133,6 +233,41 @@ await pool.cancel(id);
133
233
 
134
234
  See more example usage in [example.ts](./example/convex/example.ts).
135
235
 
236
+ ### Configuring the Workpool
237
+
238
+ Check out the [docstrings](./src/client/index.ts), but notable options include:
239
+
240
+ - `maxParallelism`: How many actions/mutations can run at once within this pool.
241
+ - `retryActionsByDefault`: Whether to retry actions that fail by default.
242
+ - `defaultRetryBehavior`: The default retry behavior for enqueued actions.
243
+
244
+ You can override the retry behavior per-call with the `retry` option.
245
+
246
+ ### Options for enqueueing work
247
+
248
+ See the [docstrings](./src/client/index.ts) for more details, but notable options include:
249
+
250
+ - `retry`: Whether to retry the action if it fails. Overrides defaults.
251
+ If it's set to `true`, it will use the `defaultRetryBehavior`.
252
+ If it's set to a custom config, it will use that (and do retries).
253
+ - `onComplete`: A mutation to run after the function finishes.
254
+ - `context`: Any data you want to pass to the `onComplete` mutation.
255
+ - `runAt` and `runAfter`: Similar to `ctx.scheduler.run*`, allows you to
256
+ schedule the work to run later. By default it's immediate.
257
+
258
+ ### Retry behavior
259
+
260
+ The retry options work like this:
261
+
262
+ - The first request runs as it's scheduled.
263
+ - If it fails, it will wait _around_ `initialBackoffMs` and then try again.
264
+ - Each subsequent retry waits `initialBackoffMs * base^<retryNumber - 1>`.
265
+ - The standard base is 2.
266
+ - The actual wait time uses "jitter" to avoid all retries happening at once,
267
+ if they all failed at the same time.
268
+
269
+ You can override the retry behavior per-call with the `retry` option.
270
+
136
271
  ## Optimizations with and without Workpool
137
272
 
138
273
  The benefit of Workpool is that it won't fall over if there are many jobs
@@ -168,16 +303,19 @@ You can read the status of a function by calling `pool.status(id)`.
168
303
 
169
304
  The status will be one of:
170
305
 
171
- - `{ kind: "pending" }`: The function has not started yet.
172
- - `{ kind: "inProgress" }`: The function is currently running.
173
- - `{ kind: "completed"; completionStatus: CompletionStatus }`: The function has
174
- finished.
306
+ - `{ kind: "pending"; previousAttempts: number }`: The function has not started yet.
307
+ - `{ kind: "running"; previousAttempts: number }`: The function is currently running.
308
+ - `{ kind: "finished" }`: The function has succeeded, failed, or been canceled.
309
+
310
+ ## Canceling work
175
311
 
176
- The `CompletionStatus` type is one of:
312
+ You can cancel work by calling `pool.cancel(id)` or all of them with
313
+ `pool.cancelAll()`.
177
314
 
178
- - `"success"`: The function completed successfully.
179
- - `"error"`: The function threw an error.
180
- - `"canceled"`: The function was canceled.
181
- - `"timeout"`: The function timed out.
315
+ This will avoid starting or retrying, but will not stop in-progress work.
182
316
 
183
317
  <!-- END: Include on https://convex.dev/components -->
318
+
319
+ ```
320
+
321
+ ```
@@ -1,53 +1,141 @@
1
- import { DefaultFunctionArgs, Expand, FunctionReference, FunctionVisibility, GenericDataModel, GenericMutationCtx, GenericQueryCtx } from "convex/server";
2
- import { GenericId } from "convex/values";
3
- import { api } from "../component/_generated/api";
4
- import { LogLevel } from "../component/logging";
5
- import { completionStatus, type CompletionStatus } from "../component/schema";
6
- export { completionStatus, type CompletionStatus };
7
- export type WorkId = string;
1
+ import { DefaultFunctionArgs, FunctionReference, FunctionVisibility } from "convex/server";
2
+ import { VString } from "convex/values";
3
+ import { Mounts } from "../component/_generated/api.js";
4
+ import { runResult as runResultValidator, RunResult, type RetryBehavior, Status } from "../component/shared.js";
5
+ import { type LogLevel } from "../component/logging.js";
6
+ import { RunMutationCtx, RunQueryCtx, UseApi } from "./utils.js";
7
+ export { runResultValidator, type RunResult };
8
+ export declare const DEFAULT_RETRY_BEHAVIOR: RetryBehavior;
9
+ export type WorkId = string & {
10
+ __isWorkId: true;
11
+ };
12
+ export declare const workIdValidator: VString<WorkId, "required">;
8
13
  export declare class Workpool {
9
14
  private component;
10
15
  private options;
11
- constructor(component: UseApi<typeof api>, options: {
16
+ /**
17
+ * Initializes a Workpool.
18
+ *
19
+ * Note: if you want different pools, you need to *create different instances*
20
+ * of Workpool in convex.config.ts. It isn't sufficient to have different
21
+ * instances of this class.
22
+ *
23
+ * @param component - The component to use, like `components.workpool` from
24
+ * `./_generated/api.ts`.
25
+ * @param options - The options for the Workpool.
26
+ */
27
+ constructor(component: UseApi<Mounts>, // UseApi<api> for jump to definition
28
+ options: {
12
29
  /** How many actions/mutations can be running at once within this pool.
13
30
  * Min 1, Max 300.
14
31
  */
15
- maxParallelism: number;
16
- /** How much to log.
17
- * Default WARN.
32
+ maxParallelism?: number;
33
+ /** How much to log. This is updated on each call to `enqueue*`,
34
+ * `status`, or `cancel*`.
35
+ * Default is WARN.
18
36
  * With INFO, you can see events for started and completed work, which can
19
- * be parsed.
37
+ * be parsed by tools like [Axiom](https://axiom.co) for monitoring.
20
38
  * With DEBUG, you can see timers and internal events for work being
21
39
  * scheduled.
22
40
  */
23
41
  logLevel?: LogLevel;
24
- /** How long to keep completed work in the database, for access by `status`.
25
- * Default 1 day.
42
+ /** Default retry behavior for enqueued actions. */
43
+ defaultRetryBehavior?: RetryBehavior;
44
+ /** Whether to retry actions that fail by default. Default: false.
45
+ * NOTE: Only do this if your actions are idempotent.
46
+ * See the docs (README.md) for more details.
26
47
  */
27
- statusTtl?: number;
48
+ retryActionsByDefault?: boolean;
28
49
  });
29
- enqueueAction<Args extends DefaultFunctionArgs, ReturnType>(ctx: RunMutationCtx, fn: FunctionReference<"action", FunctionVisibility, Args, ReturnType>, fnArgs: Args): Promise<WorkId>;
30
- enqueueMutation<Args extends DefaultFunctionArgs, ReturnType>(ctx: RunMutationCtx, fn: FunctionReference<"mutation", FunctionVisibility, Args, ReturnType>, fnArgs: Args): Promise<WorkId>;
50
+ /**
51
+ * Enqueues an action to be run.
52
+ *
53
+ * @param ctx - The mutation or action context that can call ctx.runMutation.
54
+ * @param fn - The action to run, like `internal.example.myAction`.
55
+ * @param fnArgs - The arguments to pass to the action.
56
+ * @param options - The options for the action to specify retry behavior,
57
+ * onComplete handling, and scheduling via `runAt` or `runAfter`.
58
+ * @returns The ID of the work that was enqueued.
59
+ */
60
+ enqueueAction<Args extends DefaultFunctionArgs, ReturnType>(ctx: RunMutationCtx, fn: FunctionReference<"action", FunctionVisibility, Args, ReturnType>, fnArgs: Args, options?: {
61
+ /** Whether to retry the action if it fails.
62
+ * If true, it will use the default retry behavior.
63
+ * If custom behavior is provided, it will retry using that behavior.
64
+ * If unset, it will use the Workpool's configured default.
65
+ */
66
+ retry?: boolean | RetryBehavior;
67
+ } & CallbackOptions & SchedulerOptions): Promise<WorkId>;
68
+ /**
69
+ * Enqueues a mutation to be run.
70
+ *
71
+ * Note: mutations are not retried by the workpool. Convex automatically
72
+ * retries them on database conflicts and transient failures.
73
+ * Because they're deterministic, external retries don't provide any benefit.
74
+ *
75
+ * @param ctx - The mutation or action context that can call ctx.runMutation.
76
+ * @param fn - The mutation to run, like `internal.example.myMutation`.
77
+ * @param fnArgs - The arguments to pass to the mutation.
78
+ * @param options - The options for the mutation to specify onComplete handling
79
+ * and scheduling via `runAt` or `runAfter`.
80
+ */
81
+ enqueueMutation<Args extends DefaultFunctionArgs, ReturnType>(ctx: RunMutationCtx, fn: FunctionReference<"mutation", FunctionVisibility, Args, ReturnType>, fnArgs: Args, options?: CallbackOptions & SchedulerOptions): Promise<WorkId>;
31
82
  cancel(ctx: RunMutationCtx, id: WorkId): Promise<void>;
32
- status(ctx: RunQueryCtx, id: WorkId): Promise<{
33
- kind: "pending";
34
- } | {
35
- kind: "inProgress";
36
- } | {
37
- kind: "completed";
38
- completionStatus: CompletionStatus;
39
- }>;
83
+ cancelAll(ctx: RunMutationCtx): Promise<void>;
84
+ status(ctx: RunQueryCtx, id: WorkId): Promise<Status>;
40
85
  }
41
- type RunQueryCtx = {
42
- runQuery: GenericQueryCtx<GenericDataModel>["runQuery"];
86
+ export type SchedulerOptions = {
87
+ /**
88
+ * The time (ms since epoch) to run the action at.
89
+ * If not provided, the action will be run as soon as possible.
90
+ * Note: this is advisory only. It may run later.
91
+ */
92
+ runAt?: number;
93
+ } | {
94
+ /**
95
+ * The number of milliseconds to run the action after.
96
+ * If not provided, the action will be run as soon as possible.
97
+ * Note: this is advisory only. It may run later.
98
+ */
99
+ runAfter?: number;
100
+ };
101
+ export type CallbackOptions = {
102
+ /**
103
+ * A mutation to run after the function succeeds, fails, or is canceled.
104
+ * The context type is for your use, feel free to provide a validator for it.
105
+ * e.g.
106
+ * ```ts
107
+ * export const completion = internalMutation({
108
+ * args: {
109
+ * workId: workIdValidator,
110
+ * context: v.any(),
111
+ * result: runResult,
112
+ * },
113
+ * handler: async (ctx, args) => {
114
+ * console.log(args.result, "Got Context back -> ", args.context, Date.now() - args.context);
115
+ * },
116
+ * });
117
+ * ```
118
+ */
119
+ onComplete?: FunctionReference<"mutation", FunctionVisibility, OnCompleteArgs> | null;
120
+ /**
121
+ * A context object to pass to the `onComplete` mutation.
122
+ * Useful for passing data from the enqueue site to the onComplete site.
123
+ */
124
+ context?: unknown;
43
125
  };
44
- type RunMutationCtx = {
45
- runMutation: GenericMutationCtx<GenericDataModel>["runMutation"];
126
+ export type OnCompleteArgs = {
127
+ /**
128
+ * The ID of the work that completed.
129
+ */
130
+ workId: WorkId;
131
+ /**
132
+ * The context object passed when enqueuing the work.
133
+ * Useful for passing data from the enqueue site to the onComplete site.
134
+ */
135
+ context: unknown;
136
+ /**
137
+ * The result of the run that completed.
138
+ */
139
+ result: RunResult;
46
140
  };
47
- export type OpaqueIds<T> = T extends GenericId<infer _T> ? string : T extends (infer U)[] ? OpaqueIds<U>[] : T extends object ? {
48
- [K in keyof T]: OpaqueIds<T[K]>;
49
- } : T;
50
- export type UseApi<API> = Expand<{
51
- [mod in keyof API]: API[mod] extends FunctionReference<infer FType, "public", infer FArgs, infer FReturnType, infer FComponentPath> ? FunctionReference<FType, "internal", OpaqueIds<FArgs>, OpaqueIds<FReturnType>, FComponentPath> : UseApi<API[mod]>;
52
- }>;
53
141
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,mBAAmB,EACnB,iBAAiB,EACjB,kBAAkB,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AACxD,OAAO,EAEL,SAAS,IAAI,kBAAkB,EAC/B,SAAS,EACT,KAAK,aAAa,EAElB,MAAM,EAEP,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAGjE,OAAO,EAAE,kBAAkB,EAAE,KAAK,SAAS,EAAE,CAAC;AAG9C,eAAO,MAAM,sBAAsB,EAAE,aAIpC,CAAC;AACF,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG;IAAE,UAAU,EAAE,IAAI,CAAA;CAAE,CAAC;AACnD,eAAO,MAAM,eAAe,6BAAgC,CAAC;AAE7D,qBAAa,QAAQ;IAajB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;IAbjB;;;;;;;;;;OAUG;gBAEO,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,qCAAqC;IAChE,OAAO,EAAE;QACf;;WAEG;QACH,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;;;;WAOG;QACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;QACpB,mDAAmD;QACnD,oBAAoB,CAAC,EAAE,aAAa,CAAC;QACrC;;;WAGG;QACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;KACjC;IAEH;;;;;;;;;OASG;IACG,aAAa,CAAC,IAAI,SAAS,mBAAmB,EAAE,UAAU,EAC9D,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,iBAAiB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,IAAI,EAAE,UAAU,CAAC,EACrE,MAAM,EAAE,IAAI,EACZ,OAAO,CAAC,EAAE;QACR;;;;WAIG;QACH,KAAK,CAAC,EAAE,OAAO,GAAG,aAAa,CAAC;KACjC,GAAG,eAAe,GACjB,gBAAgB,GACjB,OAAO,CAAC,MAAM,CAAC;IAuBlB;;;;;;;;;;;;OAYG;IACG,eAAe,CAAC,IAAI,SAAS,mBAAmB,EAAE,UAAU,EAChE,GAAG,EAAE,cAAc,EACnB,EAAE,EAAE,iBAAiB,CAAC,UAAU,EAAE,kBAAkB,EAAE,IAAI,EAAE,UAAU,CAAC,EACvE,MAAM,EAAE,IAAI,EACZ,OAAO,CAAC,EAAE,eAAe,GAAG,gBAAgB,GAC3C,OAAO,CAAC,MAAM,CAAC;IASZ,MAAM,CAAC,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C,MAAM,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAG5D;AAgCD,MAAM,MAAM,gBAAgB,GACxB;IACE;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACD;IACE;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEN,MAAM,MAAM,eAAe,GAAG;IAC5B;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,CAAC,EAAE,iBAAiB,CAC5B,UAAU,EACV,kBAAkB,EAClB,cAAc,CACf,GAAG,IAAI,CAAC;IAET;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,MAAM,EAAE,SAAS,CAAC;CACnB,CAAC"}
@@ -1,40 +1,147 @@
1
1
  import { createFunctionHandle, getFunctionName, } from "convex/server";
2
- import { completionStatus } from "../component/schema";
3
- export { completionStatus };
2
+ import { v } from "convex/values";
3
+ import { runResult as runResultValidator, } from "../component/shared.js";
4
+ import { logLevel } from "../component/logging.js";
5
+ import { DEFAULT_LOG_LEVEL } from "../component/logging.js";
6
+ import { DEFAULT_MAX_PARALLELISM } from "../component/kick.js";
7
+ export { runResultValidator };
8
+ // Attempts will run with delay [0, 250, 500, 1000, 2000] (ms)
9
+ export const DEFAULT_RETRY_BEHAVIOR = {
10
+ maxAttempts: 5,
11
+ initialBackoffMs: 250,
12
+ base: 2,
13
+ };
14
+ export const workIdValidator = v.string();
4
15
  export class Workpool {
5
16
  component;
6
17
  options;
7
- constructor(component, options) {
18
+ /**
19
+ * Initializes a Workpool.
20
+ *
21
+ * Note: if you want different pools, you need to *create different instances*
22
+ * of Workpool in convex.config.ts. It isn't sufficient to have different
23
+ * instances of this class.
24
+ *
25
+ * @param component - The component to use, like `components.workpool` from
26
+ * `./_generated/api.ts`.
27
+ * @param options - The options for the Workpool.
28
+ */
29
+ constructor(component, // UseApi<api> for jump to definition
30
+ options) {
8
31
  this.component = component;
9
32
  this.options = options;
10
33
  }
11
- async enqueueAction(ctx, fn, fnArgs) {
12
- const fnHandle = await createFunctionHandle(fn);
34
+ /**
35
+ * Enqueues an action to be run.
36
+ *
37
+ * @param ctx - The mutation or action context that can call ctx.runMutation.
38
+ * @param fn - The action to run, like `internal.example.myAction`.
39
+ * @param fnArgs - The arguments to pass to the action.
40
+ * @param options - The options for the action to specify retry behavior,
41
+ * onComplete handling, and scheduling via `runAt` or `runAfter`.
42
+ * @returns The ID of the work that was enqueued.
43
+ */
44
+ async enqueueAction(ctx, fn, fnArgs, options) {
45
+ const retryBehavior = getRetryBehavior(this.options.defaultRetryBehavior, this.options.retryActionsByDefault, options?.retry);
46
+ const onComplete = options?.onComplete
47
+ ? {
48
+ fnHandle: await createFunctionHandle(options.onComplete),
49
+ context: options.context,
50
+ }
51
+ : undefined;
13
52
  const id = await ctx.runMutation(this.component.lib.enqueue, {
14
- fnHandle,
15
- fnName: getFunctionName(fn),
53
+ ...(await defaultEnqueueArgs(fn, this.options)),
16
54
  fnArgs,
17
55
  fnType: "action",
18
- options: this.options,
56
+ runAt: getRunAt(options),
57
+ onComplete,
58
+ retryBehavior,
19
59
  });
20
60
  return id;
21
61
  }
22
- async enqueueMutation(ctx, fn, fnArgs) {
23
- const fnHandle = await createFunctionHandle(fn);
62
+ /**
63
+ * Enqueues a mutation to be run.
64
+ *
65
+ * Note: mutations are not retried by the workpool. Convex automatically
66
+ * retries them on database conflicts and transient failures.
67
+ * Because they're deterministic, external retries don't provide any benefit.
68
+ *
69
+ * @param ctx - The mutation or action context that can call ctx.runMutation.
70
+ * @param fn - The mutation to run, like `internal.example.myMutation`.
71
+ * @param fnArgs - The arguments to pass to the mutation.
72
+ * @param options - The options for the mutation to specify onComplete handling
73
+ * and scheduling via `runAt` or `runAfter`.
74
+ */
75
+ async enqueueMutation(ctx, fn, fnArgs, options) {
24
76
  const id = await ctx.runMutation(this.component.lib.enqueue, {
25
- fnHandle,
26
- fnName: getFunctionName(fn),
77
+ ...(await defaultEnqueueArgs(fn, this.options)),
27
78
  fnArgs,
28
79
  fnType: "mutation",
29
- options: this.options,
80
+ runAt: getRunAt(options),
30
81
  });
31
82
  return id;
32
83
  }
33
84
  async cancel(ctx, id) {
34
- await ctx.runMutation(this.component.lib.cancel, { id });
85
+ await ctx.runMutation(this.component.lib.cancel, {
86
+ id,
87
+ logLevel: this.options.logLevel ?? getDefaultLogLevel(),
88
+ });
89
+ }
90
+ async cancelAll(ctx) {
91
+ await ctx.runMutation(this.component.lib.cancelAll, {
92
+ logLevel: this.options.logLevel ?? getDefaultLogLevel(),
93
+ });
35
94
  }
36
95
  async status(ctx, id) {
37
- return await ctx.runQuery(this.component.lib.status, { id });
96
+ return ctx.runQuery(this.component.lib.status, { id });
97
+ }
98
+ }
99
+ function getRetryBehavior(defaultRetryBehavior, retryActionsByDefault, retryOverride) {
100
+ const defaultRetry = defaultRetryBehavior ?? DEFAULT_RETRY_BEHAVIOR;
101
+ const retryByDefault = retryActionsByDefault ?? false;
102
+ if (retryOverride === true) {
103
+ return defaultRetry;
104
+ }
105
+ if (retryOverride === false) {
106
+ return undefined;
107
+ }
108
+ return retryOverride ?? (retryByDefault ? defaultRetry : undefined);
109
+ }
110
+ async function defaultEnqueueArgs(fn, { logLevel, maxParallelism }) {
111
+ return {
112
+ fnHandle: await createFunctionHandle(fn),
113
+ fnName: getFunctionName(fn),
114
+ config: {
115
+ logLevel: logLevel ?? getDefaultLogLevel(),
116
+ maxParallelism: maxParallelism ?? DEFAULT_MAX_PARALLELISM,
117
+ },
118
+ };
119
+ }
120
+ // ensure OnCompleteArgs satisfies SharedOnCompleteArgs
121
+ const _ = {};
122
+ function getRunAt(options) {
123
+ if (!options) {
124
+ return Date.now();
125
+ }
126
+ if ("runAt" in options && options.runAt !== undefined) {
127
+ return options.runAt;
128
+ }
129
+ if ("runAfter" in options && options.runAfter !== undefined) {
130
+ return Date.now() + options.runAfter;
131
+ }
132
+ return Date.now();
133
+ }
134
+ function getDefaultLogLevel() {
135
+ if (process.env.WORKPOOL_LOG_LEVEL) {
136
+ if (!logLevel.members
137
+ .map((m) => m.value)
138
+ .includes(process.env.WORKPOOL_LOG_LEVEL)) {
139
+ console.warn(`Invalid log level (${process.env.WORKPOOL_LOG_LEVEL}), defaulting to "INFO"`);
140
+ }
141
+ else {
142
+ return process.env.WORKPOOL_LOG_LEVEL;
143
+ }
38
144
  }
145
+ return DEFAULT_LOG_LEVEL;
39
146
  }
40
147
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EAQpB,eAAe,GAChB,MAAM,eAAe,CAAC;AAIvB,OAAO,EAAE,gBAAgB,EAAyB,MAAM,qBAAqB,CAAC;AAC9E,OAAO,EAAE,gBAAgB,EAAyB,CAAC;AAInD,MAAM,OAAO,QAAQ;IAET;IACA;IAFV,YACU,SAA6B,EAC7B,OAiBP;QAlBO,cAAS,GAAT,SAAS,CAAoB;QAC7B,YAAO,GAAP,OAAO,CAiBd;IACA,CAAC;IACJ,KAAK,CAAC,aAAa,CACjB,GAAmB,EACnB,EAAqE,EACrE,MAAY;QAEZ,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,EAAE,CAAC,CAAC;QAChD,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE;YAC3D,QAAQ;YACR,MAAM,EAAE,eAAe,CAAC,EAAE,CAAC;YAC3B,MAAM;YACN,MAAM,EAAE,QAAQ;YAChB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;QACH,OAAO,EAAY,CAAC;IACtB,CAAC;IACD,KAAK,CAAC,eAAe,CACnB,GAAmB,EACnB,EAAuE,EACvE,MAAY;QAEZ,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC,EAAE,CAAC,CAAC;QAChD,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE;YAC3D,QAAQ;YACR,MAAM,EAAE,eAAe,CAAC,EAAE,CAAC;YAC3B,MAAM;YACN,MAAM,EAAE,UAAU;YAClB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC,CAAC;QACH,OAAO,EAAY,CAAC;IACtB,CAAC;IACD,KAAK,CAAC,MAAM,CAAC,GAAmB,EAAE,EAAU;QAC1C,MAAM,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC;IACD,KAAK,CAAC,MAAM,CACV,GAAgB,EAChB,EAAU;QAMV,OAAO,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/D,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { Expand, FunctionReference, GenericMutationCtx, GenericQueryCtx } from "convex/server";
2
+ import { GenericId } from "convex/values";
3
+ import { GenericDataModel } from "convex/server";
4
+ export type RunQueryCtx = {
5
+ runQuery: GenericQueryCtx<GenericDataModel>["runQuery"];
6
+ };
7
+ export type RunMutationCtx = {
8
+ runMutation: GenericMutationCtx<GenericDataModel>["runMutation"];
9
+ };
10
+ export type OpaqueIds<T> = T extends GenericId<infer _T> ? string : T extends (infer U)[] ? OpaqueIds<U>[] : T extends object ? {
11
+ [K in keyof T]: OpaqueIds<T[K]>;
12
+ } : T;
13
+ export type UseApi<API> = Expand<{
14
+ [mod in keyof API]: API[mod] extends FunctionReference<infer FType, "public", infer FArgs, infer FReturnType, infer FComponentPath> ? FunctionReference<FType, "internal", OpaqueIds<FArgs>, OpaqueIds<FReturnType>, FComponentPath> : UseApi<API[mod]>;
15
+ }>;
16
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/client/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAIjD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC,UAAU,CAAC,CAAC;CACzD,CAAC;AACF,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC,aAAa,CAAC,CAAC;CAClE,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IACrB,CAAC,SAAS,SAAS,CAAC,MAAM,EAAE,CAAC,GACzB,MAAM,GACN,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,GACnB,SAAS,CAAC,CAAC,CAAC,EAAE,GACd,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACnC,CAAC,CAAC;AAEZ,MAAM,MAAM,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC;KAC9B,GAAG,IAAI,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,SAAS,iBAAiB,CACpD,MAAM,KAAK,EACX,QAAQ,EACR,MAAM,KAAK,EACX,MAAM,WAAW,EACjB,MAAM,cAAc,CACrB,GACG,iBAAiB,CACf,KAAK,EACL,UAAU,EACV,SAAS,CAAC,KAAK,CAAC,EAChB,SAAS,CAAC,WAAW,CAAC,EACtB,cAAc,CACf,GACD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CACrB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/client/utils.ts"],"names":[],"mappings":""}