velocious 1.0.666 → 1.0.668
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 +40 -11
- package/build/background-jobs/adapter-client.js +22 -0
- package/build/background-jobs/adapter.js +15 -0
- package/build/background-jobs/client.js +147 -1
- package/build/background-jobs/job-semantics.js +30 -0
- package/build/background-jobs/job.js +26 -1
- package/build/background-jobs/local-adapter.js +36 -8
- package/build/background-jobs/local-store.js +410 -39
- package/build/background-jobs/main.js +69 -2
- package/build/background-jobs/platform-job.js +26 -1
- package/build/background-jobs/runtime.js +58 -0
- package/build/background-jobs/store.js +374 -6
- package/build/background-jobs/types.js +27 -2
- package/build/background-jobs/web/controller.js +1 -0
- package/build/configuration.js +30 -6
- package/build/database/drivers/mssql/column.js +2 -2
- package/build/database/drivers/mssql/table.js +19 -5
- package/build/database/pool/async-tracked-multi-connection.js +2 -1
- package/build/database/query/alter-table-base.js +8 -1
- package/build/database/record/attachments/store.js +2 -2
- package/build/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +2 -48
- package/build/environment-handlers/node/cli/commands/test.js +17 -69
- package/build/frontend-model-controller.js +22 -2
- package/build/frontend-models/websocket-channel.js +18 -0
- package/build/http-server/client/websocket-session.js +41 -4
- package/build/http-server/websocket-channel.js +14 -0
- package/build/http-server/websocket-event-log-store.js +85 -12
- package/build/http-server/websocket-events-host.js +17 -7
- package/build/src/background-jobs/adapter-client.d.ts +17 -0
- package/build/src/background-jobs/adapter-client.d.ts.map +1 -1
- package/build/src/background-jobs/adapter-client.js +19 -1
- package/build/src/background-jobs/adapter.d.ts +15 -0
- package/build/src/background-jobs/adapter.d.ts.map +1 -1
- package/build/src/background-jobs/adapter.js +14 -1
- package/build/src/background-jobs/client.d.ts +17 -0
- package/build/src/background-jobs/client.d.ts.map +1 -1
- package/build/src/background-jobs/client.js +129 -1
- package/build/src/background-jobs/job-semantics.d.ts +18 -0
- package/build/src/background-jobs/job-semantics.d.ts.map +1 -1
- package/build/src/background-jobs/job-semantics.js +28 -1
- package/build/src/background-jobs/job.d.ts +15 -0
- package/build/src/background-jobs/job.d.ts.map +1 -1
- package/build/src/background-jobs/job.js +21 -2
- package/build/src/background-jobs/local-adapter.d.ts +23 -8
- package/build/src/background-jobs/local-adapter.d.ts.map +1 -1
- package/build/src/background-jobs/local-adapter.js +35 -9
- package/build/src/background-jobs/local-store.d.ts +120 -5
- package/build/src/background-jobs/local-store.d.ts.map +1 -1
- package/build/src/background-jobs/local-store.js +366 -40
- package/build/src/background-jobs/main.d.ts +24 -2
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +68 -3
- package/build/src/background-jobs/platform-job.d.ts +15 -0
- package/build/src/background-jobs/platform-job.d.ts.map +1 -1
- package/build/src/background-jobs/platform-job.js +19 -2
- package/build/src/background-jobs/runtime.d.ts +39 -0
- package/build/src/background-jobs/runtime.d.ts.map +1 -1
- package/build/src/background-jobs/runtime.js +49 -1
- package/build/src/background-jobs/store.d.ts +100 -1
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +334 -8
- package/build/src/background-jobs/types.d.ts +97 -4
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +28 -3
- package/build/src/background-jobs/web/controller.d.ts.map +1 -1
- package/build/src/background-jobs/web/controller.js +2 -1
- package/build/src/configuration.d.ts +18 -1
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +28 -6
- package/build/src/database/drivers/mssql/column.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/column.js +2 -2
- package/build/src/database/drivers/mssql/table.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/table.js +19 -5
- package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
- package/build/src/database/pool/async-tracked-multi-connection.js +3 -2
- package/build/src/database/query/alter-table-base.d.ts.map +1 -1
- package/build/src/database/query/alter-table-base.js +9 -2
- package/build/src/database/record/attachments/store.js +3 -3
- package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +3 -47
- package/build/src/environment-handlers/node/cli/commands/test.d.ts +2 -28
- package/build/src/environment-handlers/node/cli/commands/test.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/test.js +10 -61
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +18 -3
- package/build/src/frontend-models/websocket-channel.d.ts +9 -0
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +16 -1
- package/build/src/http-server/client/websocket-session.d.ts +25 -2
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +40 -5
- package/build/src/http-server/websocket-channel.d.ts +11 -0
- package/build/src/http-server/websocket-channel.d.ts.map +1 -1
- package/build/src/http-server/websocket-channel.js +14 -1
- package/build/src/http-server/websocket-event-log-store.d.ts +60 -41
- package/build/src/http-server/websocket-event-log-store.d.ts.map +1 -1
- package/build/src/http-server/websocket-event-log-store.js +79 -12
- package/build/src/http-server/websocket-events-host.d.ts +10 -3
- package/build/src/http-server/websocket-events-host.d.ts.map +1 -1
- package/build/src/http-server/websocket-events-host.js +17 -8
- package/build/src/testing/test-files-finder.d.ts +17 -117
- package/build/src/testing/test-files-finder.d.ts.map +1 -1
- package/build/src/testing/test-files-finder.js +29 -294
- package/build/src/testing/test-filter-parser.d.ts +32 -22
- package/build/src/testing/test-filter-parser.d.ts.map +1 -1
- package/build/src/testing/test-filter-parser.js +25 -217
- package/build/src/testing/test-profile-activity.d.ts +1 -6
- package/build/src/testing/test-profile-activity.d.ts.map +1 -1
- package/build/src/testing/test-profile-activity.js +2 -12
- package/build/src/testing/test-profile-output.d.ts +1 -40
- package/build/src/testing/test-profile-output.d.ts.map +1 -1
- package/build/src/testing/test-profile-output.js +2 -217
- package/build/src/testing/test-profiler.d.ts +15 -659
- package/build/src/testing/test-profiler.d.ts.map +1 -1
- package/build/src/testing/test-profiler.js +35 -833
- package/build/src/testing/test-runner.d.ts +10 -1
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +26 -4
- package/build/src/testing/test-suite-splitter.d.ts +1 -128
- package/build/src/testing/test-suite-splitter.d.ts.map +1 -1
- package/build/src/testing/test-suite-splitter.js +2 -259
- package/build/src/testing/timing-manifest.d.ts +5 -93
- package/build/src/testing/timing-manifest.d.ts.map +1 -1
- package/build/src/testing/timing-manifest.js +4 -283
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -1
- package/build/src/testing/velocious-attempt-executor.js +8 -2
- package/build/testing/test-files-finder.js +32 -343
- package/build/testing/test-filter-parser.js +29 -248
- package/build/testing/test-profile-activity.js +1 -12
- package/build/testing/test-profile-output.js +8 -253
- package/build/testing/test-profiler.js +38 -895
- package/build/testing/test-runner.js +33 -9
- package/build/testing/test-suite-splitter.js +1 -301
- package/build/testing/timing-manifest.js +10 -344
- package/build/testing/velocious-attempt-executor.js +7 -1
- package/package.json +3 -3
- package/scripts/test-browser.js +30 -6
- package/scripts/verify-docker-dev-environment.js +13 -1
- package/src/background-jobs/adapter-client.js +22 -0
- package/src/background-jobs/adapter.js +15 -0
- package/src/background-jobs/client.js +147 -1
- package/src/background-jobs/job-semantics.js +30 -0
- package/src/background-jobs/job.js +26 -1
- package/src/background-jobs/local-adapter.js +36 -8
- package/src/background-jobs/local-store.js +410 -39
- package/src/background-jobs/main.js +69 -2
- package/src/background-jobs/platform-job.js +26 -1
- package/src/background-jobs/runtime.js +58 -0
- package/src/background-jobs/store.js +374 -6
- package/src/background-jobs/types.js +27 -2
- package/src/background-jobs/web/controller.js +1 -0
- package/src/configuration.js +30 -6
- package/src/database/drivers/mssql/column.js +2 -2
- package/src/database/drivers/mssql/table.js +19 -5
- package/src/database/pool/async-tracked-multi-connection.js +2 -1
- package/src/database/query/alter-table-base.js +8 -1
- package/src/database/record/attachments/store.js +2 -2
- package/src/environment-handlers/node/cli/commands/test/timing-manifest/merge.js +2 -48
- package/src/environment-handlers/node/cli/commands/test.js +17 -69
- package/src/frontend-model-controller.js +22 -2
- package/src/frontend-models/websocket-channel.js +18 -0
- package/src/http-server/client/websocket-session.js +41 -4
- package/src/http-server/websocket-channel.js +14 -0
- package/src/http-server/websocket-event-log-store.js +85 -12
- package/src/http-server/websocket-events-host.js +17 -7
- package/src/testing/test-files-finder.js +32 -343
- package/src/testing/test-filter-parser.js +29 -248
- package/src/testing/test-profile-activity.js +1 -12
- package/src/testing/test-profile-output.js +8 -253
- package/src/testing/test-profiler.js +38 -895
- package/src/testing/test-runner.js +33 -9
- package/src/testing/test-suite-splitter.js +1 -301
- package/src/testing/timing-manifest.js +10 -344
- package/src/testing/velocious-attempt-executor.js +7 -1
|
@@ -5,31 +5,39 @@ import UUID from "pure-uuid"
|
|
|
5
5
|
import TableData from "../database/table-data/index.js"
|
|
6
6
|
import TableIndex from "../database/table-data/table-index.js"
|
|
7
7
|
import sha256Hex from "../utils/sha256-hex.js"
|
|
8
|
+
import VelociousError from "../velocious-error.js"
|
|
8
9
|
import normalizeBackgroundJobError from "./normalize-error.js"
|
|
9
10
|
import {
|
|
11
|
+
BACKGROUND_JOB_TERMINAL_STATUSES,
|
|
10
12
|
DEFAULT_BACKGROUND_JOB_QUEUE,
|
|
11
13
|
QUEUE_CONCURRENCY_KEY_PREFIX,
|
|
12
14
|
normalizeBackgroundJobConcurrency,
|
|
13
15
|
normalizeBackgroundJobExecutionMode,
|
|
14
16
|
normalizeBackgroundJobMaxRetries,
|
|
15
17
|
normalizeBackgroundJobQueue,
|
|
18
|
+
normalizeBackgroundJobScheduleKey,
|
|
16
19
|
normalizeBackgroundJobScheduledAtMs,
|
|
20
|
+
normalizeBackgroundJobStatus,
|
|
17
21
|
rescheduledBackgroundJobAtMs,
|
|
18
22
|
retryDelayMs
|
|
19
23
|
} from "./job-semantics.js"
|
|
20
24
|
|
|
21
25
|
export const LOCAL_BACKGROUND_JOBS_TABLE = "velocious_local_background_jobs"
|
|
22
26
|
export const LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE = "velocious_local_background_job_concurrency"
|
|
27
|
+
export const LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE = "velocious_local_background_job_schedule_keys"
|
|
23
28
|
const MIGRATIONS_TABLE = "velocious_internal_migrations"
|
|
24
29
|
const MIGRATION_SCOPE = "local_background_jobs"
|
|
25
|
-
const
|
|
30
|
+
const MIGRATION_VERSIONS = ["1", "2", "3"]
|
|
26
31
|
const LOCAL_EXECUTION_MODES = [/** @type {const} */ ("inline")]
|
|
27
32
|
export const LOCAL_BACKGROUND_JOBS_INDEX_NAMES = [
|
|
28
33
|
"index_velocious_local_background_jobs_due",
|
|
29
34
|
"index_velocious_local_background_jobs_queue_status",
|
|
30
35
|
"index_velocious_local_background_jobs_deduplication",
|
|
31
|
-
"index_velocious_local_background_jobs_concurrency"
|
|
36
|
+
"index_velocious_local_background_jobs_concurrency",
|
|
37
|
+
"index_velocious_local_background_jobs_schedule_history",
|
|
38
|
+
"index_velocious_local_background_jobs_schedule_order"
|
|
32
39
|
]
|
|
40
|
+
export const LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_INDEX_NAMES = ["index_velocious_local_background_job_schedule_keys_job"]
|
|
33
41
|
const EXPECTED_JOB_COLUMNS = [
|
|
34
42
|
"id",
|
|
35
43
|
"job_name",
|
|
@@ -37,6 +45,8 @@ const EXPECTED_JOB_COLUMNS = [
|
|
|
37
45
|
"args_digest",
|
|
38
46
|
"execution_mode",
|
|
39
47
|
"queue",
|
|
48
|
+
"schedule_key",
|
|
49
|
+
"schedule_order",
|
|
40
50
|
"max_retries",
|
|
41
51
|
"attempts",
|
|
42
52
|
"status",
|
|
@@ -56,6 +66,7 @@ const EXPECTED_JOB_COLUMNS = [
|
|
|
56
66
|
"child_pid"
|
|
57
67
|
]
|
|
58
68
|
const EXPECTED_CONCURRENCY_COLUMNS = ["concurrency_key", "max_concurrency", "active_count"]
|
|
69
|
+
const EXPECTED_SCHEDULE_KEY_COLUMNS = ["schedule_key", "job_id"]
|
|
59
70
|
/** @type {WeakMap<import("../configuration.js").default, Map<string, Promise<void>>>} */
|
|
60
71
|
const deduplicatedEnqueueChains = new WeakMap()
|
|
61
72
|
|
|
@@ -190,7 +201,8 @@ export default class LocalBackgroundJobsStore {
|
|
|
190
201
|
}
|
|
191
202
|
|
|
192
203
|
/**
|
|
193
|
-
* Creates or
|
|
204
|
+
* Creates or upgrades the versioned local tables and indexes without
|
|
205
|
+
* rebuilding persisted queue data.
|
|
194
206
|
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
195
207
|
* @returns {Promise<boolean>} - Whether schema state changed.
|
|
196
208
|
*/
|
|
@@ -217,20 +229,19 @@ export default class LocalBackgroundJobsStore {
|
|
|
217
229
|
await this._assertColumns(db, LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE, EXPECTED_CONCURRENCY_COLUMNS)
|
|
218
230
|
}
|
|
219
231
|
|
|
232
|
+
if (!(await db.tableExists(LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE))) {
|
|
233
|
+
await db.createTable(this._scheduleKeysTableData())
|
|
234
|
+
changed = true
|
|
235
|
+
} else {
|
|
236
|
+
await this._assertColumns(db, LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE, EXPECTED_SCHEDULE_KEY_COLUMNS)
|
|
237
|
+
}
|
|
238
|
+
|
|
220
239
|
if (await this._ensureIndexes(db)) changed = true
|
|
221
240
|
|
|
222
|
-
|
|
223
|
-
await
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
applied_at_ms: this.clock.now(),
|
|
227
|
-
key: this._migrationKey(),
|
|
228
|
-
scope: MIGRATION_SCOPE,
|
|
229
|
-
version: MIGRATION_VERSION
|
|
230
|
-
},
|
|
231
|
-
conflictColumns: ["key"],
|
|
232
|
-
updateColumns: ["scope", "version", "applied_at_ms"]
|
|
233
|
-
})
|
|
241
|
+
for (const version of MIGRATION_VERSIONS) {
|
|
242
|
+
if (await this._hasMigration(db, version)) continue
|
|
243
|
+
|
|
244
|
+
await this._recordMigration(db, version)
|
|
234
245
|
changed = true
|
|
235
246
|
}
|
|
236
247
|
|
|
@@ -303,6 +314,8 @@ export default class LocalBackgroundJobsStore {
|
|
|
303
314
|
table.string("args_digest", {maxLength: 64, null: false})
|
|
304
315
|
table.string("execution_mode", {null: false})
|
|
305
316
|
table.string("queue", {null: false})
|
|
317
|
+
table.string("schedule_key", {null: true})
|
|
318
|
+
table.bigint("schedule_order", {null: true})
|
|
306
319
|
table.integer("max_retries", {null: false})
|
|
307
320
|
table.integer("attempts", {null: false})
|
|
308
321
|
table.string("status", {null: false})
|
|
@@ -324,6 +337,8 @@ export default class LocalBackgroundJobsStore {
|
|
|
324
337
|
table.addIndex(new TableIndex(["queue", "status", "created_at_ms"], {name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[1]}))
|
|
325
338
|
table.addIndex(new TableIndex(["args_digest"], {name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[2]}))
|
|
326
339
|
table.addIndex(new TableIndex(["status", "concurrency_key", "scheduled_at_ms"], {name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[3]}))
|
|
340
|
+
table.addIndex(new TableIndex(["schedule_key", "created_at_ms", "id"], {name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[4]}))
|
|
341
|
+
table.addIndex(new TableIndex(["schedule_key", "schedule_order", "created_at_ms", "id"], {name: LOCAL_BACKGROUND_JOBS_INDEX_NAMES[5]}))
|
|
327
342
|
return table
|
|
328
343
|
}
|
|
329
344
|
|
|
@@ -340,6 +355,19 @@ export default class LocalBackgroundJobsStore {
|
|
|
340
355
|
return table
|
|
341
356
|
}
|
|
342
357
|
|
|
358
|
+
/**
|
|
359
|
+
* Builds the stable schedule-owner table definition.
|
|
360
|
+
* @returns {TableData} - Stable owner table definition.
|
|
361
|
+
*/
|
|
362
|
+
_scheduleKeysTableData() {
|
|
363
|
+
const table = new TableData(LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE, {ifNotExists: true})
|
|
364
|
+
|
|
365
|
+
table.string("schedule_key", {null: false, primaryKey: true})
|
|
366
|
+
table.string("job_id", {null: false})
|
|
367
|
+
table.addIndex(new TableIndex(["job_id"], {name: LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_INDEX_NAMES[0]}))
|
|
368
|
+
return table
|
|
369
|
+
}
|
|
370
|
+
|
|
343
371
|
/**
|
|
344
372
|
* Rejects an incompatible current-version table rather than rebuilding data.
|
|
345
373
|
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
@@ -368,25 +396,33 @@ export default class LocalBackgroundJobsStore {
|
|
|
368
396
|
*/
|
|
369
397
|
async _ensureIndexes(db) {
|
|
370
398
|
db.clearSchemaCache()
|
|
371
|
-
const jobsTable = await db.getTableByNameOrFail(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
372
|
-
const existingNames = new Set((await jobsTable.getIndexes()).map((index) => index.getName()))
|
|
373
399
|
let changed = false
|
|
400
|
+
/** @type {Array<[string, TableData]>} */
|
|
401
|
+
const definitions = [
|
|
402
|
+
[LOCAL_BACKGROUND_JOBS_TABLE, this._jobsTableData()],
|
|
403
|
+
[LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE, this._scheduleKeysTableData()]
|
|
404
|
+
]
|
|
405
|
+
|
|
406
|
+
for (const [tableName, tableData] of definitions) {
|
|
407
|
+
const table = await db.getTableByNameOrFail(tableName)
|
|
408
|
+
const existingNames = new Set((await table.getIndexes()).map((index) => index.getName()))
|
|
409
|
+
|
|
410
|
+
for (const index of tableData.getIndexes()) {
|
|
411
|
+
const indexName = index.getName()
|
|
412
|
+
|
|
413
|
+
if (!indexName || existingNames.has(indexName)) continue
|
|
414
|
+
|
|
415
|
+
const sqls = await db.createIndexSQLs({
|
|
416
|
+
columns: index.getColumns(),
|
|
417
|
+
ifNotExists: true,
|
|
418
|
+
name: indexName,
|
|
419
|
+
tableName,
|
|
420
|
+
unique: index.getUnique()
|
|
421
|
+
})
|
|
374
422
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (!indexName || existingNames.has(indexName)) continue
|
|
379
|
-
|
|
380
|
-
const sqls = await db.createIndexSQLs({
|
|
381
|
-
columns: index.getColumns(),
|
|
382
|
-
ifNotExists: true,
|
|
383
|
-
name: indexName,
|
|
384
|
-
tableName: LOCAL_BACKGROUND_JOBS_TABLE,
|
|
385
|
-
unique: index.getUnique()
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
for (const sql of sqls) await db.query(sql)
|
|
389
|
-
changed = true
|
|
423
|
+
for (const sql of sqls) await db.query(sql)
|
|
424
|
+
changed = true
|
|
425
|
+
}
|
|
390
426
|
}
|
|
391
427
|
|
|
392
428
|
if (changed) db.clearSchemaCache()
|
|
@@ -396,24 +432,46 @@ export default class LocalBackgroundJobsStore {
|
|
|
396
432
|
/**
|
|
397
433
|
* Checks whether the current local schema version is recorded.
|
|
398
434
|
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
399
|
-
* @
|
|
435
|
+
* @param {string} version - Local schema version.
|
|
436
|
+
* @returns {Promise<boolean>} - Whether the version is recorded.
|
|
400
437
|
*/
|
|
401
|
-
async _hasMigration(db) {
|
|
438
|
+
async _hasMigration(db, version) {
|
|
402
439
|
const rows = await db
|
|
403
440
|
.newQuery()
|
|
404
441
|
.from(MIGRATIONS_TABLE)
|
|
405
|
-
.where({key: this._migrationKey()})
|
|
442
|
+
.where({key: this._migrationKey(version)})
|
|
406
443
|
.limit(1)
|
|
407
444
|
.results()
|
|
408
445
|
|
|
409
446
|
return rows.length > 0
|
|
410
447
|
}
|
|
411
448
|
|
|
449
|
+
/**
|
|
450
|
+
* Records one local schema version after its additive changes are present.
|
|
451
|
+
* @param {import("../database/drivers/base.js").default} db - Connection.
|
|
452
|
+
* @param {string} version - Local schema version.
|
|
453
|
+
* @returns {Promise<void>} - Resolves after recording.
|
|
454
|
+
*/
|
|
455
|
+
async _recordMigration(db, version) {
|
|
456
|
+
await db.upsert({
|
|
457
|
+
tableName: MIGRATIONS_TABLE,
|
|
458
|
+
data: {
|
|
459
|
+
applied_at_ms: this.clock.now(),
|
|
460
|
+
key: this._migrationKey(version),
|
|
461
|
+
scope: MIGRATION_SCOPE,
|
|
462
|
+
version
|
|
463
|
+
},
|
|
464
|
+
conflictColumns: ["key"],
|
|
465
|
+
updateColumns: ["scope", "version", "applied_at_ms"]
|
|
466
|
+
})
|
|
467
|
+
}
|
|
468
|
+
|
|
412
469
|
/**
|
|
413
470
|
* Builds the scoped migration key.
|
|
471
|
+
* @param {string} version - Local schema version.
|
|
414
472
|
* @returns {string} - Scoped migration key.
|
|
415
473
|
*/
|
|
416
|
-
_migrationKey() { return `${MIGRATION_SCOPE}:${
|
|
474
|
+
_migrationKey(version) { return `${MIGRATION_SCOPE}:${version}` }
|
|
417
475
|
|
|
418
476
|
/**
|
|
419
477
|
* Enqueues a local job in the caller's active transaction when present.
|
|
@@ -469,6 +527,180 @@ export default class LocalBackgroundJobsStore {
|
|
|
469
527
|
return await mutate()
|
|
470
528
|
}
|
|
471
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Replaces the queued owner of a stable schedule key with a new local job.
|
|
532
|
+
* A handed-off owner remains runnable but is detached from future ownership.
|
|
533
|
+
* @param {object} args - Replacement request.
|
|
534
|
+
* @param {string} args.scheduleKey - Stable logical schedule key.
|
|
535
|
+
* @param {string} args.jobName - Registered job name.
|
|
536
|
+
* @param {Array<ReturnType<typeof JSON.parse>>} args.args - Serialized job arguments.
|
|
537
|
+
* @param {import("./types.js").BackgroundJobOptions} [args.options] - Job options.
|
|
538
|
+
* @returns {Promise<import("./types.js").BackgroundJobReplacementResult>} - Replacement result.
|
|
539
|
+
*/
|
|
540
|
+
async replaceScheduled({scheduleKey, jobName, args, options = {}}) {
|
|
541
|
+
await this.ensureReady()
|
|
542
|
+
|
|
543
|
+
const normalizedScheduleKey = normalizeBackgroundJobScheduleKey(scheduleKey)
|
|
544
|
+
const preparedJob = this._prepareJob({args, jobName, options})
|
|
545
|
+
|
|
546
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
547
|
+
await this._lockScheduleKey(db, normalizedScheduleKey)
|
|
548
|
+
const ownerJob = await this._scheduledOwnerJob(db, normalizedScheduleKey)
|
|
549
|
+
/** @type {import("./types.js").BackgroundJobReplacementPreviousStatus} */
|
|
550
|
+
let previousStatus = null
|
|
551
|
+
let previousJobId = null
|
|
552
|
+
|
|
553
|
+
if (ownerJob?.status === "queued") {
|
|
554
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
555
|
+
conditions: {id: ownerJob.id, status: "queued"},
|
|
556
|
+
data: {status: "cancelled"},
|
|
557
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
558
|
+
})
|
|
559
|
+
|
|
560
|
+
if (affectedRows === 1) {
|
|
561
|
+
previousJobId = ownerJob.id
|
|
562
|
+
previousStatus = "queued"
|
|
563
|
+
} else {
|
|
564
|
+
const currentOwnerJob = await this._getJob(db, ownerJob.id)
|
|
565
|
+
|
|
566
|
+
if (currentOwnerJob?.status === "handed_off") {
|
|
567
|
+
previousJobId = currentOwnerJob.id
|
|
568
|
+
previousStatus = "handed_off"
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
} else if (ownerJob?.status === "handed_off") {
|
|
572
|
+
previousJobId = ownerJob.id
|
|
573
|
+
previousStatus = "handed_off"
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const scheduleOrder = await this._nextScheduleOrder(db, normalizedScheduleKey)
|
|
577
|
+
|
|
578
|
+
if (preparedJob.concurrency) await this._ensureConcurrency(db, preparedJob.concurrency)
|
|
579
|
+
await this._insertPreparedJob(db, preparedJob, normalizedScheduleKey, scheduleOrder)
|
|
580
|
+
await db.upsert({
|
|
581
|
+
conflictColumns: ["schedule_key"],
|
|
582
|
+
data: {job_id: preparedJob.jobId, schedule_key: normalizedScheduleKey},
|
|
583
|
+
tableName: LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE,
|
|
584
|
+
updateColumns: ["job_id"]
|
|
585
|
+
})
|
|
586
|
+
await this._wakeDispatcherAfterCommit(db)
|
|
587
|
+
|
|
588
|
+
return {jobId: preparedJob.jobId, previousJobId, previousStatus}
|
|
589
|
+
}))
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Cancels a queued stable owner or detaches an active handoff truthfully.
|
|
594
|
+
* @param {string} scheduleKey - Stable logical schedule key.
|
|
595
|
+
* @returns {Promise<import("./types.js").BackgroundJobCancellationResult>} - Cancellation result.
|
|
596
|
+
*/
|
|
597
|
+
async cancelScheduled(scheduleKey) {
|
|
598
|
+
await this.ensureReady()
|
|
599
|
+
|
|
600
|
+
const normalizedScheduleKey = normalizeBackgroundJobScheduleKey(scheduleKey)
|
|
601
|
+
|
|
602
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
603
|
+
await this._lockScheduleKey(db, normalizedScheduleKey)
|
|
604
|
+
const ownerJob = await this._scheduledOwnerJob(db, normalizedScheduleKey)
|
|
605
|
+
|
|
606
|
+
if (!ownerJob) {
|
|
607
|
+
await this._releaseScheduleOwnership(db, {jobId: null, scheduleKey: normalizedScheduleKey})
|
|
608
|
+
return {jobId: null, outcome: "not_found"}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (ownerJob.status === "queued") {
|
|
612
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
613
|
+
conditions: {id: ownerJob.id, status: "queued"},
|
|
614
|
+
data: {status: "cancelled"},
|
|
615
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
616
|
+
})
|
|
617
|
+
|
|
618
|
+
if (affectedRows === 1) {
|
|
619
|
+
await this._releaseScheduleOwnership(db, {jobId: ownerJob.id, scheduleKey: normalizedScheduleKey})
|
|
620
|
+
await this._wakeDispatcherAfterCommit(db)
|
|
621
|
+
return {jobId: ownerJob.id, outcome: "cancelled"}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const currentJob = await this._scheduledOwnerJob(db, normalizedScheduleKey)
|
|
626
|
+
|
|
627
|
+
await this._releaseScheduleOwnership(db, {jobId: ownerJob.id, scheduleKey: normalizedScheduleKey})
|
|
628
|
+
await this._wakeDispatcherAfterCommit(db)
|
|
629
|
+
if (currentJob?.status === "handed_off") return {jobId: currentJob.id, outcome: "handed_off"}
|
|
630
|
+
return {jobId: null, outcome: "not_found"}
|
|
631
|
+
}))
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Reads stable ownership and optional latest terminal history in one transaction.
|
|
636
|
+
* @param {string} scheduleKey - Stable logical schedule key.
|
|
637
|
+
* @param {{includeLatestTerminal?: boolean}} [options] - Lookup options.
|
|
638
|
+
* @returns {Promise<import("./types.js").BackgroundJobScheduledLookupResult>} - Normalized local jobs.
|
|
639
|
+
*/
|
|
640
|
+
async getScheduledJob(scheduleKey, {includeLatestTerminal = false} = {}) {
|
|
641
|
+
await this.ensureReady()
|
|
642
|
+
|
|
643
|
+
const normalizedScheduleKey = normalizeBackgroundJobScheduleKey(scheduleKey)
|
|
644
|
+
|
|
645
|
+
if (typeof includeLatestTerminal !== "boolean") {
|
|
646
|
+
throw VelociousError.safe("background job includeLatestTerminal must be a boolean")
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
650
|
+
return await this._scheduledJobLookup(db, {
|
|
651
|
+
includeLatestTerminal,
|
|
652
|
+
scheduleKey: normalizedScheduleKey
|
|
653
|
+
})
|
|
654
|
+
}))
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Moves only a future queued stable owner to the current time.
|
|
659
|
+
* @param {string} scheduleKey - Stable logical schedule key.
|
|
660
|
+
* @returns {Promise<import("./types.js").BackgroundJobWakeResult>} - Exact wake outcome.
|
|
661
|
+
*/
|
|
662
|
+
async wakeScheduled(scheduleKey) {
|
|
663
|
+
await this.ensureReady()
|
|
664
|
+
|
|
665
|
+
const normalizedScheduleKey = normalizeBackgroundJobScheduleKey(scheduleKey)
|
|
666
|
+
|
|
667
|
+
return await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
668
|
+
await this._lockScheduleKey(db, normalizedScheduleKey)
|
|
669
|
+
const job = await this._scheduledOwnerJob(db, normalizedScheduleKey)
|
|
670
|
+
|
|
671
|
+
if (!job || (job.status !== "queued" && job.status !== "handed_off")) return {jobId: null, outcome: "not_found"}
|
|
672
|
+
if (job.status === "handed_off") return {jobId: job.id, outcome: "handed_off"}
|
|
673
|
+
|
|
674
|
+
const nowMs = this.clock.now()
|
|
675
|
+
|
|
676
|
+
if (Number(job.scheduledAtMs) <= nowMs) {
|
|
677
|
+
await this._wakeDispatcherAfterCommit(db)
|
|
678
|
+
return {jobId: job.id, outcome: "already_due"}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const affectedRows = await this._updateAffectedRows(db, {
|
|
682
|
+
conditions: {id: job.id, scheduled_at_ms: job.scheduledAtMs, status: "queued"},
|
|
683
|
+
data: {scheduled_at_ms: nowMs},
|
|
684
|
+
tableName: LOCAL_BACKGROUND_JOBS_TABLE
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
if (affectedRows === 1) {
|
|
688
|
+
await this._wakeDispatcherAfterCommit(db)
|
|
689
|
+
return {jobId: job.id, outcome: "woken"}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const currentJob = await this._scheduledOwnerJob(db, normalizedScheduleKey)
|
|
693
|
+
|
|
694
|
+
if (currentJob?.status === "handed_off") return {jobId: currentJob.id, outcome: "handed_off"}
|
|
695
|
+
if (currentJob?.status === "queued" && Number(currentJob.scheduledAtMs) <= nowMs) {
|
|
696
|
+
await this._wakeDispatcherAfterCommit(db)
|
|
697
|
+
return {jobId: currentJob.id, outcome: "already_due"}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return {jobId: null, outcome: "not_found"}
|
|
701
|
+
}))
|
|
702
|
+
}
|
|
703
|
+
|
|
472
704
|
/**
|
|
473
705
|
* Serializes matching in-process deduplication checks through commit while
|
|
474
706
|
* leaving unrelated job identities independent.
|
|
@@ -559,9 +791,11 @@ export default class LocalBackgroundJobsStore {
|
|
|
559
791
|
* Inserts one prepared local job row and its concurrency metadata.
|
|
560
792
|
* @param {import("../database/drivers/base.js").default} db - Local SQLite connection.
|
|
561
793
|
* @param {import("./types.js").PreparedLocalBackgroundJob} preparedJob - Prepared row data.
|
|
794
|
+
* @param {string | null} [scheduleKey] - Stable schedule history key.
|
|
795
|
+
* @param {number | null} [scheduleOrder] - Monotonic stable ownership order.
|
|
562
796
|
* @returns {Promise<void>} - Resolves after insertion.
|
|
563
797
|
*/
|
|
564
|
-
async _insertPreparedJob(db, preparedJob) {
|
|
798
|
+
async _insertPreparedJob(db, preparedJob, scheduleKey = null, scheduleOrder = null) {
|
|
565
799
|
await db.insert({
|
|
566
800
|
tableName: LOCAL_BACKGROUND_JOBS_TABLE,
|
|
567
801
|
data: {
|
|
@@ -581,6 +815,8 @@ export default class LocalBackgroundJobsStore {
|
|
|
581
815
|
max_concurrency: preparedJob.concurrency?.maxConcurrency || null,
|
|
582
816
|
max_retries: preparedJob.maxRetries,
|
|
583
817
|
queue: preparedJob.queue,
|
|
818
|
+
schedule_key: scheduleKey,
|
|
819
|
+
schedule_order: scheduleOrder,
|
|
584
820
|
scheduled_at_ms: preparedJob.scheduledAtMs,
|
|
585
821
|
status: "queued",
|
|
586
822
|
worker_id: null
|
|
@@ -847,6 +1083,7 @@ export default class LocalBackgroundJobsStore {
|
|
|
847
1083
|
|
|
848
1084
|
if (affectedRows !== 1) return false
|
|
849
1085
|
await this._releaseConcurrency(db, job.concurrencyKey)
|
|
1086
|
+
await this._releaseScheduleOwnershipForJob(db, job)
|
|
850
1087
|
return true
|
|
851
1088
|
}))
|
|
852
1089
|
}
|
|
@@ -976,6 +1213,7 @@ export default class LocalBackgroundJobsStore {
|
|
|
976
1213
|
async clearAll() {
|
|
977
1214
|
await this.ensureReady()
|
|
978
1215
|
await this._withDb(async (connection) => await this._mutate(connection, async (db) => {
|
|
1216
|
+
await db.query(`DELETE FROM ${db.quoteTable(LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE)}`)
|
|
979
1217
|
await db.query(`DELETE FROM ${db.quoteTable(LOCAL_BACKGROUND_JOBS_TABLE)}`)
|
|
980
1218
|
await db.query(`DELETE FROM ${db.quoteTable(LOCAL_BACKGROUND_JOB_CONCURRENCY_TABLE)}`)
|
|
981
1219
|
}))
|
|
@@ -1020,6 +1258,7 @@ export default class LocalBackgroundJobsStore {
|
|
|
1020
1258
|
|
|
1021
1259
|
if (affectedRows !== 1) return null
|
|
1022
1260
|
await this._releaseConcurrency(db, job.concurrencyKey)
|
|
1261
|
+
if (!willRetry) await this._releaseScheduleOwnershipForJob(db, job)
|
|
1023
1262
|
|
|
1024
1263
|
return {
|
|
1025
1264
|
...job,
|
|
@@ -1197,6 +1436,137 @@ export default class LocalBackgroundJobsStore {
|
|
|
1197
1436
|
return rows[0] ? this._normalizeRow(rows[0]) : null
|
|
1198
1437
|
}
|
|
1199
1438
|
|
|
1439
|
+
/**
|
|
1440
|
+
* Reads the job currently named by one stable owner row.
|
|
1441
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
1442
|
+
* @param {string} scheduleKey - Validated stable schedule key.
|
|
1443
|
+
* @returns {Promise<import("./types.js").BackgroundJobRow | null>} - Normalized owner job.
|
|
1444
|
+
*/
|
|
1445
|
+
async _scheduledOwnerJob(db, scheduleKey) {
|
|
1446
|
+
const ownerRows = await db
|
|
1447
|
+
.newQuery()
|
|
1448
|
+
.from(LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE)
|
|
1449
|
+
.where({schedule_key: scheduleKey})
|
|
1450
|
+
.limit(1)
|
|
1451
|
+
.results()
|
|
1452
|
+
const ownerRow = ownerRows[0]
|
|
1453
|
+
|
|
1454
|
+
if (!ownerRow) return null
|
|
1455
|
+
|
|
1456
|
+
return await this._getJob(db, String(ownerRow.job_id))
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* Assigns the next ownership order after SQLite write serialization is held.
|
|
1461
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
1462
|
+
* @param {string} scheduleKey - Validated stable schedule key.
|
|
1463
|
+
* @returns {Promise<number>} - Next monotonic ownership order.
|
|
1464
|
+
*/
|
|
1465
|
+
async _nextScheduleOrder(db, scheduleKey) {
|
|
1466
|
+
const rows = await db
|
|
1467
|
+
.newQuery()
|
|
1468
|
+
.from(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
1469
|
+
.select("schedule_order")
|
|
1470
|
+
.where({schedule_key: scheduleKey})
|
|
1471
|
+
.where(`${db.quoteColumn("schedule_order")} IS NOT NULL`)
|
|
1472
|
+
.order("schedule_order DESC")
|
|
1473
|
+
.limit(1)
|
|
1474
|
+
.results()
|
|
1475
|
+
const currentOrder = this._numberOrNull(/** @type {Record<string, ReturnType<typeof JSON.parse>>} */ (rows[0] || {}).schedule_order)
|
|
1476
|
+
|
|
1477
|
+
if (currentOrder === null) return 1
|
|
1478
|
+
if (!Number.isSafeInteger(currentOrder) || currentOrder < 1 || currentOrder >= Number.MAX_SAFE_INTEGER) {
|
|
1479
|
+
throw new Error(`Invalid local background job schedule ownership order: ${currentOrder}`)
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
return currentOrder + 1
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* Builds a stable-schedule lookup exclusively from normalized local jobs.
|
|
1487
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
1488
|
+
* @param {object} args - Lookup options.
|
|
1489
|
+
* @param {boolean} args.includeLatestTerminal - Whether terminal history is requested.
|
|
1490
|
+
* @param {string} args.scheduleKey - Validated stable schedule key.
|
|
1491
|
+
* @returns {Promise<import("./types.js").BackgroundJobScheduledLookupResult>} - Normalized local jobs.
|
|
1492
|
+
*/
|
|
1493
|
+
async _scheduledJobLookup(db, {includeLatestTerminal, scheduleKey}) {
|
|
1494
|
+
const ownerJob = await this._scheduledOwnerJob(db, scheduleKey)
|
|
1495
|
+
const currentJob = ownerJob && (ownerJob.status === "queued" || ownerJob.status === "handed_off") ? ownerJob : null
|
|
1496
|
+
|
|
1497
|
+
if (!includeLatestTerminal) return {currentJob, latestTerminalJob: null}
|
|
1498
|
+
|
|
1499
|
+
const terminalStatuses = BACKGROUND_JOB_TERMINAL_STATUSES.map((status) => db.quote(status)).join(", ")
|
|
1500
|
+
const terminalRows = await db
|
|
1501
|
+
.newQuery()
|
|
1502
|
+
.from(LOCAL_BACKGROUND_JOBS_TABLE)
|
|
1503
|
+
.where({schedule_key: scheduleKey})
|
|
1504
|
+
.where(`${db.quoteColumn("status")} IN (${terminalStatuses})`)
|
|
1505
|
+
.order(`CASE WHEN ${db.quoteColumn("schedule_order")} IS NULL THEN 0 ELSE 1 END DESC`)
|
|
1506
|
+
.order("schedule_order DESC")
|
|
1507
|
+
.order("created_at_ms DESC")
|
|
1508
|
+
.order("id DESC")
|
|
1509
|
+
.limit(1)
|
|
1510
|
+
.results()
|
|
1511
|
+
const latestTerminalJob = terminalRows[0] ? this._normalizeRow(terminalRows[0]) : null
|
|
1512
|
+
|
|
1513
|
+
return {currentJob, latestTerminalJob}
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/**
|
|
1517
|
+
* Releases ownership only when the key still points at the expected job.
|
|
1518
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
1519
|
+
* @param {object} args - Ownership identity.
|
|
1520
|
+
* @param {string | null} args.jobId - Expected owner job id, or null for a dangling owner.
|
|
1521
|
+
* @param {string} args.scheduleKey - Stable schedule key.
|
|
1522
|
+
* @returns {Promise<void>} - Resolves when deleted or already superseded.
|
|
1523
|
+
*/
|
|
1524
|
+
async _releaseScheduleOwnership(db, {jobId, scheduleKey}) {
|
|
1525
|
+
const conditions = jobId === null
|
|
1526
|
+
? {schedule_key: scheduleKey}
|
|
1527
|
+
: {job_id: jobId, schedule_key: scheduleKey}
|
|
1528
|
+
|
|
1529
|
+
await db.delete({conditions, tableName: LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE})
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
/**
|
|
1533
|
+
* Releases a terminal job's stable ownership when still current.
|
|
1534
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
1535
|
+
* @param {import("./types.js").BackgroundJobRow} job - Terminal job.
|
|
1536
|
+
* @returns {Promise<void>} - Resolves when deleted or not applicable.
|
|
1537
|
+
*/
|
|
1538
|
+
async _releaseScheduleOwnershipForJob(db, job) {
|
|
1539
|
+
if (!job.scheduleKey) return
|
|
1540
|
+
|
|
1541
|
+
await this._releaseScheduleOwnership(db, {jobId: job.id, scheduleKey: job.scheduleKey})
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/**
|
|
1545
|
+
* Acquires SQLite's transaction write serialization before reading a stable
|
|
1546
|
+
* owner. A zero-row update still establishes the write boundary for a new key.
|
|
1547
|
+
* @param {import("../database/drivers/base.js").default} db - Database connection.
|
|
1548
|
+
* @param {string} scheduleKey - Validated stable schedule key.
|
|
1549
|
+
* @returns {Promise<void>} - Resolves after write serialization is acquired.
|
|
1550
|
+
*/
|
|
1551
|
+
async _lockScheduleKey(db, scheduleKey) {
|
|
1552
|
+
const table = db.quoteTable(LOCAL_BACKGROUND_JOB_SCHEDULE_KEYS_TABLE)
|
|
1553
|
+
const jobId = db.quoteColumn("job_id")
|
|
1554
|
+
|
|
1555
|
+
await db.query(
|
|
1556
|
+
`UPDATE ${table} SET ${jobId} = ${jobId} ` +
|
|
1557
|
+
`WHERE ${db.quoteColumn("schedule_key")} = ${db.quote(scheduleKey)}`
|
|
1558
|
+
)
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
/**
|
|
1562
|
+
* Registers the local dispatch poke on the surrounding transaction commit.
|
|
1563
|
+
* @param {import("../database/drivers/base.js").default} db - Transaction connection.
|
|
1564
|
+
* @returns {Promise<void>} - Resolves after registration.
|
|
1565
|
+
*/
|
|
1566
|
+
async _wakeDispatcherAfterCommit(db) {
|
|
1567
|
+
if (this.onCommittedEnqueue) await db.afterCommit(this.onCommittedEnqueue)
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1200
1570
|
/**
|
|
1201
1571
|
* Normalizes one raw local database row.
|
|
1202
1572
|
* @param {Record<string, ReturnType<typeof JSON.parse>>} row - Raw row.
|
|
@@ -1230,9 +1600,10 @@ export default class LocalBackgroundJobsStore {
|
|
|
1230
1600
|
maxRetries: this._numberOrNull(row.max_retries),
|
|
1231
1601
|
orphanedAtMs: null,
|
|
1232
1602
|
queue: row.queue ? String(row.queue) : DEFAULT_BACKGROUND_JOB_QUEUE,
|
|
1233
|
-
scheduleKey: null,
|
|
1603
|
+
scheduleKey: row.schedule_key === null || row.schedule_key === undefined ? null : String(row.schedule_key),
|
|
1604
|
+
scheduleOrder: this._numberOrNull(row.schedule_order),
|
|
1234
1605
|
scheduledAtMs: this._numberOrNull(row.scheduled_at_ms),
|
|
1235
|
-
status: row.status ? String(row.status) : "queued",
|
|
1606
|
+
status: normalizeBackgroundJobStatus(row.status ? String(row.status) : "queued"),
|
|
1236
1607
|
timeoutMs: null,
|
|
1237
1608
|
workerId: row.worker_id === null || row.worker_id === undefined ? null : String(row.worker_id)
|
|
1238
1609
|
}
|