velocious 1.0.533 → 1.0.535
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 +2 -0
- package/build/background-jobs/store.js +67 -22
- package/build/database/drivers/mysql/index.js +24 -11
- package/build/database/migration/index.js +20 -0
- package/build/database/migrator.js +5 -0
- package/build/database/tenants/data-copier.js +40 -5
- package/build/environment-handlers/base.js +14 -0
- package/build/environment-handlers/node.js +23 -0
- package/build/src/background-jobs/store.d.ts +30 -1
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +62 -22
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +19 -10
- package/build/src/database/migration/index.d.ts +7 -0
- package/build/src/database/migration/index.d.ts.map +1 -1
- package/build/src/database/migration/index.js +18 -1
- package/build/src/database/migrator.d.ts.map +1 -1
- package/build/src/database/migrator.js +6 -1
- package/build/src/database/tenants/data-copier.d.ts +31 -3
- package/build/src/database/tenants/data-copier.d.ts.map +1 -1
- package/build/src/database/tenants/data-copier.js +39 -6
- package/build/src/environment-handlers/base.d.ts +13 -0
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +14 -1
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +22 -1
- package/package.json +1 -1
- package/src/background-jobs/store.js +67 -22
- package/src/database/drivers/mysql/index.js +24 -11
- package/src/database/migration/index.js +20 -0
- package/src/database/migrator.js +5 -0
- package/src/database/tenants/data-copier.js +40 -5
- package/src/environment-handlers/base.js +14 -0
- package/src/environment-handlers/node.js +23 -0
|
@@ -468,6 +468,26 @@ export default class VelociousDatabaseMigration {
|
|
|
468
468
|
return Boolean(false)
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
/**
|
|
472
|
+
* Checks whether an index with the given name exists on a table.
|
|
473
|
+
* @param {string} tableName - Table name.
|
|
474
|
+
* @param {string} indexName - Index name to look for.
|
|
475
|
+
* @returns {Promise<boolean>} - Whether the index exists on the table.
|
|
476
|
+
*/
|
|
477
|
+
async indexExists(tableName, indexName) {
|
|
478
|
+
const table = await this.getDriver().getTableByName(tableName, {throwError: false})
|
|
479
|
+
|
|
480
|
+
if (table) {
|
|
481
|
+
for (const index of await table.getIndexes()) {
|
|
482
|
+
if (index.getName() == indexName) {
|
|
483
|
+
return true
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return false
|
|
489
|
+
}
|
|
490
|
+
|
|
471
491
|
/**
|
|
472
492
|
* Sets up the database schema for a gap-less positional list. Adds the
|
|
473
493
|
* position column (INT NOT NULL) if absent and creates a UNIQUE index on
|
package/src/database/migrator.js
CHANGED
|
@@ -204,6 +204,11 @@ export default class VelociousDatabaseMigrator {
|
|
|
204
204
|
|
|
205
205
|
if (!environmentHandler || Object.keys(filteredDbs).length == 0) return
|
|
206
206
|
|
|
207
|
+
// Ensure velocious' own framework schema (background jobs) before the structure
|
|
208
|
+
// dump, and unconditionally — the dump is gated to enabled environments but the
|
|
209
|
+
// framework schema must exist after every migrate so `db:migrate` (and thus
|
|
210
|
+
// schema:load of the dumped SQL) produces a complete DB in every environment.
|
|
211
|
+
await environmentHandler.ensureFrameworkSchema({dbs: filteredDbs})
|
|
207
212
|
await environmentHandler.afterMigrations({dbs: filteredDbs})
|
|
208
213
|
}
|
|
209
214
|
|
|
@@ -130,6 +130,35 @@ export default class DataCopier {
|
|
|
130
130
|
* @returns {Promise<Map<string, Record<string, unknown>[]>>} - Loaded rows grouped by table name.
|
|
131
131
|
*/
|
|
132
132
|
async loadRows(db, keyValue) {
|
|
133
|
+
return (await this.traversePlan(db, keyValue)).rowsByTableName
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Loads only the ids for `keyValue` for every table in the plan from `db`, using the same
|
|
138
|
+
* parent/child traversal as {@link DataCopier#loadRows} but selecting just the id column.
|
|
139
|
+
* Callers that only need to compare row membership — for example verifying a tenant already
|
|
140
|
+
* holds every default row before a cleanup delete — should use this instead of loadRows so
|
|
141
|
+
* they never materialise full rows; for large tenants that is the difference between a few
|
|
142
|
+
* kilobytes of ids and gigabytes of row data.
|
|
143
|
+
* @param {import("../drivers/base.js").default} db - Source or target database to traverse.
|
|
144
|
+
* @param {string} keyValue - Tenant key selecting the root plan rows.
|
|
145
|
+
* @returns {Promise<Map<string, string[]>>} - Loaded ids grouped by table name.
|
|
146
|
+
*/
|
|
147
|
+
async loadRowIds(db, keyValue) {
|
|
148
|
+
return (await this.traversePlan(db, keyValue, [this.idColumn])).idsByTableName
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Traverses the table plan for `keyValue`, querying each table by its tenant key column or
|
|
153
|
+
* (for child tables) by the ids already selected for its parent, and returns both the ids
|
|
154
|
+
* and the loaded rows grouped by table name. `selectColumns` bounds the columns each query
|
|
155
|
+
* selects; pass `[idColumn]` for an id-only traversal, or omit it to load full rows.
|
|
156
|
+
* @param {import("../drivers/base.js").default} db - Source or target database to traverse.
|
|
157
|
+
* @param {string} keyValue - Tenant key selecting the root plan rows.
|
|
158
|
+
* @param {string[]} [selectColumns] - Columns to select; defaults to every column.
|
|
159
|
+
* @returns {Promise<{idsByTableName: Map<string, string[]>, rowsByTableName: Map<string, Record<string, unknown>[]>}>} - Ids and rows grouped by table name.
|
|
160
|
+
*/
|
|
161
|
+
async traversePlan(db, keyValue, selectColumns) {
|
|
133
162
|
/** @type {Map<string, string[]>} */
|
|
134
163
|
const idsByTableName = new Map()
|
|
135
164
|
/** @type {Map<string, Record<string, unknown>[]>} */
|
|
@@ -142,6 +171,7 @@ export default class DataCopier {
|
|
|
142
171
|
rows = await this.queryRowsByColumn({
|
|
143
172
|
columnName: tableConfig.keyColumn,
|
|
144
173
|
db,
|
|
174
|
+
selectColumns,
|
|
145
175
|
tableName: tableConfig.tableName,
|
|
146
176
|
values: [keyValue]
|
|
147
177
|
})
|
|
@@ -157,6 +187,7 @@ export default class DataCopier {
|
|
|
157
187
|
rows = await this.queryRowsByColumn({
|
|
158
188
|
columnName: tableConfig.parentColumn,
|
|
159
189
|
db,
|
|
190
|
+
selectColumns,
|
|
160
191
|
tableName: tableConfig.tableName,
|
|
161
192
|
values: idsByTableName.get(tableConfig.parentTableName) || []
|
|
162
193
|
})
|
|
@@ -170,15 +201,16 @@ export default class DataCopier {
|
|
|
170
201
|
rowsByTableName.set(tableConfig.tableName, rows)
|
|
171
202
|
}
|
|
172
203
|
|
|
173
|
-
return rowsByTableName
|
|
204
|
+
return {idsByTableName, rowsByTableName}
|
|
174
205
|
}
|
|
175
206
|
|
|
176
207
|
/**
|
|
177
|
-
* Selects
|
|
178
|
-
*
|
|
208
|
+
* Selects `tableName` rows in `db` whose `columnName` is in `values`, chunked. `selectColumns`
|
|
209
|
+
* bounds the projection and defaults to every column.
|
|
210
|
+
* @param {{columnName: string, db: import("../drivers/base.js").default, selectColumns?: string[], tableName: string, values: string[]}} args - Table, column, projection, database, and values for the chunked lookup.
|
|
179
211
|
* @returns {Promise<Record<string, unknown>[]>} - Rows matching the supplied column values.
|
|
180
212
|
*/
|
|
181
|
-
async queryRowsByColumn({columnName, db, tableName, values}) {
|
|
213
|
+
async queryRowsByColumn({columnName, db, selectColumns, tableName, values}) {
|
|
182
214
|
const normalizedValues = uniqueStrings(values)
|
|
183
215
|
|
|
184
216
|
if (normalizedValues.length <= 0) {
|
|
@@ -188,9 +220,12 @@ export default class DataCopier {
|
|
|
188
220
|
const rows = []
|
|
189
221
|
const quotedTable = db.quoteTable(tableName)
|
|
190
222
|
const quotedColumn = db.quoteColumn(columnName)
|
|
223
|
+
const selectList = selectColumns
|
|
224
|
+
? selectColumns.map((column) => `${quotedTable}.${db.quoteColumn(column)}`).join(", ")
|
|
225
|
+
: `${quotedTable}.*`
|
|
191
226
|
|
|
192
227
|
for (const valuesChunk of chunks(normalizedValues, this.queryChunkSize)) {
|
|
193
|
-
const sql = `SELECT ${
|
|
228
|
+
const sql = `SELECT ${selectList} FROM ${quotedTable} WHERE ${quotedColumn} IN (${this.quotedValuesSql(db, valuesChunk)})`
|
|
194
229
|
|
|
195
230
|
rows.push(...await this.executeQuietQuery(db, sql))
|
|
196
231
|
}
|
|
@@ -508,6 +508,20 @@ export default class VelociousEnvironmentHandlerBase {
|
|
|
508
508
|
return
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
/**
|
|
512
|
+
* Ensures velocious' own framework-owned schema (e.g. the background-jobs
|
|
513
|
+
* tables) exists after app migrations run, so `db:migrate` produces a complete
|
|
514
|
+
* schema deterministically instead of it only appearing once a runtime store
|
|
515
|
+
* boots. Runs before the structure dump. No-op by default; the node handler
|
|
516
|
+
* overrides it.
|
|
517
|
+
* @param {object} args - Options object.
|
|
518
|
+
* @param {Record<string, import("../database/drivers/base.js").default>} args.dbs - Dbs being migrated.
|
|
519
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
520
|
+
*/
|
|
521
|
+
async ensureFrameworkSchema(args) { // eslint-disable-line no-unused-vars
|
|
522
|
+
return
|
|
523
|
+
}
|
|
524
|
+
|
|
511
525
|
/**
|
|
512
526
|
* Runs require command.
|
|
513
527
|
* @abstract
|
|
@@ -1005,6 +1005,29 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
1005
1005
|
return basePath
|
|
1006
1006
|
}
|
|
1007
1007
|
|
|
1008
|
+
/**
|
|
1009
|
+
* Ensures velocious' background-jobs schema exists on its configured database
|
|
1010
|
+
* as part of `db:migrate`, so the framework's own tables (background_jobs +
|
|
1011
|
+
* background_job_concurrency, execution_mode etc.) are created deterministically
|
|
1012
|
+
* alongside app migrations and captured in the dumped structure SQL — rather than
|
|
1013
|
+
* only appearing once a runtime store boots. Reuses the store's idempotent
|
|
1014
|
+
* `_ensureSchema`, so it's safe to re-run against DBs that already have it.
|
|
1015
|
+
* @param {object} args - Options object.
|
|
1016
|
+
* @param {Record<string, import("../database/drivers/base.js").default>} args.dbs - Dbs being migrated.
|
|
1017
|
+
* @returns {Promise<void>} - Resolves when complete.
|
|
1018
|
+
*/
|
|
1019
|
+
async ensureFrameworkSchema({dbs}) {
|
|
1020
|
+
const {default: BackgroundJobsStore} = await import("../background-jobs/store.js")
|
|
1021
|
+
const store = new BackgroundJobsStore({configuration: this.getConfiguration()})
|
|
1022
|
+
const databaseIdentifier = store.getDatabaseIdentifier() ?? "default"
|
|
1023
|
+
|
|
1024
|
+
// Reuse the connection db:migrate already holds for this database; opening a
|
|
1025
|
+
// nested checkout would deadlock a database whose pool is capped at one
|
|
1026
|
+
// connection. When the background-jobs database isn't among the migrated set,
|
|
1027
|
+
// this passes undefined and the store checks out its own (that pool is free).
|
|
1028
|
+
await store.ensureSchema(dbs[databaseIdentifier])
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1008
1031
|
/**
|
|
1009
1032
|
* Runs after migrations.
|
|
1010
1033
|
* @param {object} args - Options object.
|