@convex-dev/workpool 0.2.0-beta.0 → 0.2.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.
- package/README.md +7 -16
- package/dist/commonjs/client/index.d.ts +3 -3
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +10 -5
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/component/complete.d.ts +89 -0
- package/dist/commonjs/component/complete.d.ts.map +1 -0
- package/dist/commonjs/component/complete.js +80 -0
- package/dist/commonjs/component/complete.js.map +1 -0
- package/dist/commonjs/component/kick.d.ts +1 -2
- package/dist/commonjs/component/kick.d.ts.map +1 -1
- package/dist/commonjs/component/kick.js +7 -5
- package/dist/commonjs/component/kick.js.map +1 -1
- package/dist/commonjs/component/lib.d.ts +3 -3
- package/dist/commonjs/component/lib.d.ts.map +1 -1
- package/dist/commonjs/component/lib.js +43 -20
- package/dist/commonjs/component/lib.js.map +1 -1
- package/dist/commonjs/component/logging.d.ts.map +1 -1
- package/dist/commonjs/component/logging.js +1 -2
- package/dist/commonjs/component/logging.js.map +1 -1
- package/dist/commonjs/component/loop.d.ts +1 -14
- package/dist/commonjs/component/loop.d.ts.map +1 -1
- package/dist/commonjs/component/loop.js +215 -178
- package/dist/commonjs/component/loop.js.map +1 -1
- package/dist/commonjs/component/recovery.d.ts +16 -0
- package/dist/commonjs/component/recovery.d.ts.map +1 -1
- package/dist/commonjs/component/recovery.js +64 -44
- package/dist/commonjs/component/recovery.js.map +1 -1
- package/dist/commonjs/component/schema.d.ts +6 -2
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +5 -3
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/shared.d.ts +20 -11
- package/dist/commonjs/component/shared.d.ts.map +1 -1
- package/dist/commonjs/component/shared.js +18 -5
- package/dist/commonjs/component/shared.js.map +1 -1
- package/dist/commonjs/component/stats.d.ts +21 -13
- package/dist/commonjs/component/stats.d.ts.map +1 -1
- package/dist/commonjs/component/stats.js +32 -22
- package/dist/commonjs/component/stats.js.map +1 -1
- package/dist/commonjs/component/worker.d.ts +2 -12
- package/dist/commonjs/component/worker.d.ts.map +1 -1
- package/dist/commonjs/component/worker.js +23 -36
- package/dist/commonjs/component/worker.js.map +1 -1
- package/dist/esm/client/index.d.ts +3 -3
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +10 -5
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/component/complete.d.ts +89 -0
- package/dist/esm/component/complete.d.ts.map +1 -0
- package/dist/esm/component/complete.js +80 -0
- package/dist/esm/component/complete.js.map +1 -0
- package/dist/esm/component/kick.d.ts +1 -2
- package/dist/esm/component/kick.d.ts.map +1 -1
- package/dist/esm/component/kick.js +7 -5
- package/dist/esm/component/kick.js.map +1 -1
- package/dist/esm/component/lib.d.ts +3 -3
- package/dist/esm/component/lib.d.ts.map +1 -1
- package/dist/esm/component/lib.js +43 -20
- package/dist/esm/component/lib.js.map +1 -1
- package/dist/esm/component/logging.d.ts.map +1 -1
- package/dist/esm/component/logging.js +1 -2
- package/dist/esm/component/logging.js.map +1 -1
- package/dist/esm/component/loop.d.ts +1 -14
- package/dist/esm/component/loop.d.ts.map +1 -1
- package/dist/esm/component/loop.js +215 -178
- package/dist/esm/component/loop.js.map +1 -1
- package/dist/esm/component/recovery.d.ts +16 -0
- package/dist/esm/component/recovery.d.ts.map +1 -1
- package/dist/esm/component/recovery.js +64 -44
- package/dist/esm/component/recovery.js.map +1 -1
- package/dist/esm/component/schema.d.ts +6 -2
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +5 -3
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/shared.d.ts +20 -11
- package/dist/esm/component/shared.d.ts.map +1 -1
- package/dist/esm/component/shared.js +18 -5
- package/dist/esm/component/shared.js.map +1 -1
- package/dist/esm/component/stats.d.ts +21 -13
- package/dist/esm/component/stats.d.ts.map +1 -1
- package/dist/esm/component/stats.js +32 -22
- package/dist/esm/component/stats.js.map +1 -1
- package/dist/esm/component/worker.d.ts +2 -12
- package/dist/esm/component/worker.d.ts.map +1 -1
- package/dist/esm/component/worker.js +23 -36
- package/dist/esm/component/worker.js.map +1 -1
- package/package.json +7 -6
- package/src/client/index.ts +18 -8
- package/src/component/README.md +15 -15
- package/src/component/_generated/api.d.ts +7 -2
- package/src/component/complete.test.ts +508 -0
- package/src/component/complete.ts +98 -0
- package/src/component/kick.test.ts +13 -13
- package/src/component/kick.ts +13 -8
- package/src/component/lib.test.ts +262 -17
- package/src/component/lib.ts +55 -24
- package/src/component/logging.ts +1 -2
- package/src/component/loop.test.ts +1158 -0
- package/src/component/loop.ts +289 -221
- package/src/component/recovery.test.ts +541 -0
- package/src/component/recovery.ts +80 -63
- package/src/component/schema.ts +6 -4
- package/src/component/shared.ts +21 -6
- package/src/component/stats.ts +48 -25
- package/src/component/worker.ts +25 -38
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { v } from "convex/values";
|
|
2
|
-
import { internalQuery } from "./_generated/server.js";
|
|
2
|
+
import { internalQuery, query } from "./_generated/server.js";
|
|
3
|
+
import { DEFAULT_MAX_PARALLELISM } from "./shared.js";
|
|
3
4
|
/**
|
|
4
5
|
* Record stats about work execution. Intended to be queried by Axiom or Datadog.
|
|
5
6
|
*/
|
|
@@ -12,36 +13,39 @@ workpool
|
|
|
12
13
|
parse_json(trim("'", tostring(["data.message"]))),
|
|
13
14
|
parse_json('{}')
|
|
14
15
|
)
|
|
15
|
-
| extend
|
|
16
|
+
| extend startLag = parsed_message["startLag"]
|
|
16
17
|
| extend fnName = parsed_message["fnName"]
|
|
17
|
-
| summarize avg(todouble(
|
|
18
|
+
| summarize avg(todouble(startLag)) by bin_auto(_time), tostring(fnName)
|
|
18
19
|
|
|
19
20
|
*/
|
|
20
|
-
export function
|
|
21
|
-
|
|
21
|
+
export function recordEnqueued(console, data) {
|
|
22
|
+
console.event("enqueued", {
|
|
23
|
+
...data,
|
|
24
|
+
enqueuedAt: Date.now(),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function recordStarted(console, work, lagMs) {
|
|
28
|
+
console.event("started", {
|
|
22
29
|
workId: work._id,
|
|
23
|
-
event: "started",
|
|
24
30
|
fnName: work.fnName,
|
|
25
31
|
enqueuedAt: work._creationTime,
|
|
26
32
|
startedAt: Date.now(),
|
|
27
|
-
|
|
33
|
+
startLag: lagMs,
|
|
28
34
|
});
|
|
29
35
|
}
|
|
30
|
-
export function recordCompleted(work, status) {
|
|
31
|
-
|
|
36
|
+
export function recordCompleted(console, work, status) {
|
|
37
|
+
console.event("completed", {
|
|
32
38
|
workId: work._id,
|
|
33
|
-
event: "completed",
|
|
34
39
|
fnName: work.fnName,
|
|
35
40
|
completedAt: Date.now(),
|
|
41
|
+
attempts: work.attempts,
|
|
36
42
|
status,
|
|
37
|
-
lagSinceEnqueued: Date.now() - work._creationTime,
|
|
38
43
|
});
|
|
39
44
|
}
|
|
40
|
-
export function recordReport(state) {
|
|
45
|
+
export function recordReport(console, state) {
|
|
41
46
|
const { completed, succeeded, failed, retries, canceled } = state.report;
|
|
42
47
|
const withoutRetries = completed - retries;
|
|
43
|
-
|
|
44
|
-
event: "report",
|
|
48
|
+
console.event("report", {
|
|
45
49
|
completed,
|
|
46
50
|
succeeded,
|
|
47
51
|
failed,
|
|
@@ -55,7 +59,7 @@ export function recordReport(state) {
|
|
|
55
59
|
* Warning: this should not be used from a mutation, as it will cause conflicts.
|
|
56
60
|
* Use this to debug or diagnose your queue length when it's backed up.
|
|
57
61
|
*/
|
|
58
|
-
export const queueLength =
|
|
62
|
+
export const queueLength = query({
|
|
59
63
|
args: {},
|
|
60
64
|
returns: v.number(),
|
|
61
65
|
handler: async (ctx) => {
|
|
@@ -67,22 +71,28 @@ export const queueLength = internalQuery({
|
|
|
67
71
|
* Warning: this should not be used from a mutation, as it will cause conflicts.
|
|
68
72
|
* Use this while developing to see the state of the queue.
|
|
69
73
|
*/
|
|
70
|
-
export const
|
|
74
|
+
export const diagnostic = internalQuery({
|
|
71
75
|
args: {},
|
|
72
76
|
returns: v.any(),
|
|
73
77
|
handler: async (ctx) => {
|
|
74
|
-
const
|
|
78
|
+
const global = await ctx.db.query("globals").unique();
|
|
79
|
+
const internalState = await ctx.db.query("internalState").unique();
|
|
80
|
+
const inProgressWork = internalState?.running.length ?? 0;
|
|
81
|
+
const maxParallelism = global?.maxParallelism ?? DEFAULT_MAX_PARALLELISM;
|
|
75
82
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
76
83
|
const pendingStart = await ctx.db.query("pendingStart").count();
|
|
77
84
|
const pendingCompletion = await ctx.db.query("pendingCompletion").count();
|
|
78
85
|
const pendingCancelation = await ctx.db.query("pendingCancelation").count();
|
|
86
|
+
const runStatus = await ctx.db.query("runStatus").unique();
|
|
79
87
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
80
88
|
return {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
pendingCompletion,
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
canceling: pendingCancelation,
|
|
90
|
+
waiting: pendingStart,
|
|
91
|
+
running: inProgressWork - pendingCompletion,
|
|
92
|
+
completing: pendingCompletion,
|
|
93
|
+
spareCapacity: maxParallelism - inProgressWork,
|
|
94
|
+
runStatus: runStatus?.state.kind,
|
|
95
|
+
generation: internalState?.generation,
|
|
86
96
|
};
|
|
87
97
|
},
|
|
88
98
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/component/stats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../../../src/component/stats.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,23 +3,13 @@ export declare const runMutationWrapper: import("convex/server").RegisteredMutat
|
|
|
3
3
|
fnHandle: string;
|
|
4
4
|
workId: import("convex/values").GenericId<"work">;
|
|
5
5
|
fnArgs: any;
|
|
6
|
+
attempt: number;
|
|
6
7
|
}, Promise<void>>;
|
|
7
8
|
export declare const runActionWrapper: import("convex/server").RegisteredAction<"internal", {
|
|
8
9
|
logLevel: "DEBUG" | "INFO" | "WARN" | "ERROR";
|
|
9
10
|
fnHandle: string;
|
|
10
11
|
workId: import("convex/values").GenericId<"work">;
|
|
11
12
|
fnArgs: any;
|
|
12
|
-
|
|
13
|
-
export declare const saveResult: import("convex/server").RegisteredMutation<"internal", {
|
|
14
|
-
workId: import("convex/values").GenericId<"work">;
|
|
15
|
-
runResult: {
|
|
16
|
-
kind: "success";
|
|
17
|
-
returnValue: any;
|
|
18
|
-
} | {
|
|
19
|
-
kind: "failed";
|
|
20
|
-
error: string;
|
|
21
|
-
} | {
|
|
22
|
-
kind: "canceled";
|
|
23
|
-
};
|
|
13
|
+
attempt: number;
|
|
24
14
|
}, Promise<void>>;
|
|
25
15
|
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/component/worker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/component/worker.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,kBAAkB;;;;;;iBA4B7B,CAAC;AASH,eAAO,MAAM,gBAAgB;;;;;;iBA4B3B,CAAC"}
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import { v } from "convex/values";
|
|
2
2
|
import { internal } from "./_generated/api.js";
|
|
3
3
|
import { internalAction, internalMutation } from "./_generated/server.js";
|
|
4
|
-
import { kickMainLoop } from "./kick.js";
|
|
5
4
|
import { createLogger, logLevel } from "./logging.js";
|
|
6
|
-
import { nextSegment, runResult } from "./shared.js";
|
|
7
5
|
export const runMutationWrapper = internalMutation({
|
|
8
6
|
args: {
|
|
9
7
|
workId: v.id("work"),
|
|
10
8
|
fnHandle: v.string(),
|
|
11
9
|
fnArgs: v.any(),
|
|
12
10
|
logLevel,
|
|
11
|
+
attempt: v.number(),
|
|
13
12
|
},
|
|
14
|
-
handler: async (ctx, { workId,
|
|
15
|
-
const console = createLogger(logLevel);
|
|
16
|
-
const fnHandle =
|
|
13
|
+
handler: async (ctx, { workId, attempt, ...args }) => {
|
|
14
|
+
const console = createLogger(args.logLevel);
|
|
15
|
+
const fnHandle = args.fnHandle;
|
|
17
16
|
try {
|
|
18
|
-
const returnValue = await ctx.runMutation(fnHandle, fnArgs);
|
|
17
|
+
const returnValue = await ctx.runMutation(fnHandle, args.fnArgs);
|
|
19
18
|
// NOTE: we could run the `saveResult` handler here, or call `ctx.runMutation`,
|
|
20
19
|
// but we want the mutation to be a separate transaction to reduce the window for OCCs.
|
|
21
|
-
await ctx.scheduler.runAfter(0, internal.
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
await ctx.scheduler.runAfter(0, internal.complete.complete, {
|
|
21
|
+
jobs: [
|
|
22
|
+
{ workId, runResult: { kind: "success", returnValue }, attempt },
|
|
23
|
+
],
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
catch (e) {
|
|
27
27
|
console.error(e);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
const runResult = { kind: "failed", error: formatError(e) };
|
|
29
|
+
await ctx.scheduler.runAfter(0, internal.complete.complete, {
|
|
30
|
+
jobs: [{ workId, runResult, attempt }],
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
},
|
|
@@ -44,43 +44,30 @@ export const runActionWrapper = internalAction({
|
|
|
44
44
|
fnHandle: v.string(),
|
|
45
45
|
fnArgs: v.any(),
|
|
46
46
|
logLevel,
|
|
47
|
+
attempt: v.number(),
|
|
47
48
|
},
|
|
48
|
-
handler: async (ctx, { workId,
|
|
49
|
-
const console = createLogger(logLevel);
|
|
50
|
-
const fnHandle =
|
|
49
|
+
handler: async (ctx, { workId, attempt, ...args }) => {
|
|
50
|
+
const console = createLogger(args.logLevel);
|
|
51
|
+
const fnHandle = args.fnHandle;
|
|
51
52
|
try {
|
|
52
|
-
const returnValue = await ctx.runAction(fnHandle, fnArgs);
|
|
53
|
+
const returnValue = await ctx.runAction(fnHandle, args.fnArgs);
|
|
53
54
|
// NOTE: we could run `ctx.runMutation`, but we want to guarantee execution,
|
|
54
55
|
// and `ctx.scheduler.runAfter` won't OCC.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
const runResult = { kind: "success", returnValue };
|
|
57
|
+
await ctx.scheduler.runAfter(0, internal.complete.complete, {
|
|
58
|
+
jobs: [{ workId, runResult, attempt }],
|
|
58
59
|
});
|
|
59
60
|
}
|
|
60
61
|
catch (e) {
|
|
61
62
|
console.error(e);
|
|
62
63
|
// We let the main loop handle the retries.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
const runResult = { kind: "failed", error: formatError(e) };
|
|
65
|
+
await ctx.scheduler.runAfter(0, internal.complete.complete, {
|
|
66
|
+
jobs: [{ workId, runResult, attempt }],
|
|
66
67
|
});
|
|
67
68
|
}
|
|
68
69
|
},
|
|
69
70
|
});
|
|
70
|
-
export const saveResult = internalMutation({
|
|
71
|
-
args: {
|
|
72
|
-
workId: v.id("work"),
|
|
73
|
-
runResult,
|
|
74
|
-
},
|
|
75
|
-
handler: async (ctx, { workId, runResult }) => {
|
|
76
|
-
await ctx.db.insert("pendingCompletion", {
|
|
77
|
-
runResult,
|
|
78
|
-
workId,
|
|
79
|
-
segment: nextSegment(),
|
|
80
|
-
});
|
|
81
|
-
await kickMainLoop(ctx, "saveResult");
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
71
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
85
72
|
const console = "THIS IS A REMINDER TO USE createLogger";
|
|
86
73
|
//# sourceMappingURL=worker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../../src/component/worker.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../../src/component/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"email": "support@convex.dev",
|
|
8
8
|
"url": "https://github.com/get-convex/workpool/issues"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.2.0
|
|
10
|
+
"version": "0.2.0",
|
|
11
11
|
"license": "Apache-2.0",
|
|
12
12
|
"keywords": [
|
|
13
13
|
"convex",
|
|
@@ -24,14 +24,15 @@
|
|
|
24
24
|
"build:cjs": "tsc --project ./commonjs.json && echo '{\\n \"type\": \"commonjs\"\\n}' > dist/commonjs/package.json",
|
|
25
25
|
"dev": "cd example; npm run dev",
|
|
26
26
|
"typecheck": "tsc --noEmit",
|
|
27
|
-
"prepare": "npm run build",
|
|
28
|
-
"prepack": "node node10stubs.mjs",
|
|
29
|
-
"postpack": "node node10stubs.mjs --cleanup",
|
|
30
|
-
"test": "vitest",
|
|
31
27
|
"lint": "tsc --noEmit && eslint . && prettier --check .",
|
|
32
28
|
"format": "prettier --write .",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
33
31
|
"test:debug": "vitest --inspect-brk --no-file-parallelism",
|
|
34
|
-
"test:coverage": "vitest run --coverage --coverage.reporter=text"
|
|
32
|
+
"test:coverage": "vitest run --coverage --coverage.reporter=text",
|
|
33
|
+
"prepare": "npm run build",
|
|
34
|
+
"prepack": "node node10stubs.mjs",
|
|
35
|
+
"postpack": "node node10stubs.mjs --cleanup"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
37
38
|
"dist",
|
package/src/client/index.ts
CHANGED
|
@@ -8,19 +8,22 @@ import {
|
|
|
8
8
|
import { v, VString } from "convex/values";
|
|
9
9
|
import { Mounts } from "../component/_generated/api.js";
|
|
10
10
|
import {
|
|
11
|
+
DEFAULT_LOG_LEVEL,
|
|
12
|
+
type LogLevel,
|
|
13
|
+
logLevel,
|
|
14
|
+
} from "../component/logging.js";
|
|
15
|
+
import {
|
|
16
|
+
Config,
|
|
17
|
+
DEFAULT_MAX_PARALLELISM,
|
|
11
18
|
OnComplete,
|
|
12
|
-
runResult as
|
|
13
|
-
RunResult,
|
|
19
|
+
runResult as resultValidator,
|
|
14
20
|
type RetryBehavior,
|
|
21
|
+
RunResult,
|
|
15
22
|
OnCompleteArgs as SharedOnCompleteArgs,
|
|
16
23
|
Status,
|
|
17
|
-
Config,
|
|
18
24
|
} from "../component/shared.js";
|
|
19
|
-
import { type LogLevel, logLevel } from "../component/logging.js";
|
|
20
25
|
import { RunMutationCtx, RunQueryCtx, UseApi } from "./utils.js";
|
|
21
|
-
|
|
22
|
-
import { DEFAULT_MAX_PARALLELISM } from "../component/kick.js";
|
|
23
|
-
export { runResultValidator, type RunResult };
|
|
26
|
+
export { resultValidator, type RunResult };
|
|
24
27
|
|
|
25
28
|
// Attempts will run with delay [0, 250, 500, 1000, 2000] (ms)
|
|
26
29
|
export const DEFAULT_RETRY_BEHAVIOR: RetryBehavior = {
|
|
@@ -133,11 +136,18 @@ export class Workpool {
|
|
|
133
136
|
fnArgs: Args,
|
|
134
137
|
options?: CallbackOptions & SchedulerOptions
|
|
135
138
|
): Promise<WorkId> {
|
|
139
|
+
const onComplete: OnComplete | undefined = options?.onComplete
|
|
140
|
+
? {
|
|
141
|
+
fnHandle: await createFunctionHandle(options.onComplete),
|
|
142
|
+
context: options.context,
|
|
143
|
+
}
|
|
144
|
+
: undefined;
|
|
136
145
|
const id = await ctx.runMutation(this.component.lib.enqueue, {
|
|
137
146
|
...(await defaultEnqueueArgs(fn, this.options)),
|
|
138
147
|
fnArgs,
|
|
139
148
|
fnType: "mutation",
|
|
140
149
|
runAt: getRunAt(options),
|
|
150
|
+
onComplete,
|
|
141
151
|
});
|
|
142
152
|
return id as WorkId;
|
|
143
153
|
}
|
|
@@ -215,7 +225,7 @@ export type CallbackOptions = {
|
|
|
215
225
|
* args: {
|
|
216
226
|
* workId: workIdValidator,
|
|
217
227
|
* context: v.any(),
|
|
218
|
-
* result:
|
|
228
|
+
* result: resultValidator,
|
|
219
229
|
* },
|
|
220
230
|
* handler: async (ctx, args) => {
|
|
221
231
|
* console.log(args.result, "Got Context back -> ", args.context, Date.now() - args.context);
|
package/src/component/README.md
CHANGED
|
@@ -25,19 +25,19 @@ Concepts:
|
|
|
25
25
|
flowchart LR
|
|
26
26
|
Client -->|enqueue| pendingStart
|
|
27
27
|
Client -->|cancel| pendingCancelation
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
workerRunning
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
complete --> |success or failure| pendingCompletion
|
|
29
|
+
pendingCompletion -->|retry| pendingStart
|
|
30
|
+
pendingStart --> workerRunning["worker running"]
|
|
31
|
+
workerRunning -->|worker finished| complete
|
|
32
|
+
workerRunning --> |recovery| complete
|
|
33
|
+
successfulCancel["AND"]@{shape: delay} --> |canceled| complete
|
|
34
|
+
pendingStart --> successfulCancel
|
|
35
|
+
pendingCancelation --> successfulCancel
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Notably:
|
|
39
39
|
|
|
40
|
-
- The pending\* states are
|
|
40
|
+
- The pending\* states are written by outside sources.
|
|
41
41
|
- The main loop federates changes to/from "running"
|
|
42
42
|
- Canceling only impacts pending and retrying jobs.
|
|
43
43
|
|
|
@@ -53,12 +53,12 @@ flowchart TD
|
|
|
53
53
|
running-->|"all done"| idle
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
- While the loop is running,
|
|
57
|
-
|
|
58
|
-
- The "saturated" state is concretely "running" or "scheduled"
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
terminating.
|
|
56
|
+
- While the loop is running, the runStatus doesn't change, making it safer to
|
|
57
|
+
read from clients without database conflicts.
|
|
58
|
+
- The "saturated" state is concretely "running" or "scheduled" at max
|
|
59
|
+
parallelism. There is a boolean set on "scheduled" to avoid clients from
|
|
60
|
+
kicking the main loop on enqueueing, which is unlikely to be productive, since
|
|
61
|
+
the next action needs to be something terminating.
|
|
62
62
|
|
|
63
63
|
## Retention optimization strategy
|
|
64
64
|
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type * as complete from "../complete.js";
|
|
11
12
|
import type * as kick from "../kick.js";
|
|
12
13
|
import type * as lib from "../lib.js";
|
|
13
14
|
import type * as logging from "../logging.js";
|
|
@@ -31,6 +32,7 @@ import type {
|
|
|
31
32
|
* ```
|
|
32
33
|
*/
|
|
33
34
|
declare const fullApi: ApiFromModules<{
|
|
35
|
+
complete: typeof complete;
|
|
34
36
|
kick: typeof kick;
|
|
35
37
|
lib: typeof lib;
|
|
36
38
|
logging: typeof logging;
|
|
@@ -80,11 +82,14 @@ export type Mounts = {
|
|
|
80
82
|
"query",
|
|
81
83
|
"public",
|
|
82
84
|
{ id: string },
|
|
83
|
-
| {
|
|
84
|
-
| {
|
|
85
|
+
| { previousAttempts: number; state: "pending" }
|
|
86
|
+
| { previousAttempts: number; state: "running" }
|
|
85
87
|
| { state: "finished" }
|
|
86
88
|
>;
|
|
87
89
|
};
|
|
90
|
+
stats: {
|
|
91
|
+
queueLength: FunctionReference<"query", "public", {}, number>;
|
|
92
|
+
};
|
|
88
93
|
};
|
|
89
94
|
// For now fullApiWithMounts is only fullApi which provides
|
|
90
95
|
// jump-to-definition in component client code.
|