velocious 1.0.530 → 1.0.532
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 +3 -1
- package/build/background-jobs/worker.js +71 -8
- package/build/database/drivers/mysql/sql/alter-table.js +42 -0
- package/build/database/query/preloader/belongs-to.js +9 -9
- package/build/database/query/preloader/has-many.js +8 -8
- package/build/database/query/preloader/has-one.js +5 -5
- package/build/database/tenants/schema-cloner.js +46 -9
- package/build/src/background-jobs/worker.d.ts +38 -5
- package/build/src/background-jobs/worker.d.ts.map +1 -1
- package/build/src/background-jobs/worker.js +70 -9
- package/build/src/database/drivers/mysql/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/alter-table.js +36 -1
- package/build/src/database/query/preloader/belongs-to.js +10 -12
- package/build/src/database/query/preloader/has-many.js +9 -11
- package/build/src/database/query/preloader/has-one.js +6 -7
- package/build/src/database/tenants/schema-cloner.d.ts +10 -0
- package/build/src/database/tenants/schema-cloner.d.ts.map +1 -1
- package/build/src/database/tenants/schema-cloner.js +36 -9
- package/package.json +3 -1
- package/src/background-jobs/worker.js +71 -8
- package/src/database/drivers/mysql/sql/alter-table.js +42 -0
- package/src/database/query/preloader/belongs-to.js +9 -9
- package/src/database/query/preloader/has-many.js +8 -8
- package/src/database/query/preloader/has-one.js +5 -5
- package/src/database/tenants/schema-cloner.js +46 -9
package/README.md
CHANGED
|
@@ -2013,7 +2013,7 @@ New jobs default to `executionMode: "pooled"`: a worker runs them in warm, reusa
|
|
|
2013
2013
|
|
|
2014
2014
|
`maxConcurrentForkedJobs` (default: `4`) caps how many out-of-process `executionMode: "forked"` or `executionMode: "spawned"` jobs one worker may keep in flight. Forked jobs use `child_process.fork()` with an attached IPC channel. After the main process acknowledges their durable status report, forked and spawned one-shot runners exit without waiting for graceful Beacon/database teardown; the OS closes their process-owned resources. A missing or rejected status acknowledgement makes the runner exit as failed instead of reporting clean success. Spawned jobs use the legacy `background-jobs-runner` CLI process via `child_process.spawn()` and are only for callers that intentionally want that spawned behavior.
|
|
2015
2015
|
|
|
2016
|
-
`jobTimeoutMs` (or `VELOCIOUS_BACKGROUND_JOBS_JOB_TIMEOUT_MS`, milliseconds; default: disabled) is a wall-clock backstop for
|
|
2016
|
+
`jobTimeoutMs` (or `VELOCIOUS_BACKGROUND_JOBS_JOB_TIMEOUT_MS`, milliseconds; default: disabled) is a wall-clock backstop for `"forked"` and `"pooled"` jobs. A job still running after the timeout is terminated (`SIGTERM`, then `SIGKILL` after the reaping grace) and reported `failed`, so a genuinely-hung job can't pin a worker's capacity — and its whole-app boot and DB connections — indefinitely (notably a retired-release worker draining after a deploy). For a **pooled** job the whole child running it is killed, so its concurrent in-flight siblings on that child are also reported `failed` and requeued — a hung JS job can't be cancelled any other way — before a replacement child is spawned. It's a coarse safety net, not per-job tuning: it applies to every forked and pooled job, so set it well above the longest legitimate job. Omit it, or set `null`/`<= 0`, to disable. `"inline"` jobs are not covered — they share the worker's process and can't be killed without killing the worker. See [docs/background-jobs.md](docs/background-jobs.md#job-timeout-hung-runner-backstop).
|
|
2017
2017
|
|
|
2018
2018
|
### Dispatch strategy
|
|
2019
2019
|
|
|
@@ -2327,4 +2327,6 @@ npx velocious db:tenants:migrate projectTenant --parallel 20
|
|
|
2327
2327
|
|
|
2328
2328
|
At runtime, the apartment-style `Tenant` façade (`velocious/build/src/tenants/tenant.js`) is the single entry point: `Tenant.with(tenant, callback)` / `Tenant.current()` to switch into and read a tenant context, `Tenant.each({identifier, callback, parallel?, filter?})` to run a callback within every provider-listed tenant, and `Tenant.drop({identifier, tenant})` (plus the `db:tenants:drop` CLI command) to drop a tenant's database through the provider's `dropDatabase` hook. `Tenant.with` and `Tenant.each` run their callbacks inside `ensureConnections`, so switching into a tenant establishes its database connections (global + tenant) and passes them to the callback — the tenant is immediately queryable without the caller wiring up connections (already-open connections are reused, so nesting does not double-connect). `Tenant.with` is generic over its callback's return type, so a value returned from inside the tenant context comes back to the caller with its type preserved (no cast needed). `Tenant.aggregateAcross({identifier, aggregates, keyColumns, subquery, tenants?, filter?})` runs one aggregate over the same table across many tenant databases and returns the merged result — grouping tenants by server and using a single cross-database `UNION ALL` where the driver supports two-part `` `database`.`table` `` references (MySQL/MariaDB) or one query per tenant otherwise (PostgreSQL/SQLite/MSSQL).
|
|
2329
2329
|
|
|
2330
|
+
`SchemaCloner` adds a missing auto-increment column and its separate source unique index in one schema alteration, including on MySQL/MariaDB where an auto-increment column must be keyed when it is created.
|
|
2331
|
+
|
|
2330
2332
|
See [docs/tenant-databases.md](docs/tenant-databases.md) for the full configuration and migration pattern.
|
|
@@ -72,7 +72,7 @@ export default class BackgroundJobsWorker {
|
|
|
72
72
|
* @param {number} [args.pooledRunnerMaxLifetimeMs] - Override the per-runner recycle lifetime.
|
|
73
73
|
* @param {number} [args.forkedChildSigkillGraceMs] - Override the grace period between SIGTERM and SIGKILL when reaping lingering process runners on stop.
|
|
74
74
|
* @param {number} [args.heartbeatIntervalMs] - Override the liveness heartbeat interval (default 15000ms).
|
|
75
|
-
* @param {number} [args.jobTimeoutMs] - Override the
|
|
75
|
+
* @param {number} [args.jobTimeoutMs] - Override the wall-clock timeout for forked and pooled jobs from `configuration.getBackgroundJobsConfig()`. `0` disables it.
|
|
76
76
|
*/
|
|
77
77
|
constructor({configuration, host, port, maxConcurrentForkedJobs, maxConcurrentInlineJobs, pooledRunnerCount, pooledRunnerConcurrency, pooledRunnerMaxJobs, pooledRunnerMaxRssBytes, pooledRunnerMaxLifetimeMs, forkedChildSigkillGraceMs, heartbeatIntervalMs, jobTimeoutMs} = {}) {
|
|
78
78
|
/**
|
|
@@ -129,7 +129,7 @@ export default class BackgroundJobsWorker {
|
|
|
129
129
|
? forkedChildSigkillGraceMs
|
|
130
130
|
: FORKED_CHILD_SIGKILL_GRACE_MS
|
|
131
131
|
/**
|
|
132
|
-
* Constructor override for the forked
|
|
132
|
+
* Constructor override for the forked and pooled wall-clock job timeout. When unset the
|
|
133
133
|
* timeout is read from `configuration.getBackgroundJobsConfig().jobTimeoutMs`
|
|
134
134
|
* at fork time (default: disabled).
|
|
135
135
|
* @type {number | undefined}
|
|
@@ -186,7 +186,7 @@ export default class BackgroundJobsWorker {
|
|
|
186
186
|
this.inflightPooledJobs = new Set()
|
|
187
187
|
/** @type {Set<import("node:child_process").ChildProcess>} */
|
|
188
188
|
this.pooledChildren = new Set()
|
|
189
|
-
/** @type {Map<import("node:child_process").ChildProcess, {createdAtMs: number, jobsRun: number, inflight: Map<string, {payload: import("./types.js").BackgroundJobPayload & {id: string}, resolve?: (value: void) => void}>, retiring: boolean, settling?: boolean}>} */
|
|
189
|
+
/** @type {Map<import("node:child_process").ChildProcess, {createdAtMs: number, jobsRun: number, inflight: Map<string, {payload: import("./types.js").BackgroundJobPayload & {id: string}, resolve?: (value: void) => void, timeoutTimer?: ReturnType<typeof setTimeout> | null}>, retiring: boolean, settling?: boolean, timeoutSigkillTimer?: ReturnType<typeof setTimeout> | null}>} */
|
|
190
190
|
this.pooledChildStates = new Map()
|
|
191
191
|
}
|
|
192
192
|
|
|
@@ -659,7 +659,9 @@ export default class BackgroundJobsWorker {
|
|
|
659
659
|
if (!state) throw new Error("Pooled runner state missing")
|
|
660
660
|
|
|
661
661
|
return new Promise((resolve) => {
|
|
662
|
-
|
|
662
|
+
const timeoutTimer = this._armPooledJobTimeout({child, jobId: payload.id})
|
|
663
|
+
|
|
664
|
+
state.inflight.set(payload.id, {payload, resolve, timeoutTimer})
|
|
663
665
|
try {
|
|
664
666
|
child.send({type: "job", payload})
|
|
665
667
|
} catch (error) {
|
|
@@ -668,6 +670,58 @@ export default class BackgroundJobsWorker {
|
|
|
668
670
|
})
|
|
669
671
|
}
|
|
670
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Arms a per-job wall-clock backstop for a pooled job. A pooled child hosts many
|
|
675
|
+
* concurrent jobs, so a single genuinely-hung job would otherwise pin its
|
|
676
|
+
* runner's concurrency slot forever — the lifetime recycle only retires a child
|
|
677
|
+
* once its in-flight set drains, which a hung job never does. On overrun the
|
|
678
|
+
* whole child is terminated so the hung job (and its siblings) requeue. Returns
|
|
679
|
+
* the timer, or null when no timeout is configured.
|
|
680
|
+
* @param {object} args - Options.
|
|
681
|
+
* @param {import("node:child_process").ChildProcess} args.child - Pooled child.
|
|
682
|
+
* @param {string} args.jobId - Job id whose overrun is guarded.
|
|
683
|
+
* @returns {ReturnType<typeof setTimeout> | null} - The armed timer, or null.
|
|
684
|
+
*/
|
|
685
|
+
_armPooledJobTimeout({child, jobId}) {
|
|
686
|
+
const timeoutMs = this._resolveJobTimeoutMs()
|
|
687
|
+
|
|
688
|
+
if (!(typeof timeoutMs === "number" && timeoutMs > 0)) return null
|
|
689
|
+
|
|
690
|
+
return setTimeout(() => this._onPooledJobTimeout({child, jobId}), timeoutMs)
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Fired when a pooled job overruns its timeout. Terminates the child running it
|
|
695
|
+
* (SIGTERM, then SIGKILL after the grace) — a hung JS job cannot be cancelled
|
|
696
|
+
* any other way. The non-clean exit flows through `_handlePooledChildFailure`,
|
|
697
|
+
* which reports every in-flight job on the child failed (so they requeue) and
|
|
698
|
+
* drops it from tracking; capacity is refilled on the next dispatch.
|
|
699
|
+
* @param {object} args - Options.
|
|
700
|
+
* @param {import("node:child_process").ChildProcess} args.child - Pooled child.
|
|
701
|
+
* @param {string} args.jobId - Job id that overran.
|
|
702
|
+
* @returns {void}
|
|
703
|
+
*/
|
|
704
|
+
_onPooledJobTimeout({child, jobId}) {
|
|
705
|
+
const state = this.pooledChildStates.get(child)
|
|
706
|
+
|
|
707
|
+
// Already settling/gone, or the job finished in the race with this timer.
|
|
708
|
+
if (!state || state.settling || !state.inflight.has(jobId)) return
|
|
709
|
+
|
|
710
|
+
try {
|
|
711
|
+
child.kill("SIGTERM")
|
|
712
|
+
} catch {
|
|
713
|
+
// Child already exited; nothing to do.
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
state.timeoutSigkillTimer = setTimeout(() => {
|
|
717
|
+
try {
|
|
718
|
+
child.kill("SIGKILL")
|
|
719
|
+
} catch {
|
|
720
|
+
// Child already exited; nothing to do.
|
|
721
|
+
}
|
|
722
|
+
}, this.forkedChildSigkillGraceMs)
|
|
723
|
+
}
|
|
724
|
+
|
|
671
725
|
/**
|
|
672
726
|
* Creates a reusable pooled child.
|
|
673
727
|
* @returns {import("node:child_process").ChildProcess} - New pooled child.
|
|
@@ -705,6 +759,7 @@ export default class BackgroundJobsWorker {
|
|
|
705
759
|
const entry = state.inflight.get(record.jobId)
|
|
706
760
|
if (!entry) return
|
|
707
761
|
|
|
762
|
+
if (entry.timeoutTimer) clearTimeout(entry.timeoutTimer)
|
|
708
763
|
state.inflight.delete(record.jobId)
|
|
709
764
|
state.jobsRun += 1
|
|
710
765
|
const resolve = entry.resolve
|
|
@@ -789,7 +844,15 @@ export default class BackgroundJobsWorker {
|
|
|
789
844
|
async _handlePooledChildFailure({child, error}) {
|
|
790
845
|
const state = this.pooledChildStates.get(child)
|
|
791
846
|
if (state?.settling) return
|
|
792
|
-
if (state)
|
|
847
|
+
if (state) {
|
|
848
|
+
state.settling = true
|
|
849
|
+
// Cancel this child's pending timers before its in-flight set is reported —
|
|
850
|
+
// the SIGKILL grace from a timeout kill, and every armed per-job backstop.
|
|
851
|
+
if (state.timeoutSigkillTimer) clearTimeout(state.timeoutSigkillTimer)
|
|
852
|
+
for (const inflightEntry of state.inflight.values()) {
|
|
853
|
+
if (inflightEntry.timeoutTimer) clearTimeout(inflightEntry.timeoutTimer)
|
|
854
|
+
}
|
|
855
|
+
}
|
|
793
856
|
this.pooledChildren.delete(child)
|
|
794
857
|
this.inflightProcessChildren.delete(child)
|
|
795
858
|
|
|
@@ -908,7 +971,7 @@ export default class BackgroundJobsWorker {
|
|
|
908
971
|
* @returns {ForkedJobTimeoutState} - Timeout state.
|
|
909
972
|
*/
|
|
910
973
|
_armForkedJobTimeout({child}) {
|
|
911
|
-
const timeoutMs = this.
|
|
974
|
+
const timeoutMs = this._resolveJobTimeoutMs()
|
|
912
975
|
/** @type {ForkedJobTimeoutState} */
|
|
913
976
|
const state = {timedOut: false, timeoutMs, timer: null, sigkillTimer: null}
|
|
914
977
|
|
|
@@ -920,12 +983,12 @@ export default class BackgroundJobsWorker {
|
|
|
920
983
|
}
|
|
921
984
|
|
|
922
985
|
/**
|
|
923
|
-
* Resolves the effective
|
|
986
|
+
* Resolves the effective wall-clock job timeout in ms (shared by forked and pooled jobs), or null when disabled. The
|
|
924
987
|
* constructor override wins; otherwise the value comes from the background-jobs
|
|
925
988
|
* configuration. A non-positive value disables the backstop.
|
|
926
989
|
* @returns {number | null} - Timeout in ms, or null when disabled.
|
|
927
990
|
*/
|
|
928
|
-
|
|
991
|
+
_resolveJobTimeoutMs() {
|
|
929
992
|
const raw = typeof this.jobTimeoutMsOverride === "number"
|
|
930
993
|
? this.jobTimeoutMsOverride
|
|
931
994
|
: (this.configuration ? this.configuration.getBackgroundJobsConfig().jobTimeoutMs : null)
|
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import AlterTableBase from "../../../query/alter-table-base.js"
|
|
4
|
+
import TableColumn from "../../../table-data/table-column.js"
|
|
4
5
|
|
|
5
6
|
export default class VelociousDatabaseConnectionDriversMysqlSqlAlterTable extends AlterTableBase {
|
|
7
|
+
/**
|
|
8
|
+
* Builds MySQL ALTER TABLE statements, adding indexes atomically with columns.
|
|
9
|
+
* @returns {Promise<string[]>} - Resolves with SQL statements.
|
|
10
|
+
*/
|
|
11
|
+
async toSQLs() {
|
|
12
|
+
const sqls = await super.toSQLs()
|
|
13
|
+
const indexes = this.tableData.getIndexes()
|
|
14
|
+
|
|
15
|
+
if (indexes.length === 0) return sqls
|
|
16
|
+
if (sqls.length !== 1) throw new Error("Expected one MySQL ALTER TABLE statement when adding indexes")
|
|
17
|
+
|
|
18
|
+
const options = this.getOptions()
|
|
19
|
+
let sql = sqls[0]
|
|
20
|
+
|
|
21
|
+
for (const index of indexes) {
|
|
22
|
+
sql += ", ADD"
|
|
23
|
+
|
|
24
|
+
if (index.getUnique()) sql += " UNIQUE"
|
|
25
|
+
|
|
26
|
+
sql += " INDEX"
|
|
27
|
+
|
|
28
|
+
const indexName = index.getName()
|
|
29
|
+
|
|
30
|
+
if (typeof indexName === "string") {
|
|
31
|
+
sql += ` ${options.quoteIndexName(indexName)}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
sql += " ("
|
|
35
|
+
sql += index
|
|
36
|
+
.getColumns()
|
|
37
|
+
.map((column) => {
|
|
38
|
+
const columnName = column instanceof TableColumn ? column.getName() : column
|
|
39
|
+
|
|
40
|
+
return options.quoteColumnName(columnName)
|
|
41
|
+
})
|
|
42
|
+
.join(", ")
|
|
43
|
+
sql += ")"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return [sql]
|
|
47
|
+
}
|
|
6
48
|
}
|
|
@@ -58,8 +58,8 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Foreign key values.
|
|
61
|
-
* @type {
|
|
62
|
-
const foreignKeyValues =
|
|
61
|
+
* @type {Set<number | string>} */
|
|
62
|
+
const foreignKeyValues = new Set()
|
|
63
63
|
|
|
64
64
|
for (const model of modelsToLoad) {
|
|
65
65
|
const foreignKeyValue = /** @type {string | number | null | undefined} */ (model.readColumn(foreignKey))
|
|
@@ -69,7 +69,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
69
69
|
// throws on non-string primary-key columns.
|
|
70
70
|
if (foreignKeyValue === null || foreignKeyValue === undefined) continue
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
foreignKeyValues.add(foreignKeyValue)
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
@@ -83,7 +83,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
83
83
|
let targetModels = []
|
|
84
84
|
|
|
85
85
|
// Only query when at least one model has a non-null foreign key.
|
|
86
|
-
if (foreignKeyValues.
|
|
86
|
+
if (foreignKeyValues.size > 0) {
|
|
87
87
|
await ensureModelClassInitialized(targetModelClass, this.relationship.getConfiguration())
|
|
88
88
|
|
|
89
89
|
/**
|
|
@@ -91,7 +91,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
91
91
|
* @type {Record<string, string | number | Array<string | number>>} */
|
|
92
92
|
const whereArgs = {}
|
|
93
93
|
|
|
94
|
-
whereArgs[primaryKey] = foreignKeyValues
|
|
94
|
+
whereArgs[primaryKey] = [...foreignKeyValues]
|
|
95
95
|
|
|
96
96
|
// Load target models to be preloaded on the given models
|
|
97
97
|
let query = targetModelClass.where(whereArgs)
|
|
@@ -178,15 +178,15 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
178
178
|
|
|
179
179
|
/**
|
|
180
180
|
* Foreign key values by type.
|
|
181
|
-
* @type {Record<string,
|
|
181
|
+
* @type {Record<string, Set<number | string>>} */
|
|
182
182
|
const foreignKeyValuesByType = {}
|
|
183
183
|
|
|
184
184
|
for (const meta of modelMeta) {
|
|
185
185
|
if (meta.targetType === undefined || meta.targetType === null) continue
|
|
186
186
|
if (meta.foreignKeyValue === undefined || meta.foreignKeyValue === null) continue
|
|
187
187
|
|
|
188
|
-
if (!foreignKeyValuesByType[meta.targetType]) foreignKeyValuesByType[meta.targetType] =
|
|
189
|
-
|
|
188
|
+
if (!foreignKeyValuesByType[meta.targetType]) foreignKeyValuesByType[meta.targetType] = new Set()
|
|
189
|
+
foreignKeyValuesByType[meta.targetType].add(meta.foreignKeyValue)
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/**
|
|
@@ -209,7 +209,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
209
209
|
* @type {Record<string, string | number | Array<string | number>>} */
|
|
210
210
|
const whereArgs = {}
|
|
211
211
|
|
|
212
|
-
whereArgs[primaryKey] = foreignKeyValuesByType[targetType]
|
|
212
|
+
whereArgs[primaryKey] = [...foreignKeyValuesByType[targetType]]
|
|
213
213
|
|
|
214
214
|
let query = targetModelClass.where(whereArgs)
|
|
215
215
|
|
|
@@ -130,8 +130,8 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
132
|
* Models primary key values.
|
|
133
|
-
* @type {
|
|
134
|
-
const modelsPrimaryKeyValues =
|
|
133
|
+
* @type {Set<number | string>} */
|
|
134
|
+
const modelsPrimaryKeyValues = new Set()
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* Models by primary key value.
|
|
@@ -148,7 +148,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
148
148
|
|
|
149
149
|
preloadCollections[primaryKeyValue] = []
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
modelsPrimaryKeyValues.add(primaryKeyValue)
|
|
152
152
|
if (!(primaryKeyValue in modelsByPrimaryKeyValue)) modelsByPrimaryKeyValue[primaryKeyValue] = []
|
|
153
153
|
|
|
154
154
|
modelsByPrimaryKeyValue[primaryKeyValue].push(model)
|
|
@@ -156,7 +156,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
156
156
|
|
|
157
157
|
// Step 1: Query the through table to build parent→target ID mapping
|
|
158
158
|
const throughModels = await throughModelClass
|
|
159
|
-
.where({[throughForeignKey]: modelsPrimaryKeyValues})
|
|
159
|
+
.where({[throughForeignKey]: [...modelsPrimaryKeyValues]})
|
|
160
160
|
.toArray()
|
|
161
161
|
|
|
162
162
|
/**
|
|
@@ -260,8 +260,8 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
260
260
|
|
|
261
261
|
/**
|
|
262
262
|
* Models primary key values.
|
|
263
|
-
* @type {
|
|
264
|
-
const modelsPrimaryKeyValues =
|
|
263
|
+
* @type {Set<number | string>} */
|
|
264
|
+
const modelsPrimaryKeyValues = new Set()
|
|
265
265
|
|
|
266
266
|
/**
|
|
267
267
|
* Models by primary key value.
|
|
@@ -278,7 +278,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
278
278
|
|
|
279
279
|
preloadCollections[primaryKeyValue] = []
|
|
280
280
|
|
|
281
|
-
|
|
281
|
+
modelsPrimaryKeyValues.add(primaryKeyValue)
|
|
282
282
|
if (!(primaryKeyValue in modelsByPrimaryKeyValue)) modelsByPrimaryKeyValue[primaryKeyValue] = []
|
|
283
283
|
|
|
284
284
|
modelsByPrimaryKeyValue[primaryKeyValue].push(model)
|
|
@@ -289,7 +289,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
289
289
|
* @type {Record<string, string | number | Array<string | number>>} */
|
|
290
290
|
const whereArgs = {}
|
|
291
291
|
|
|
292
|
-
whereArgs[foreignKey] = modelsPrimaryKeyValues
|
|
292
|
+
whereArgs[foreignKey] = [...modelsPrimaryKeyValues]
|
|
293
293
|
|
|
294
294
|
if (this.relationship.getPolymorphic()) {
|
|
295
295
|
const typeColumn = this.relationship.getPolymorphicTypeColumn()
|
|
@@ -23,8 +23,8 @@ export default class VelociousDatabaseQueryPreloaderHasOne {
|
|
|
23
23
|
async run() {
|
|
24
24
|
/**
|
|
25
25
|
* Models primary key values.
|
|
26
|
-
* @type {
|
|
27
|
-
const modelsPrimaryKeyValues =
|
|
26
|
+
* @type {Set<number | string>} */
|
|
27
|
+
const modelsPrimaryKeyValues = new Set()
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Models by primary key value.
|
|
@@ -63,20 +63,20 @@ export default class VelociousDatabaseQueryPreloaderHasOne {
|
|
|
63
63
|
|
|
64
64
|
preloadCollections[primaryKeyValue] = undefined
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
modelsPrimaryKeyValues.add(primaryKeyValue)
|
|
67
67
|
if (!(primaryKeyValue in modelsByPrimaryKeyValue)) modelsByPrimaryKeyValue[primaryKeyValue] = []
|
|
68
68
|
|
|
69
69
|
modelsByPrimaryKeyValue[primaryKeyValue].push(model)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
if (modelsPrimaryKeyValues.
|
|
72
|
+
if (modelsPrimaryKeyValues.size == 0) return satisfiedTargets
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* Where args.
|
|
76
76
|
* @type {Record<string, string | number | Array<string | number>>} */
|
|
77
77
|
const whereArgs = {}
|
|
78
78
|
|
|
79
|
-
whereArgs[foreignKey] = modelsPrimaryKeyValues
|
|
79
|
+
whereArgs[foreignKey] = [...modelsPrimaryKeyValues]
|
|
80
80
|
|
|
81
81
|
if (this.relationship.getPolymorphic()) {
|
|
82
82
|
const typeColumn = this.relationship.getPolymorphicTypeColumn()
|
|
@@ -143,6 +143,37 @@ export default class SchemaCloner {
|
|
|
143
143
|
tableData.addColumn(sourceColumn.getName(), this.columnArgsFromSourceColumn(sourceColumn, {isNewColumn: false}))
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
if (this.targetDb.getType() === "mysql") {
|
|
147
|
+
const missingAutoIncrementColumnNames = new Set(
|
|
148
|
+
missingColumns
|
|
149
|
+
.filter((sourceColumn) => sourceColumn.getAutoIncrement())
|
|
150
|
+
.map((sourceColumn) => sourceColumn.getName())
|
|
151
|
+
)
|
|
152
|
+
const targetIndexesByName = new Map(
|
|
153
|
+
(await targetTable.getIndexes()).map((targetIndex) => [targetIndex.getName(), targetIndex])
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
for (const sourceIndex of await sourceTable.getIndexes()) {
|
|
157
|
+
if (
|
|
158
|
+
!sourceIndex.isPrimaryKey() &&
|
|
159
|
+
sourceIndex.isUnique() &&
|
|
160
|
+
missingAutoIncrementColumnNames.has(sourceIndex.getColumnNames()[0])
|
|
161
|
+
) {
|
|
162
|
+
const targetIndex = targetIndexesByName.get(sourceIndex.getName())
|
|
163
|
+
|
|
164
|
+
if (targetIndex) {
|
|
165
|
+
if (this.indexesMatch(sourceIndex, targetIndex)) continue
|
|
166
|
+
|
|
167
|
+
this.assertSafeIndexReplacement({sourceIndex, tableName, targetIndex})
|
|
168
|
+
await this.dropTargetIndex({tableName, targetIndex})
|
|
169
|
+
targetIndexesByName.delete(targetIndex.getName())
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
tableData.addIndex(this.tableDataIndexFromSourceIndex(sourceIndex))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
146
177
|
for (const alterSql of await this.targetDb.alterTableSQLs(tableData)) {
|
|
147
178
|
await this.targetDb.query(alterSql)
|
|
148
179
|
}
|
|
@@ -186,15 +217,7 @@ export default class SchemaCloner {
|
|
|
186
217
|
|
|
187
218
|
if (targetIndex) {
|
|
188
219
|
if (!this.indexesMatch(sourceIndex, targetIndex)) {
|
|
189
|
-
|
|
190
|
-
// the target may have duplicate values that will reject the new
|
|
191
|
-
// unique constraint, and the old index was already dropped by this
|
|
192
|
-
// point. The opposite direction (unique → non-unique) is always
|
|
193
|
-
// safe — non-unique indexes never fail on duplicate values.
|
|
194
|
-
if (sourceIndex.isUnique() && !targetIndex.isUnique()) {
|
|
195
|
-
throw new Error(`Schema clone index drift for ${tableName}.${sourceIndex.getName()}: cannot safely replace a non-unique index with a unique one.`)
|
|
196
|
-
}
|
|
197
|
-
|
|
220
|
+
this.assertSafeIndexReplacement({sourceIndex, tableName, targetIndex})
|
|
198
221
|
await this.dropTargetIndex({tableName, targetIndex})
|
|
199
222
|
targetIndexesByName.delete(targetIndex.getName())
|
|
200
223
|
targetIndexSignatures.delete(this.indexSignature(targetIndex))
|
|
@@ -241,6 +264,20 @@ export default class SchemaCloner {
|
|
|
241
264
|
}
|
|
242
265
|
}
|
|
243
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Refuses index replacements that could fail after the existing index is dropped.
|
|
269
|
+
* @param {{sourceIndex: import("../drivers/base-columns-index.js").default, tableName: string, targetIndex: import("../drivers/base-columns-index.js").default}} args - Source and target index definitions.
|
|
270
|
+
* @returns {void}
|
|
271
|
+
*/
|
|
272
|
+
assertSafeIndexReplacement({sourceIndex, tableName, targetIndex}) {
|
|
273
|
+
// Replacing a non-unique index with a unique one is unsafe because the
|
|
274
|
+
// target may have duplicate values that will reject the new constraint.
|
|
275
|
+
// The opposite direction is safe because non-unique indexes accept them.
|
|
276
|
+
if (sourceIndex.isUnique() && !targetIndex.isUnique()) {
|
|
277
|
+
throw new Error(`Schema clone index drift for ${tableName}.${sourceIndex.getName()}: cannot safely replace a non-unique index with a unique one.`)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
244
281
|
/**
|
|
245
282
|
* Baselines the target ledger so the cloned schema is recorded as already-applied.
|
|
246
283
|
* @returns {Promise<string[]>} The versions newly recorded on the target.
|
|
@@ -22,7 +22,7 @@ export default class BackgroundJobsWorker {
|
|
|
22
22
|
* @param {number} [args.pooledRunnerMaxLifetimeMs] - Override the per-runner recycle lifetime.
|
|
23
23
|
* @param {number} [args.forkedChildSigkillGraceMs] - Override the grace period between SIGTERM and SIGKILL when reaping lingering process runners on stop.
|
|
24
24
|
* @param {number} [args.heartbeatIntervalMs] - Override the liveness heartbeat interval (default 15000ms).
|
|
25
|
-
* @param {number} [args.jobTimeoutMs] - Override the
|
|
25
|
+
* @param {number} [args.jobTimeoutMs] - Override the wall-clock timeout for forked and pooled jobs from `configuration.getBackgroundJobsConfig()`. `0` disables it.
|
|
26
26
|
*/
|
|
27
27
|
constructor({ configuration, host, port, maxConcurrentForkedJobs, maxConcurrentInlineJobs, pooledRunnerCount, pooledRunnerConcurrency, pooledRunnerMaxJobs, pooledRunnerMaxRssBytes, pooledRunnerMaxLifetimeMs, forkedChildSigkillGraceMs, heartbeatIntervalMs, jobTimeoutMs }?: {
|
|
28
28
|
configuration?: import("../configuration.js").default | undefined;
|
|
@@ -87,7 +87,7 @@ export default class BackgroundJobsWorker {
|
|
|
87
87
|
*/
|
|
88
88
|
forkedChildSigkillGraceMs: number;
|
|
89
89
|
/**
|
|
90
|
-
* Constructor override for the forked
|
|
90
|
+
* Constructor override for the forked and pooled wall-clock job timeout. When unset the
|
|
91
91
|
* timeout is read from `configuration.getBackgroundJobsConfig().jobTimeoutMs`
|
|
92
92
|
* at fork time (default: disabled).
|
|
93
93
|
* @type {number | undefined}
|
|
@@ -142,7 +142,7 @@ export default class BackgroundJobsWorker {
|
|
|
142
142
|
inflightPooledJobs: Set<Promise<void>>;
|
|
143
143
|
/** @type {Set<import("node:child_process").ChildProcess>} */
|
|
144
144
|
pooledChildren: Set<import("node:child_process").ChildProcess>;
|
|
145
|
-
/** @type {Map<import("node:child_process").ChildProcess, {createdAtMs: number, jobsRun: number, inflight: Map<string, {payload: import("./types.js").BackgroundJobPayload & {id: string}, resolve?: (value: void) => void}>, retiring: boolean, settling?: boolean}>} */
|
|
145
|
+
/** @type {Map<import("node:child_process").ChildProcess, {createdAtMs: number, jobsRun: number, inflight: Map<string, {payload: import("./types.js").BackgroundJobPayload & {id: string}, resolve?: (value: void) => void, timeoutTimer?: ReturnType<typeof setTimeout> | null}>, retiring: boolean, settling?: boolean, timeoutSigkillTimer?: ReturnType<typeof setTimeout> | null}>} */
|
|
146
146
|
pooledChildStates: Map<import("node:child_process").ChildProcess, {
|
|
147
147
|
createdAtMs: number;
|
|
148
148
|
jobsRun: number;
|
|
@@ -151,9 +151,11 @@ export default class BackgroundJobsWorker {
|
|
|
151
151
|
id: string;
|
|
152
152
|
};
|
|
153
153
|
resolve?: (value: void) => void;
|
|
154
|
+
timeoutTimer?: ReturnType<typeof setTimeout> | null;
|
|
154
155
|
}>;
|
|
155
156
|
retiring: boolean;
|
|
156
157
|
settling?: boolean;
|
|
158
|
+
timeoutSigkillTimer?: ReturnType<typeof setTimeout> | null;
|
|
157
159
|
}>;
|
|
158
160
|
/**
|
|
159
161
|
* Runs start.
|
|
@@ -295,6 +297,37 @@ export default class BackgroundJobsWorker {
|
|
|
295
297
|
_runPooledJob(payload: import("./types.js").BackgroundJobPayload & {
|
|
296
298
|
id: string;
|
|
297
299
|
}): Promise<void>;
|
|
300
|
+
/**
|
|
301
|
+
* Arms a per-job wall-clock backstop for a pooled job. A pooled child hosts many
|
|
302
|
+
* concurrent jobs, so a single genuinely-hung job would otherwise pin its
|
|
303
|
+
* runner's concurrency slot forever — the lifetime recycle only retires a child
|
|
304
|
+
* once its in-flight set drains, which a hung job never does. On overrun the
|
|
305
|
+
* whole child is terminated so the hung job (and its siblings) requeue. Returns
|
|
306
|
+
* the timer, or null when no timeout is configured.
|
|
307
|
+
* @param {object} args - Options.
|
|
308
|
+
* @param {import("node:child_process").ChildProcess} args.child - Pooled child.
|
|
309
|
+
* @param {string} args.jobId - Job id whose overrun is guarded.
|
|
310
|
+
* @returns {ReturnType<typeof setTimeout> | null} - The armed timer, or null.
|
|
311
|
+
*/
|
|
312
|
+
_armPooledJobTimeout({ child, jobId }: {
|
|
313
|
+
child: import("node:child_process").ChildProcess;
|
|
314
|
+
jobId: string;
|
|
315
|
+
}): ReturnType<typeof setTimeout> | null;
|
|
316
|
+
/**
|
|
317
|
+
* Fired when a pooled job overruns its timeout. Terminates the child running it
|
|
318
|
+
* (SIGTERM, then SIGKILL after the grace) — a hung JS job cannot be cancelled
|
|
319
|
+
* any other way. The non-clean exit flows through `_handlePooledChildFailure`,
|
|
320
|
+
* which reports every in-flight job on the child failed (so they requeue) and
|
|
321
|
+
* drops it from tracking; capacity is refilled on the next dispatch.
|
|
322
|
+
* @param {object} args - Options.
|
|
323
|
+
* @param {import("node:child_process").ChildProcess} args.child - Pooled child.
|
|
324
|
+
* @param {string} args.jobId - Job id that overran.
|
|
325
|
+
* @returns {void}
|
|
326
|
+
*/
|
|
327
|
+
_onPooledJobTimeout({ child, jobId }: {
|
|
328
|
+
child: import("node:child_process").ChildProcess;
|
|
329
|
+
jobId: string;
|
|
330
|
+
}): void;
|
|
298
331
|
/**
|
|
299
332
|
* Creates a reusable pooled child.
|
|
300
333
|
* @returns {import("node:child_process").ChildProcess} - New pooled child.
|
|
@@ -397,12 +430,12 @@ export default class BackgroundJobsWorker {
|
|
|
397
430
|
child: import("node:child_process").ChildProcess;
|
|
398
431
|
}): ForkedJobTimeoutState;
|
|
399
432
|
/**
|
|
400
|
-
* Resolves the effective
|
|
433
|
+
* Resolves the effective wall-clock job timeout in ms (shared by forked and pooled jobs), or null when disabled. The
|
|
401
434
|
* constructor override wins; otherwise the value comes from the background-jobs
|
|
402
435
|
* configuration. A non-positive value disables the backstop.
|
|
403
436
|
* @returns {number | null} - Timeout in ms, or null when disabled.
|
|
404
437
|
*/
|
|
405
|
-
|
|
438
|
+
_resolveJobTimeoutMs(): number | null;
|
|
406
439
|
/**
|
|
407
440
|
* Fired when a forked runner overruns its timeout. Sends SIGTERM for a clean
|
|
408
441
|
* shutdown, then SIGKILL after the grace for a runner that ignores it. The
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/worker.js"],"names":[],"mappings":"AAiDA;;;;;;;GAOG;AAEH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH,iRAdG;QAAqD,aAAa;QAC5C,IAAI;QACJ,IAAI;QACJ,uBAAuB;QACvB,uBAAuB;QACvB,iBAAiB;QACjB,uBAAuB;QACvB,mBAAmB;QACnB,uBAAuB;QACvB,yBAAyB;QACzB,yBAAyB;QACzB,mBAAmB;QACnB,YAAY;KACpC,EAmHA;IAjHC;;gEAE4D;IAC5D,sBADU,OAAO,CAAC,OAAO,qBAAqB,EAAE,OAAO,CAAC,CAC4C;IACpG;;mEAE+D;IAC/D,eADU,OAAO,qBAAqB,EAAE,OAAO,GAAG,SAAS,CAC7B;IAC9B,yBAAgB;IAChB,yBAAgB;IAChB;;;;;OAKG;IACH,iCAFU,MAAM,GAAG,SAAS,CAIf;IACb;;oCAEgC;IAChC,iCADU,MAAM,GAAG,SAAS,CAGf;IACb;;;;OAIG;IACH,yBAFU,MAAM,CAEwD;IACxE;;wBAEoB;IACpB,yBADU,MAAM,CACwD;IACxE,8CAAmE;IACnE,oDAA+E;IAC/E,gDAAuE;IACvE,oDAA8E;IAC9E,sDAAkF;IAClF,0BAA4D;IAC5D,gCAAwE;IACxE,4BAAkE;IAClE,gCAAwF;IACxF,kCAAyF;IACzF;;;;OAIG;IACH,2BAFU,MAAM,CAIiB;IACjC;;;;;OAKG;IACH,sBAFU,MAAM,GAAG,SAAS,CAE2D;IACvF,oBAAuB;IACvB,8DAA4B;IAC5B,4BAEyB;IACzB;;4DAEwD;IACxD,iBADU,UAAU,CAAC,OAAO,WAAW,CAAC,GAAG,SAAS,CACpB;IAChC;;;;;;OAMG;IACH,iBAFU,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAEI;IAChC;;wCAEoC;IACpC,YADU,UAAU,GAAG,SAAS,CACL;IAC3B;;0DAEsD;IACtD,gBADU,4BAA4B,GAAG,SAAS,CACnB;IAC/B;;;;;;OAMG;IACH,oBAFU,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAEO;IACnC;;;;OAIG;IACH,qBAFU,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAEQ;IACpC;;;;;;OAMG;IACH,yBAFU,GAAG,CAAC,OAAO,oBAAoB,EAAE,YAAY,CAAC,CAEhB;IACxC,iCAAiC;IACjC,oBADW,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CACM;IACnC,6DAA6D;IAC7D,gBADW,GAAG,CAAC,OAAO,oBAAoB,EAAE,YAAY,CAAC,CAC1B;IAC/B,
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/worker.js"],"names":[],"mappings":"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"}
|