@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.
- package/README.md +155 -17
- package/dist/commonjs/client/index.d.ts +123 -35
- package/dist/commonjs/client/index.d.ts.map +1 -1
- package/dist/commonjs/client/index.js +122 -15
- package/dist/commonjs/client/index.js.map +1 -1
- package/dist/commonjs/client/utils.d.ts +16 -0
- package/dist/commonjs/client/utils.d.ts.map +1 -0
- package/dist/commonjs/client/utils.js +2 -0
- package/dist/commonjs/client/utils.js.map +1 -0
- 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/convex.config.d.ts.map +1 -1
- package/dist/commonjs/component/convex.config.js +0 -2
- package/dist/commonjs/component/convex.config.js.map +1 -1
- package/dist/commonjs/component/kick.d.ts +9 -0
- package/dist/commonjs/component/kick.d.ts.map +1 -0
- package/dist/commonjs/component/kick.js +97 -0
- package/dist/commonjs/component/kick.js.map +1 -0
- package/dist/commonjs/component/lib.d.ts +23 -32
- package/dist/commonjs/component/lib.d.ts.map +1 -1
- package/dist/commonjs/component/lib.js +91 -563
- package/dist/commonjs/component/lib.js.map +1 -1
- package/dist/commonjs/component/logging.d.ts +5 -3
- package/dist/commonjs/component/logging.d.ts.map +1 -1
- package/dist/commonjs/component/logging.js +13 -2
- package/dist/commonjs/component/logging.js.map +1 -1
- package/dist/commonjs/component/loop.d.ts +13 -0
- package/dist/commonjs/component/loop.d.ts.map +1 -0
- package/dist/commonjs/component/loop.js +482 -0
- package/dist/commonjs/component/loop.js.map +1 -0
- package/dist/commonjs/component/recovery.d.ts +24 -0
- package/dist/commonjs/component/recovery.d.ts.map +1 -0
- package/dist/commonjs/component/recovery.js +94 -0
- package/dist/commonjs/component/recovery.js.map +1 -0
- package/dist/commonjs/component/schema.d.ts +167 -93
- package/dist/commonjs/component/schema.d.ts.map +1 -1
- package/dist/commonjs/component/schema.js +56 -65
- package/dist/commonjs/component/schema.js.map +1 -1
- package/dist/commonjs/component/shared.d.ts +138 -0
- package/dist/commonjs/component/shared.d.ts.map +1 -0
- package/dist/commonjs/component/shared.js +77 -0
- package/dist/commonjs/component/shared.js.map +1 -0
- package/dist/commonjs/component/stats.d.ts +6 -3
- package/dist/commonjs/component/stats.d.ts.map +1 -1
- package/dist/commonjs/component/stats.js +23 -4
- package/dist/commonjs/component/stats.js.map +1 -1
- package/dist/commonjs/component/worker.d.ts +15 -0
- package/dist/commonjs/component/worker.d.ts.map +1 -0
- package/dist/commonjs/component/worker.js +73 -0
- package/dist/commonjs/component/worker.js.map +1 -0
- package/dist/esm/client/index.d.ts +123 -35
- package/dist/esm/client/index.d.ts.map +1 -1
- package/dist/esm/client/index.js +122 -15
- package/dist/esm/client/index.js.map +1 -1
- package/dist/esm/client/utils.d.ts +16 -0
- package/dist/esm/client/utils.d.ts.map +1 -0
- package/dist/esm/client/utils.js +2 -0
- package/dist/esm/client/utils.js.map +1 -0
- 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/convex.config.d.ts.map +1 -1
- package/dist/esm/component/convex.config.js +0 -2
- package/dist/esm/component/convex.config.js.map +1 -1
- package/dist/esm/component/kick.d.ts +9 -0
- package/dist/esm/component/kick.d.ts.map +1 -0
- package/dist/esm/component/kick.js +97 -0
- package/dist/esm/component/kick.js.map +1 -0
- package/dist/esm/component/lib.d.ts +23 -32
- package/dist/esm/component/lib.d.ts.map +1 -1
- package/dist/esm/component/lib.js +91 -563
- package/dist/esm/component/lib.js.map +1 -1
- package/dist/esm/component/logging.d.ts +5 -3
- package/dist/esm/component/logging.d.ts.map +1 -1
- package/dist/esm/component/logging.js +13 -2
- package/dist/esm/component/logging.js.map +1 -1
- package/dist/esm/component/loop.d.ts +13 -0
- package/dist/esm/component/loop.d.ts.map +1 -0
- package/dist/esm/component/loop.js +482 -0
- package/dist/esm/component/loop.js.map +1 -0
- package/dist/esm/component/recovery.d.ts +24 -0
- package/dist/esm/component/recovery.d.ts.map +1 -0
- package/dist/esm/component/recovery.js +94 -0
- package/dist/esm/component/recovery.js.map +1 -0
- package/dist/esm/component/schema.d.ts +167 -93
- package/dist/esm/component/schema.d.ts.map +1 -1
- package/dist/esm/component/schema.js +56 -65
- package/dist/esm/component/schema.js.map +1 -1
- package/dist/esm/component/shared.d.ts +138 -0
- package/dist/esm/component/shared.d.ts.map +1 -0
- package/dist/esm/component/shared.js +77 -0
- package/dist/esm/component/shared.js.map +1 -0
- package/dist/esm/component/stats.d.ts +6 -3
- package/dist/esm/component/stats.d.ts.map +1 -1
- package/dist/esm/component/stats.js +23 -4
- package/dist/esm/component/stats.js.map +1 -1
- package/dist/esm/component/worker.d.ts +15 -0
- package/dist/esm/component/worker.d.ts.map +1 -0
- package/dist/esm/component/worker.js +73 -0
- package/dist/esm/component/worker.js.map +1 -0
- package/package.json +6 -5
- package/src/client/index.ts +232 -68
- package/src/client/utils.ts +45 -0
- package/src/component/README.md +73 -0
- package/src/component/_generated/api.d.ts +38 -66
- package/src/component/complete.test.ts +508 -0
- package/src/component/complete.ts +98 -0
- package/src/component/convex.config.ts +0 -3
- package/src/component/kick.test.ts +285 -0
- package/src/component/kick.ts +118 -0
- package/src/component/lib.test.ts +448 -0
- package/src/component/lib.ts +105 -667
- package/src/component/logging.ts +24 -12
- package/src/component/loop.test.ts +1204 -0
- package/src/component/loop.ts +637 -0
- package/src/component/recovery.test.ts +541 -0
- package/src/component/recovery.ts +96 -0
- package/src/component/schema.ts +61 -77
- package/src/component/setup.test.ts +5 -0
- package/src/component/shared.ts +141 -0
- package/src/component/stats.ts +26 -8
- package/src/component/worker.ts +81 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
import { convexTest } from "convex-test";
|
|
2
|
+
import {
|
|
3
|
+
describe,
|
|
4
|
+
expect,
|
|
5
|
+
it,
|
|
6
|
+
beforeEach,
|
|
7
|
+
afterEach,
|
|
8
|
+
vi,
|
|
9
|
+
assert,
|
|
10
|
+
} from "vitest";
|
|
11
|
+
import { Id } from "./_generated/dataModel";
|
|
12
|
+
import schema from "./schema";
|
|
13
|
+
import { api } from "./_generated/api";
|
|
14
|
+
|
|
15
|
+
const modules = import.meta.glob("./**/*.ts");
|
|
16
|
+
|
|
17
|
+
// Mock Id type
|
|
18
|
+
type WorkId = Id<"work">;
|
|
19
|
+
|
|
20
|
+
describe("lib", () => {
|
|
21
|
+
async function setupTest() {
|
|
22
|
+
const t = convexTest(schema, modules);
|
|
23
|
+
return t;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let t: Awaited<ReturnType<typeof setupTest>>;
|
|
27
|
+
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
vi.useFakeTimers();
|
|
30
|
+
t = await setupTest();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
vi.useRealTimers();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe("enqueue", () => {
|
|
38
|
+
it("should successfully enqueue a work item", async () => {
|
|
39
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
40
|
+
fnHandle: "testHandle",
|
|
41
|
+
fnName: "testFunction",
|
|
42
|
+
fnArgs: { test: true },
|
|
43
|
+
fnType: "mutation",
|
|
44
|
+
runAt: Date.now(),
|
|
45
|
+
config: {
|
|
46
|
+
maxParallelism: 10,
|
|
47
|
+
logLevel: "INFO",
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(id).toBeDefined();
|
|
52
|
+
const status = await t.query(api.lib.status, { id });
|
|
53
|
+
expect(status).toEqual({ state: "pending", previousAttempts: 0 });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should throw error if maxParallelism is too high", async () => {
|
|
57
|
+
await expect(
|
|
58
|
+
t.mutation(api.lib.enqueue, {
|
|
59
|
+
fnHandle: "testHandle",
|
|
60
|
+
fnName: "testFunction",
|
|
61
|
+
fnArgs: { test: true },
|
|
62
|
+
fnType: "mutation",
|
|
63
|
+
runAt: Date.now(),
|
|
64
|
+
config: {
|
|
65
|
+
maxParallelism: 101, // More than MAX_POSSIBLE_PARALLELISM
|
|
66
|
+
logLevel: "INFO",
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
).rejects.toThrow("maxParallelism must be <= 100");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should throw error if maxParallelism is too low", async () => {
|
|
73
|
+
await expect(
|
|
74
|
+
t.mutation(api.lib.enqueue, {
|
|
75
|
+
fnHandle: "testHandle",
|
|
76
|
+
fnName: "testFunction",
|
|
77
|
+
fnArgs: { test: true },
|
|
78
|
+
fnType: "mutation",
|
|
79
|
+
runAt: Date.now(),
|
|
80
|
+
config: {
|
|
81
|
+
maxParallelism: 0, // Less than minimum
|
|
82
|
+
logLevel: "INFO",
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
).rejects.toThrow("maxParallelism must be >= 1");
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe("cancel", () => {
|
|
90
|
+
it("should successfully queue a work item for cancelation", async () => {
|
|
91
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
92
|
+
fnHandle: "testHandle",
|
|
93
|
+
fnName: "testFunction",
|
|
94
|
+
fnArgs: { test: true },
|
|
95
|
+
fnType: "mutation",
|
|
96
|
+
runAt: Date.now(),
|
|
97
|
+
config: {
|
|
98
|
+
maxParallelism: 10,
|
|
99
|
+
logLevel: "INFO",
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
await t.mutation(api.lib.cancel, {
|
|
104
|
+
id,
|
|
105
|
+
logLevel: "INFO",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Verify a pending cancelation was created
|
|
109
|
+
await t.run(async (ctx) => {
|
|
110
|
+
const pendingCancelations = await ctx.db
|
|
111
|
+
.query("pendingCancelation")
|
|
112
|
+
.collect();
|
|
113
|
+
expect(pendingCancelations).toHaveLength(1);
|
|
114
|
+
expect(pendingCancelations[0].workId).toBe(id);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("should not create duplicate cancelation requests", async () => {
|
|
119
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
120
|
+
fnHandle: "testHandle",
|
|
121
|
+
fnName: "testFunction",
|
|
122
|
+
fnArgs: { test: true },
|
|
123
|
+
fnType: "mutation",
|
|
124
|
+
runAt: Date.now(),
|
|
125
|
+
config: {
|
|
126
|
+
maxParallelism: 10,
|
|
127
|
+
logLevel: "INFO",
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Cancel the first time
|
|
132
|
+
await t.mutation(api.lib.cancel, {
|
|
133
|
+
id,
|
|
134
|
+
logLevel: "INFO",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Cancel the second time
|
|
138
|
+
await t.mutation(api.lib.cancel, {
|
|
139
|
+
id,
|
|
140
|
+
logLevel: "INFO",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Verify only one pending cancelation was created
|
|
144
|
+
await t.run(async (ctx) => {
|
|
145
|
+
const pendingCancelations = await ctx.db
|
|
146
|
+
.query("pendingCancelation")
|
|
147
|
+
.collect();
|
|
148
|
+
expect(pendingCancelations).toHaveLength(1);
|
|
149
|
+
expect(pendingCancelations[0].workId).toBe(id);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should not create cancelation for non-existent work", async () => {
|
|
154
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
155
|
+
fnHandle: "testHandle",
|
|
156
|
+
fnName: "testFunction",
|
|
157
|
+
fnArgs: { test: true },
|
|
158
|
+
fnType: "mutation",
|
|
159
|
+
runAt: Date.now(),
|
|
160
|
+
config: {
|
|
161
|
+
maxParallelism: 10,
|
|
162
|
+
logLevel: "INFO",
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Delete the work item
|
|
167
|
+
await t.run(async (ctx) => {
|
|
168
|
+
await ctx.db.delete(id);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Try to cancel the deleted work
|
|
172
|
+
await t.mutation(api.lib.cancel, {
|
|
173
|
+
id,
|
|
174
|
+
logLevel: "INFO",
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Verify no pending cancelation was created
|
|
178
|
+
await t.run(async (ctx) => {
|
|
179
|
+
const pendingCancelations = await ctx.db
|
|
180
|
+
.query("pendingCancelation")
|
|
181
|
+
.collect();
|
|
182
|
+
expect(pendingCancelations).toHaveLength(0);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("cancelAll", () => {
|
|
188
|
+
it("should queue multiple work items for cancelation", async () => {
|
|
189
|
+
const ids: WorkId[] = [];
|
|
190
|
+
for (let i = 0; i < 3; i++) {
|
|
191
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
192
|
+
fnHandle: "testHandle",
|
|
193
|
+
fnName: "testFunction",
|
|
194
|
+
fnArgs: { test: i },
|
|
195
|
+
fnType: "mutation",
|
|
196
|
+
runAt: Date.now() + 5 * 60 * 1000,
|
|
197
|
+
config: {
|
|
198
|
+
maxParallelism: 10,
|
|
199
|
+
logLevel: "INFO",
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
ids.push(id);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
await t.mutation(api.lib.cancelAll, {
|
|
206
|
+
logLevel: "INFO",
|
|
207
|
+
before: Date.now() + 1000,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Verify pending cancelations were created
|
|
211
|
+
await t.run(async (ctx) => {
|
|
212
|
+
const pendingCancelations = await ctx.db
|
|
213
|
+
.query("pendingCancelation")
|
|
214
|
+
.collect();
|
|
215
|
+
expect(pendingCancelations).toHaveLength(3);
|
|
216
|
+
const canceledIds = pendingCancelations.map((pc) => pc.workId);
|
|
217
|
+
expect(canceledIds).toEqual(expect.arrayContaining(ids));
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("should process work items in batches for cancelAll", async () => {
|
|
222
|
+
const PAGE_SIZE = 64; // Same as in lib.ts
|
|
223
|
+
|
|
224
|
+
// Create PAGE_SIZE + 1 work items to trigger pagination
|
|
225
|
+
for (let i = 0; i < PAGE_SIZE + 1; i++) {
|
|
226
|
+
await t.mutation(api.lib.enqueue, {
|
|
227
|
+
fnHandle: "testHandle",
|
|
228
|
+
fnName: "testFunction",
|
|
229
|
+
fnArgs: { test: i },
|
|
230
|
+
fnType: "mutation",
|
|
231
|
+
runAt: Date.now(),
|
|
232
|
+
config: {
|
|
233
|
+
maxParallelism: 10,
|
|
234
|
+
logLevel: "INFO",
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
await t.mutation(api.lib.cancelAll, {
|
|
240
|
+
logLevel: "INFO",
|
|
241
|
+
before: Date.now() + 1000,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// assert that cancelAll was scheduled
|
|
245
|
+
await t.run(async (ctx) => {
|
|
246
|
+
const scheduledFunctions = await ctx.db.system
|
|
247
|
+
.query("_scheduled_functions")
|
|
248
|
+
.collect();
|
|
249
|
+
expect(scheduledFunctions.length).toBeGreaterThan(0);
|
|
250
|
+
// check that one of the scheduled functions is cancelAll
|
|
251
|
+
const cancelAllScheduledFunction = scheduledFunctions.find(
|
|
252
|
+
(sf) => sf.name === "lib:cancelAll"
|
|
253
|
+
);
|
|
254
|
+
expect(cancelAllScheduledFunction).toBeDefined();
|
|
255
|
+
assert(cancelAllScheduledFunction);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Verify the first page of cancelations was created
|
|
259
|
+
await t.run(async (ctx) => {
|
|
260
|
+
const pendingCancelations = await ctx.db
|
|
261
|
+
.query("pendingCancelation")
|
|
262
|
+
.collect();
|
|
263
|
+
|
|
264
|
+
// We should have at least PAGE_SIZE cancelations
|
|
265
|
+
expect(pendingCancelations.length).toEqual(PAGE_SIZE);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe("status", () => {
|
|
271
|
+
it("should return finished state for non-existent work", async () => {
|
|
272
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
273
|
+
fnHandle: "testHandle",
|
|
274
|
+
fnName: "testFunction",
|
|
275
|
+
fnArgs: { test: true },
|
|
276
|
+
fnType: "mutation",
|
|
277
|
+
runAt: Date.now(),
|
|
278
|
+
config: {
|
|
279
|
+
maxParallelism: 10,
|
|
280
|
+
logLevel: "INFO",
|
|
281
|
+
},
|
|
282
|
+
});
|
|
283
|
+
await t.run(async (ctx) => {
|
|
284
|
+
await ctx.db.delete(id);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const status = await t.query(api.lib.status, { id });
|
|
288
|
+
expect(status).toEqual({ state: "finished" });
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("should return pending state for newly enqueued work", async () => {
|
|
292
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
293
|
+
fnHandle: "testHandle",
|
|
294
|
+
fnName: "testFunction",
|
|
295
|
+
fnArgs: { test: true },
|
|
296
|
+
fnType: "mutation",
|
|
297
|
+
runAt: Date.now(),
|
|
298
|
+
config: {
|
|
299
|
+
maxParallelism: 10,
|
|
300
|
+
logLevel: "INFO",
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Verify work item and pending start were created
|
|
305
|
+
await t.run(async (ctx) => {
|
|
306
|
+
const work = await ctx.db.get(id);
|
|
307
|
+
expect(work).toBeDefined();
|
|
308
|
+
const pendingStarts = await ctx.db.query("pendingStart").collect();
|
|
309
|
+
expect(pendingStarts).toHaveLength(1);
|
|
310
|
+
expect(pendingStarts[0].workId).toBe(id);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const status = await t.query(api.lib.status, { id });
|
|
314
|
+
expect(status).toEqual({ state: "pending", previousAttempts: 0 });
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it("should return running state when work is in progress", async () => {
|
|
318
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
319
|
+
fnHandle: "testHandle",
|
|
320
|
+
fnName: "testFunction",
|
|
321
|
+
fnArgs: { test: true },
|
|
322
|
+
fnType: "mutation",
|
|
323
|
+
runAt: Date.now(),
|
|
324
|
+
config: {
|
|
325
|
+
maxParallelism: 10,
|
|
326
|
+
logLevel: "INFO",
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Delete the pendingStart to simulate work in progress
|
|
331
|
+
await t.run(async (ctx) => {
|
|
332
|
+
const pendingStart = await ctx.db.query("pendingStart").first();
|
|
333
|
+
expect(pendingStart).toBeDefined();
|
|
334
|
+
assert(pendingStart);
|
|
335
|
+
await ctx.db.delete(pendingStart._id);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const status = await t.query(api.lib.status, { id });
|
|
339
|
+
expect(status).toEqual({ state: "running", previousAttempts: 0 });
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("should return pending state for work pending retry", async () => {
|
|
343
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
344
|
+
fnHandle: "testHandle",
|
|
345
|
+
fnName: "testFunction",
|
|
346
|
+
fnArgs: { test: true },
|
|
347
|
+
fnType: "mutation",
|
|
348
|
+
runAt: Date.now(),
|
|
349
|
+
retryBehavior: {
|
|
350
|
+
maxAttempts: 3,
|
|
351
|
+
initialBackoffMs: 100,
|
|
352
|
+
base: 2,
|
|
353
|
+
},
|
|
354
|
+
config: {
|
|
355
|
+
maxParallelism: 10,
|
|
356
|
+
logLevel: "INFO",
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Delete the pendingStart to simulate work in progress
|
|
361
|
+
await t.run(async (ctx) => {
|
|
362
|
+
const pendingStart = await ctx.db.query("pendingStart").first();
|
|
363
|
+
expect(pendingStart).toBeDefined();
|
|
364
|
+
assert(pendingStart);
|
|
365
|
+
await ctx.db.delete(pendingStart._id);
|
|
366
|
+
|
|
367
|
+
// Create a pendingCompletion with retry=true to simulate a failed job that will be retried
|
|
368
|
+
await ctx.db.insert("pendingCompletion", {
|
|
369
|
+
workId: id,
|
|
370
|
+
segment: 1n, // Using a simple segment value for testing
|
|
371
|
+
runResult: { kind: "failed", error: "Test error" },
|
|
372
|
+
retry: true,
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
const status = await t.query(api.lib.status, { id });
|
|
377
|
+
expect(status).toEqual({ state: "pending", previousAttempts: 0 });
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it("should return running state for work with pendingCancelation", async () => {
|
|
381
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
382
|
+
fnHandle: "testHandle",
|
|
383
|
+
fnName: "testFunction",
|
|
384
|
+
fnArgs: { test: true },
|
|
385
|
+
fnType: "mutation",
|
|
386
|
+
runAt: Date.now(),
|
|
387
|
+
config: {
|
|
388
|
+
maxParallelism: 10,
|
|
389
|
+
logLevel: "INFO",
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
// Delete the pendingStart and add pendingCancelation to simulate cancellation in progress
|
|
394
|
+
await t.run(async (ctx) => {
|
|
395
|
+
const pendingStart = await ctx.db.query("pendingStart").first();
|
|
396
|
+
expect(pendingStart).toBeDefined();
|
|
397
|
+
assert(pendingStart);
|
|
398
|
+
await ctx.db.delete(pendingStart._id);
|
|
399
|
+
|
|
400
|
+
// Create a pendingCancelation
|
|
401
|
+
await ctx.db.insert("pendingCancelation", {
|
|
402
|
+
workId: id,
|
|
403
|
+
segment: 1n, // Using a simple segment value for testing
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// According to the implementation, a job with pendingCancelation but no pendingStart
|
|
408
|
+
// or pendingCompletion with retry=true is considered "running"
|
|
409
|
+
const status = await t.query(api.lib.status, { id });
|
|
410
|
+
expect(status).toEqual({ state: "running", previousAttempts: 0 });
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
it("should return running state for work with pendingCompletion but retry=false", async () => {
|
|
414
|
+
const id = await t.mutation(api.lib.enqueue, {
|
|
415
|
+
fnHandle: "testHandle",
|
|
416
|
+
fnName: "testFunction",
|
|
417
|
+
fnArgs: { test: true },
|
|
418
|
+
fnType: "mutation",
|
|
419
|
+
runAt: Date.now(),
|
|
420
|
+
config: {
|
|
421
|
+
maxParallelism: 10,
|
|
422
|
+
logLevel: "INFO",
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Delete the pendingStart and add pendingCompletion with retry=false
|
|
427
|
+
await t.run(async (ctx) => {
|
|
428
|
+
const pendingStart = await ctx.db.query("pendingStart").first();
|
|
429
|
+
expect(pendingStart).toBeDefined();
|
|
430
|
+
assert(pendingStart);
|
|
431
|
+
await ctx.db.delete(pendingStart._id);
|
|
432
|
+
|
|
433
|
+
// Create a pendingCompletion with retry=false
|
|
434
|
+
await ctx.db.insert("pendingCompletion", {
|
|
435
|
+
workId: id,
|
|
436
|
+
segment: 1n, // Using a simple segment value for testing
|
|
437
|
+
runResult: { kind: "failed", error: "Test error" },
|
|
438
|
+
retry: false,
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// According to the implementation, a job with pendingCompletion but retry=false
|
|
443
|
+
// is considered "running"
|
|
444
|
+
const status = await t.query(api.lib.status, { id });
|
|
445
|
+
expect(status).toEqual({ state: "running", previousAttempts: 0 });
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
});
|