velocious 1.0.568 → 1.0.570
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 +30 -0
- package/build/configuration.js +36 -0
- package/build/database/drivers/base.js +244 -93
- package/build/database/drivers/mssql/index.js +10 -7
- package/build/database/drivers/mysql/index.js +10 -5
- package/build/database/drivers/pgsql/index.js +4 -4
- package/build/database/drivers/sqlite/base.js +2 -2
- package/build/database/drivers/sqlite/connection-sql-js.js +62 -2
- package/build/database/drivers/sqlite/index.web.js +74 -0
- package/build/database/operation-connection.js +121 -0
- package/build/database/operation-lease.js +47 -0
- package/build/database/operation.js +150 -0
- package/build/database/pool/base.js +26 -1
- package/build/database/pool/single-multi-use.js +49 -0
- package/build/database/query/model-class-query.js +38 -7
- package/build/database/query/preloader/belongs-to.js +3 -2
- package/build/database/query/preloader/has-many.js +4 -3
- package/build/database/query/preloader/has-one.js +2 -1
- package/build/database/query/preloader/query-for-model.js +17 -0
- package/build/database/query/query-data.js +12 -3
- package/build/database/query/with-count.js +5 -3
- package/build/database/record/acts-as-list.js +11 -8
- package/build/database/record/attachments/store.js +15 -11
- package/build/database/record/auditing.js +12 -9
- package/build/database/record/counter-cache-magnitude.js +5 -3
- package/build/database/record/index.js +95 -14
- package/build/database/record/instance-relationships/belongs-to.js +4 -2
- package/build/database/record/instance-relationships/has-many.js +7 -5
- package/build/database/record/instance-relationships/has-one.js +4 -2
- package/build/database/record/validators/uniqueness.js +3 -2
- package/build/frontend-models/websocket-publishers.js +2 -2
- package/build/src/configuration.d.ts +12 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +35 -1
- package/build/src/database/drivers/base.d.ts +96 -23
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +230 -90
- package/build/src/database/drivers/mssql/index.d.ts +4 -1
- package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/index.js +11 -8
- package/build/src/database/drivers/mysql/index.d.ts +2 -1
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +10 -6
- package/build/src/database/drivers/pgsql/index.d.ts +2 -1
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +5 -5
- package/build/src/database/drivers/sqlite/base.d.ts +1 -1
- package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/base.js +3 -3
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts +20 -0
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.js +56 -3
- package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.web.js +64 -1
- package/build/src/database/operation-connection.d.ts +67 -0
- package/build/src/database/operation-connection.d.ts.map +1 -0
- package/build/src/database/operation-connection.js +102 -0
- package/build/src/database/operation-lease.d.ts +27 -0
- package/build/src/database/operation-lease.d.ts.map +1 -0
- package/build/src/database/operation-lease.js +44 -0
- package/build/src/database/operation.d.ts +85 -0
- package/build/src/database/operation.d.ts.map +1 -0
- package/build/src/database/operation.js +132 -0
- package/build/src/database/pool/base.d.ts +14 -0
- package/build/src/database/pool/base.d.ts.map +1 -1
- package/build/src/database/pool/base.js +24 -2
- package/build/src/database/pool/single-multi-use.d.ts +1 -0
- package/build/src/database/pool/single-multi-use.d.ts.map +1 -1
- package/build/src/database/pool/single-multi-use.js +49 -1
- package/build/src/database/query/model-class-query.d.ts +14 -0
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +32 -8
- package/build/src/database/query/preloader/belongs-to.d.ts.map +1 -1
- package/build/src/database/query/preloader/belongs-to.js +4 -3
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +5 -4
- package/build/src/database/query/preloader/has-one.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-one.js +3 -2
- package/build/src/database/query/preloader/query-for-model.d.ts +10 -0
- package/build/src/database/query/preloader/query-for-model.d.ts.map +1 -0
- package/build/src/database/query/preloader/query-for-model.js +16 -0
- package/build/src/database/query/query-data.d.ts.map +1 -1
- package/build/src/database/query/query-data.js +13 -4
- package/build/src/database/query/with-count.d.ts.map +1 -1
- package/build/src/database/query/with-count.js +6 -4
- package/build/src/database/record/acts-as-list.js +12 -9
- package/build/src/database/record/attachments/attachment-record.d.ts +9 -0
- package/build/src/database/record/attachments/attachment-record.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.d.ts +4 -2
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +16 -12
- package/build/src/database/record/auditing.d.ts.map +1 -1
- package/build/src/database/record/auditing.js +13 -10
- package/build/src/database/record/counter-cache-magnitude.js +6 -4
- package/build/src/database/record/index.d.ts +42 -2
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +87 -15
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +4 -3
- package/build/src/database/record/instance-relationships/has-many.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/has-many.js +7 -5
- package/build/src/database/record/instance-relationships/has-one.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/has-one.js +4 -3
- package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
- package/build/src/database/record/validators/uniqueness.js +4 -3
- package/build/src/frontend-models/websocket-publishers.js +3 -3
- package/build/src/sync/server-sequence-allocator.d.ts +16 -4
- package/build/src/sync/server-sequence-allocator.d.ts.map +1 -1
- package/build/src/sync/server-sequence-allocator.js +37 -11
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +7 -3
- package/build/src/sync/sync-publisher.d.ts +2 -1
- package/build/src/sync/sync-publisher.d.ts.map +1 -1
- package/build/src/sync/sync-publisher.js +11 -6
- package/build/sync/server-sequence-allocator.js +43 -10
- package/build/sync/sync-client.js +6 -2
- package/build/sync/sync-publisher.js +10 -5
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/configuration.js +36 -0
- package/src/database/drivers/base.js +244 -93
- package/src/database/drivers/mssql/index.js +10 -7
- package/src/database/drivers/mysql/index.js +10 -5
- package/src/database/drivers/pgsql/index.js +4 -4
- package/src/database/drivers/sqlite/base.js +2 -2
- package/src/database/drivers/sqlite/connection-sql-js.js +62 -2
- package/src/database/drivers/sqlite/index.web.js +74 -0
- package/src/database/operation-connection.js +121 -0
- package/src/database/operation-lease.js +47 -0
- package/src/database/operation.js +150 -0
- package/src/database/pool/base.js +26 -1
- package/src/database/pool/single-multi-use.js +49 -0
- package/src/database/query/model-class-query.js +38 -7
- package/src/database/query/preloader/belongs-to.js +3 -2
- package/src/database/query/preloader/has-many.js +4 -3
- package/src/database/query/preloader/has-one.js +2 -1
- package/src/database/query/preloader/query-for-model.js +17 -0
- package/src/database/query/query-data.js +12 -3
- package/src/database/query/with-count.js +5 -3
- package/src/database/record/acts-as-list.js +11 -8
- package/src/database/record/attachments/store.js +15 -11
- package/src/database/record/auditing.js +12 -9
- package/src/database/record/counter-cache-magnitude.js +5 -3
- package/src/database/record/index.js +95 -14
- package/src/database/record/instance-relationships/belongs-to.js +4 -2
- package/src/database/record/instance-relationships/has-many.js +7 -5
- package/src/database/record/instance-relationships/has-one.js +4 -2
- package/src/database/record/validators/uniqueness.js +3 -2
- package/src/frontend-models/websocket-publishers.js +2 -2
- package/src/sync/server-sequence-allocator.js +43 -10
- package/src/sync/sync-client.js +6 -2
- package/src/sync/sync-publisher.js +10 -5
|
@@ -42,6 +42,12 @@
|
|
|
42
42
|
* @typedef {Record<string, ?>} QueryRowType
|
|
43
43
|
* @typedef {Array<QueryRowType>} QueryResultType
|
|
44
44
|
*/
|
|
45
|
+
/**
|
|
46
|
+
* TransactionCallbackFrame type.
|
|
47
|
+
* @typedef {object} TransactionCallbackFrame
|
|
48
|
+
* @property {Array<() => void | Promise<void>>} afterCommitCallbacks - Callbacks to merge or run after commit.
|
|
49
|
+
* @property {Array<() => void | Promise<void>>} beforeCommitCallbacks - Guards to run before this frame completes.
|
|
50
|
+
*/
|
|
45
51
|
/**
|
|
46
52
|
* RetryableDatabaseErrorResult type.
|
|
47
53
|
* @typedef {object} RetryableDatabaseErrorResult
|
|
@@ -61,6 +67,7 @@
|
|
|
61
67
|
* @property {boolean} [sessionTimeZone] - Whether to ensure the configured database session time zone before the query.
|
|
62
68
|
* @property {AbortSignal} [signal] - Aborts the in-flight query (destroying its connection) when it fires.
|
|
63
69
|
* @property {string} [sourceStack] - Stack captured at the caller boundary.
|
|
70
|
+
* @property {symbol} [operationOwner] - Opaque owner for an operation-leased connection.
|
|
64
71
|
*/
|
|
65
72
|
|
|
66
73
|
/**
|
|
@@ -127,6 +134,21 @@ import TableForeignKey from "../table-data/table-foreign-key.js"
|
|
|
127
134
|
import wait from "awaitery/build/wait.js"
|
|
128
135
|
import {optionalPositiveInteger} from "typanic"
|
|
129
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Marks a callback failure that happened after the owning transaction was durably committed.
|
|
139
|
+
* The public transaction boundary unwraps it before deadlock classification.
|
|
140
|
+
*/
|
|
141
|
+
class VelociousDatabaseAfterCommitCallbackError extends Error {
|
|
142
|
+
/**
|
|
143
|
+
* Runs constructor.
|
|
144
|
+
* @param {?} callbackError - Original callback failure.
|
|
145
|
+
*/
|
|
146
|
+
constructor(callbackError) {
|
|
147
|
+
super("Database afterCommit callback failed")
|
|
148
|
+
this.callbackError = callbackError
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
130
152
|
/**
|
|
131
153
|
* Runs now ms.
|
|
132
154
|
* @returns {number} - Current high-resolution-ish timestamp in milliseconds.
|
|
@@ -155,8 +177,8 @@ export default class VelociousDatabaseDriversBase {
|
|
|
155
177
|
idSeq = undefined
|
|
156
178
|
/**
|
|
157
179
|
* Narrows the runtime value to the documented type.
|
|
158
|
-
* @type {
|
|
159
|
-
|
|
180
|
+
* @type {TransactionCallbackFrame[]} */
|
|
181
|
+
_transactionCallbackFrames
|
|
160
182
|
/**
|
|
161
183
|
* Narrows the runtime value to the documented type.
|
|
162
184
|
* @type {Map<string, Promise<?>>} */
|
|
@@ -175,6 +197,11 @@ export default class VelociousDatabaseDriversBase {
|
|
|
175
197
|
_activeQuery = null
|
|
176
198
|
/** @type {Map<string, number>} */
|
|
177
199
|
_heldAdvisoryLocks = new Map()
|
|
200
|
+
/**
|
|
201
|
+
* Exclusive operation lease installed by a single-multi-use pool.
|
|
202
|
+
* @type {import("../operation-lease.js").default | undefined}
|
|
203
|
+
*/
|
|
204
|
+
_operationLease = undefined
|
|
178
205
|
|
|
179
206
|
/**
|
|
180
207
|
* Runs constructor.
|
|
@@ -186,7 +213,7 @@ export default class VelociousDatabaseDriversBase {
|
|
|
186
213
|
this.configuration = configuration
|
|
187
214
|
this.mutex = new Mutex() // Can be used to lock this instance for exclusive use
|
|
188
215
|
this.logger = new Logger(this)
|
|
189
|
-
this.
|
|
216
|
+
this._transactionCallbackFrames = []
|
|
190
217
|
this._transactionsCount = 0
|
|
191
218
|
this._transactionsActionsMutex = new Mutex()
|
|
192
219
|
this._schemaCache = new Map()
|
|
@@ -488,6 +515,46 @@ export default class VelociousDatabaseDriversBase {
|
|
|
488
515
|
return this.configuration
|
|
489
516
|
}
|
|
490
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Installs an operation lease atomically with ordinary transaction admission.
|
|
520
|
+
* @param {import("../operation-lease.js").default} operationLease - Active lease.
|
|
521
|
+
* @returns {Promise<void>} - Resolves once the lease owns transaction admission.
|
|
522
|
+
*/
|
|
523
|
+
async setOperationLease(operationLease) {
|
|
524
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
525
|
+
if (this._operationLease) throw new Error("A database operation lease is already active")
|
|
526
|
+
if (this._transactionsCount > 0) {
|
|
527
|
+
throw new Error("Cannot start a database operation while an unrelated ordinary transaction is already active")
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
this._operationLease = operationLease
|
|
531
|
+
})
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* Clears the matching operation lease.
|
|
536
|
+
* @param {import("../operation-lease.js").default} operationLease - Lease to clear.
|
|
537
|
+
* @returns {void}
|
|
538
|
+
*/
|
|
539
|
+
clearOperationLease(operationLease) {
|
|
540
|
+
if (this._operationLease !== operationLease) {
|
|
541
|
+
throw new Error("Cannot clear a database operation lease owned by another operation")
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
this._operationLease = undefined
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Waits for an unrelated operation lease to release.
|
|
549
|
+
* @param {symbol | undefined} operationOwner - Candidate operation owner.
|
|
550
|
+
* @returns {Promise<void>}
|
|
551
|
+
*/
|
|
552
|
+
async _waitForOperationLease(operationOwner) {
|
|
553
|
+
const operationLease = this._operationLease
|
|
554
|
+
|
|
555
|
+
if (operationLease) await operationLease.wait(operationOwner)
|
|
556
|
+
}
|
|
557
|
+
|
|
491
558
|
/**
|
|
492
559
|
* Runs get id seq.
|
|
493
560
|
* @returns {number | undefined} - The id seq.
|
|
@@ -739,9 +806,10 @@ export default class VelociousDatabaseDriversBase {
|
|
|
739
806
|
/**
|
|
740
807
|
* Runs last insert id.
|
|
741
808
|
* @abstract
|
|
809
|
+
* @param {QueryOptions} [_options] - Query ownership options.
|
|
742
810
|
* @returns {Promise<number>} - Resolves with the last insert id.
|
|
743
811
|
*/
|
|
744
|
-
lastInsertID() {
|
|
812
|
+
lastInsertID(_options = {}) {
|
|
745
813
|
throw new Error(`${this.constructor.name}#lastInsertID not implemented`)
|
|
746
814
|
}
|
|
747
815
|
|
|
@@ -932,12 +1000,16 @@ export default class VelociousDatabaseDriversBase {
|
|
|
932
1000
|
* The outermost transaction retries the whole callback on a deadlock / lock-wait-timeout,
|
|
933
1001
|
* because such errors roll the entire transaction back and the standard recovery is to
|
|
934
1002
|
* restart it. Nested savepoints let the deadlock bubble up to this outer retry.
|
|
935
|
-
* @
|
|
936
|
-
* @
|
|
1003
|
+
* @template T
|
|
1004
|
+
* @param {() => Promise<T>} callback - Callback function.
|
|
1005
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1006
|
+
* @returns {Promise<T>} - Resolves with the transaction result.
|
|
937
1007
|
*/
|
|
938
|
-
async transaction(callback) {
|
|
1008
|
+
async transaction(callback, options = {}) {
|
|
1009
|
+
await this._waitForOperationLease(options.operationOwner)
|
|
1010
|
+
|
|
939
1011
|
if (this._transactionsCount > 0) {
|
|
940
|
-
return await this._runTransactionAttempt(callback)
|
|
1012
|
+
return await this._runTransactionAttempt(callback, options)
|
|
941
1013
|
}
|
|
942
1014
|
|
|
943
1015
|
const args = this.getArgs()
|
|
@@ -950,8 +1022,10 @@ export default class VelociousDatabaseDriversBase {
|
|
|
950
1022
|
attempt++
|
|
951
1023
|
|
|
952
1024
|
try {
|
|
953
|
-
return await this._runTransactionAttempt(callback)
|
|
1025
|
+
return await this._runTransactionAttempt(callback, options)
|
|
954
1026
|
} catch (error) {
|
|
1027
|
+
if (error instanceof VelociousDatabaseAfterCommitCallbackError) throw error.callbackError
|
|
1028
|
+
|
|
955
1029
|
const retryInfo = error instanceof Error ? this.retryableDatabaseError(error) : {retry: false, reconnect: false}
|
|
956
1030
|
|
|
957
1031
|
if (retryInfo.deadlock && attempt < maxAttempts && this._transactionsCount == 0) {
|
|
@@ -993,46 +1067,53 @@ export default class VelociousDatabaseDriversBase {
|
|
|
993
1067
|
* Runs a single transaction attempt: starts a transaction (or a savepoint when nested), runs
|
|
994
1068
|
* `callback`, and commits — rolling back on error. {@link transaction} wraps this with deadlock
|
|
995
1069
|
* retry at the outermost level.
|
|
996
|
-
* @
|
|
1070
|
+
* @template T
|
|
1071
|
+
* @param {() => Promise<T>} callback - Callback function.
|
|
1072
|
+
* @param {Pick<QueryOptions, "operationOwner">} options - Transaction ownership.
|
|
997
1073
|
* @returns {Promise<?>} - Resolves with the transaction result.
|
|
998
1074
|
*/
|
|
999
|
-
async _runTransactionAttempt(callback) {
|
|
1075
|
+
async _runTransactionAttempt(callback, options) {
|
|
1000
1076
|
const savePointName = this.generateSavePointName()
|
|
1001
|
-
/**
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1077
|
+
/** @type {TransactionCallbackFrame} */
|
|
1078
|
+
const callbackFrame = {
|
|
1079
|
+
afterCommitCallbacks: [],
|
|
1080
|
+
beforeCommitCallbacks: []
|
|
1081
|
+
}
|
|
1005
1082
|
let transactionStarted = false
|
|
1006
1083
|
let savePointStarted = false
|
|
1007
1084
|
|
|
1008
|
-
this.
|
|
1085
|
+
this._transactionCallbackFrames.push(callbackFrame)
|
|
1009
1086
|
|
|
1010
|
-
|
|
1011
|
-
this.
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1087
|
+
try {
|
|
1088
|
+
if (this._transactionsCount == 0) {
|
|
1089
|
+
this.logger.debug("Start transaction")
|
|
1090
|
+
await this.startTransaction(options)
|
|
1091
|
+
transactionStarted = true
|
|
1092
|
+
} else {
|
|
1093
|
+
this.logger.debug("Start savepoint", savePointName)
|
|
1094
|
+
await this.startSavePoint(savePointName, options)
|
|
1095
|
+
savePointStarted = true
|
|
1096
|
+
}
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
this._transactionCallbackFrames.pop()
|
|
1099
|
+
throw error
|
|
1018
1100
|
}
|
|
1019
1101
|
|
|
1020
1102
|
let result
|
|
1021
1103
|
|
|
1022
1104
|
try {
|
|
1023
1105
|
result = await callback()
|
|
1106
|
+
await this._runBeforeCommitCallbacks(callbackFrame)
|
|
1024
1107
|
|
|
1025
1108
|
if (savePointStarted) {
|
|
1026
1109
|
this.logger.debug("Release savepoint", savePointName)
|
|
1027
|
-
await this.releaseSavePoint(savePointName)
|
|
1110
|
+
await this.releaseSavePoint(savePointName, options)
|
|
1028
1111
|
}
|
|
1029
1112
|
|
|
1030
1113
|
if (transactionStarted) {
|
|
1031
1114
|
this.logger.debug("Commit transaction")
|
|
1032
|
-
await this.commitTransaction()
|
|
1115
|
+
await this.commitTransaction(options)
|
|
1033
1116
|
}
|
|
1034
|
-
|
|
1035
|
-
await this._commitAfterCommitCallbackFrame()
|
|
1036
1117
|
} catch (error) {
|
|
1037
1118
|
if (error instanceof Error) {
|
|
1038
1119
|
this.logger.debug("Transaction error", error.message)
|
|
@@ -1040,58 +1121,86 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1040
1121
|
this.logger.debug("Transaction error", error)
|
|
1041
1122
|
}
|
|
1042
1123
|
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1124
|
+
try {
|
|
1125
|
+
let transactionRolledBack = false
|
|
1126
|
+
|
|
1127
|
+
if (savePointStarted) {
|
|
1128
|
+
this.logger.debug("Rollback savepoint", savePointName)
|
|
1129
|
+
try {
|
|
1130
|
+
await this.rollbackSavePoint(savePointName, options)
|
|
1131
|
+
} catch (savePointError) {
|
|
1132
|
+
const message = savePointError instanceof Error ? savePointError.message : `${savePointError}`
|
|
1133
|
+
|
|
1134
|
+
// MySQL sometimes drops savepoints unexpectedly; fall back to rolling back the full transaction
|
|
1135
|
+
if (message.includes("SAVEPOINT") || message.includes("ER_SP_DOES_NOT_EXIST")) {
|
|
1136
|
+
this.logger.debug("Savepoint rollback failed; rolling back entire transaction instead")
|
|
1137
|
+
await this.rollbackTransaction(options)
|
|
1138
|
+
transactionRolledBack = true
|
|
1139
|
+
} else {
|
|
1140
|
+
throw savePointError
|
|
1141
|
+
}
|
|
1059
1142
|
}
|
|
1060
1143
|
}
|
|
1061
|
-
}
|
|
1062
1144
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1145
|
+
// Only roll back if a transaction is still open. A nested savepoint whose rollback failed
|
|
1146
|
+
// falls back to rolling back the whole transaction (above), which already closed it and
|
|
1147
|
+
// dropped the count to 0; rolling back again here would issue a second ROLLBACK and drive
|
|
1148
|
+
// `_transactionsCount` below zero, which would then defeat the outermost deadlock-retry guard.
|
|
1149
|
+
if (transactionStarted && !transactionRolledBack && this._transactionsCount > 0) {
|
|
1150
|
+
this.logger.debug("Rollback transaction")
|
|
1151
|
+
await this.rollbackTransaction(options)
|
|
1152
|
+
}
|
|
1153
|
+
} finally {
|
|
1154
|
+
this._transactionCallbackFrames.pop()
|
|
1070
1155
|
}
|
|
1071
1156
|
|
|
1072
|
-
this._afterCommitCallbackFrames.pop()
|
|
1073
|
-
|
|
1074
1157
|
throw error
|
|
1075
1158
|
}
|
|
1076
1159
|
|
|
1160
|
+
try {
|
|
1161
|
+
await this._commitTransactionCallbackFrame()
|
|
1162
|
+
} catch (error) {
|
|
1163
|
+
throw new VelociousDatabaseAfterCommitCallbackError(error)
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1077
1166
|
return result
|
|
1078
1167
|
}
|
|
1079
1168
|
|
|
1169
|
+
/**
|
|
1170
|
+
* Registers a guard to run after the current transaction callback succeeds and before its
|
|
1171
|
+
* outer commit or nested savepoint release.
|
|
1172
|
+
* @param {() => void | Promise<void>} callback - Guard callback.
|
|
1173
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Callback ownership.
|
|
1174
|
+
* @returns {Promise<void>} - Resolves when the guard has been registered.
|
|
1175
|
+
*/
|
|
1176
|
+
async beforeCommit(callback, options = {}) {
|
|
1177
|
+
await this._waitForOperationLease(options.operationOwner)
|
|
1178
|
+
|
|
1179
|
+
const currentFrame = this._transactionCallbackFrames[this._transactionCallbackFrames.length - 1]
|
|
1180
|
+
|
|
1181
|
+
if (!currentFrame) throw new Error("beforeCommit requires an active transaction")
|
|
1182
|
+
|
|
1183
|
+
currentFrame.beforeCommitCallbacks.push(callback)
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1080
1186
|
/**
|
|
1081
1187
|
* Runs a callback after the surrounding transaction commits.
|
|
1082
1188
|
* If no transaction is active, the callback runs immediately.
|
|
1083
1189
|
* @param {() => void | Promise<void>} callback - Callback.
|
|
1190
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Callback ownership.
|
|
1084
1191
|
* @returns {Promise<void>} - Resolves when the callback has been registered or run.
|
|
1085
1192
|
*/
|
|
1086
|
-
async afterCommit(callback) {
|
|
1087
|
-
|
|
1193
|
+
async afterCommit(callback, options = {}) {
|
|
1194
|
+
await this._waitForOperationLease(options.operationOwner)
|
|
1195
|
+
|
|
1196
|
+
const currentFrame = this._transactionCallbackFrames[this._transactionCallbackFrames.length - 1]
|
|
1088
1197
|
|
|
1089
1198
|
if (!currentFrame) {
|
|
1090
1199
|
await callback()
|
|
1091
1200
|
return
|
|
1092
1201
|
}
|
|
1093
1202
|
|
|
1094
|
-
currentFrame.push(callback)
|
|
1203
|
+
currentFrame.afterCommitCallbacks.push(callback)
|
|
1095
1204
|
}
|
|
1096
1205
|
|
|
1097
1206
|
/**
|
|
@@ -1102,59 +1211,90 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1102
1211
|
|
|
1103
1212
|
/**
|
|
1104
1213
|
* Runs start transaction.
|
|
1214
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1105
1215
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1106
1216
|
*/
|
|
1107
|
-
async startTransaction() {
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1217
|
+
async startTransaction(options = {}) {
|
|
1218
|
+
while (true) {
|
|
1219
|
+
/** @type {import("../operation-lease.js").default | undefined} */
|
|
1220
|
+
let blockingOperationLease
|
|
1221
|
+
|
|
1222
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
1223
|
+
const operationLease = this._operationLease
|
|
1224
|
+
|
|
1225
|
+
if (operationLease && options.operationOwner !== operationLease.owner) {
|
|
1226
|
+
blockingOperationLease = operationLease
|
|
1227
|
+
return
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
await this._startTransactionAction(options)
|
|
1231
|
+
this._transactionsCount++
|
|
1232
|
+
})
|
|
1233
|
+
|
|
1234
|
+
if (!blockingOperationLease) return
|
|
1235
|
+
|
|
1236
|
+
await blockingOperationLease.wait(options.operationOwner)
|
|
1237
|
+
}
|
|
1112
1238
|
}
|
|
1113
1239
|
|
|
1114
1240
|
/**
|
|
1115
1241
|
* Runs start transaction action.
|
|
1242
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1116
1243
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1117
1244
|
*/
|
|
1118
|
-
async _startTransactionAction() {
|
|
1119
|
-
await this.query("BEGIN TRANSACTION")
|
|
1245
|
+
async _startTransactionAction(options = {}) {
|
|
1246
|
+
await this.query("BEGIN TRANSACTION", options)
|
|
1120
1247
|
}
|
|
1121
1248
|
|
|
1122
1249
|
/**
|
|
1123
1250
|
* Runs commit transaction.
|
|
1251
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1124
1252
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1125
1253
|
*/
|
|
1126
|
-
async commitTransaction() {
|
|
1254
|
+
async commitTransaction(options = {}) {
|
|
1127
1255
|
await this._transactionsActionsMutex.sync(async () => {
|
|
1128
|
-
await this._commitTransactionAction()
|
|
1256
|
+
await this._commitTransactionAction(options)
|
|
1129
1257
|
this._transactionsCount--
|
|
1130
1258
|
})
|
|
1131
1259
|
}
|
|
1132
1260
|
|
|
1133
1261
|
/**
|
|
1134
1262
|
* Runs commit transaction action.
|
|
1263
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1135
1264
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1136
1265
|
*/
|
|
1137
|
-
async _commitTransactionAction() {
|
|
1138
|
-
await this.query("COMMIT")
|
|
1266
|
+
async _commitTransactionAction(options = {}) {
|
|
1267
|
+
await this.query("COMMIT", options)
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/**
|
|
1271
|
+
* Runs every guard registered to the transaction frame.
|
|
1272
|
+
* @param {TransactionCallbackFrame} callbackFrame - Frame whose guards are completing.
|
|
1273
|
+
* @returns {Promise<void>} - Resolves when every guard accepts the commit.
|
|
1274
|
+
*/
|
|
1275
|
+
async _runBeforeCommitCallbacks(callbackFrame) {
|
|
1276
|
+
for (const callback of callbackFrame.beforeCommitCallbacks) {
|
|
1277
|
+
await callback()
|
|
1278
|
+
}
|
|
1139
1279
|
}
|
|
1140
1280
|
|
|
1141
1281
|
/**
|
|
1142
1282
|
* Merges committed callbacks into the parent transaction frame or runs them when the outermost commit completes.
|
|
1143
1283
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1144
1284
|
*/
|
|
1145
|
-
async
|
|
1146
|
-
const
|
|
1285
|
+
async _commitTransactionCallbackFrame() {
|
|
1286
|
+
const committedFrame = this._transactionCallbackFrames.pop()
|
|
1147
1287
|
|
|
1148
|
-
if (!
|
|
1288
|
+
if (!committedFrame || committedFrame.afterCommitCallbacks.length === 0) return
|
|
1149
1289
|
|
|
1150
|
-
const parentFrame = this.
|
|
1290
|
+
const parentFrame = this._transactionCallbackFrames[this._transactionCallbackFrames.length - 1]
|
|
1151
1291
|
|
|
1152
1292
|
if (parentFrame) {
|
|
1153
|
-
parentFrame.push(...
|
|
1293
|
+
parentFrame.afterCommitCallbacks.push(...committedFrame.afterCommitCallbacks)
|
|
1154
1294
|
return
|
|
1155
1295
|
}
|
|
1156
1296
|
|
|
1157
|
-
for (const callback of
|
|
1297
|
+
for (const callback of committedFrame.afterCommitCallbacks) {
|
|
1158
1298
|
await callback()
|
|
1159
1299
|
}
|
|
1160
1300
|
}
|
|
@@ -1183,6 +1323,7 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1183
1323
|
* @returns {Promise<QueryResultType>} - Resolves with the query.
|
|
1184
1324
|
*/
|
|
1185
1325
|
async query(sql, options = {}) {
|
|
1326
|
+
await this._waitForOperationLease(options.operationOwner)
|
|
1186
1327
|
this._assertWritableQuery(sql)
|
|
1187
1328
|
|
|
1188
1329
|
let tries = 0
|
|
@@ -1232,16 +1373,18 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1232
1373
|
/**
|
|
1233
1374
|
* Executes a mutation and returns the number of rows changed by that statement.
|
|
1234
1375
|
* @param {string} sql - Mutation SQL string.
|
|
1376
|
+
* @param {QueryOptions} [options] - Query ownership options.
|
|
1235
1377
|
* @returns {Promise<number>} - Affected row count.
|
|
1236
1378
|
*/
|
|
1237
|
-
async affectedRows(sql) {
|
|
1379
|
+
async affectedRows(sql, options = {}) {
|
|
1380
|
+
await this._waitForOperationLease(options.operationOwner)
|
|
1238
1381
|
this._assertWritableQuery(sql)
|
|
1239
|
-
await this.beforeQuery(sql,
|
|
1382
|
+
await this.beforeQuery(sql, options)
|
|
1240
1383
|
|
|
1241
1384
|
try {
|
|
1242
1385
|
return await this._affectedRowsActual(sql)
|
|
1243
1386
|
} finally {
|
|
1244
|
-
await this.afterQuery(sql,
|
|
1387
|
+
await this.afterQuery(sql, options)
|
|
1245
1388
|
}
|
|
1246
1389
|
}
|
|
1247
1390
|
|
|
@@ -1609,12 +1752,13 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1609
1752
|
|
|
1610
1753
|
/**
|
|
1611
1754
|
* Runs rollback transaction.
|
|
1755
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1612
1756
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1613
1757
|
*/
|
|
1614
|
-
async rollbackTransaction() {
|
|
1758
|
+
async rollbackTransaction(options = {}) {
|
|
1615
1759
|
await this._transactionsActionsMutex.sync(async () => {
|
|
1616
1760
|
try {
|
|
1617
|
-
await this._rollbackTransactionAction()
|
|
1761
|
+
await this._rollbackTransactionAction(options)
|
|
1618
1762
|
} finally {
|
|
1619
1763
|
this._transactionsCount--
|
|
1620
1764
|
|
|
@@ -1630,10 +1774,11 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1630
1774
|
|
|
1631
1775
|
/**
|
|
1632
1776
|
* Runs rollback transaction action.
|
|
1777
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1633
1778
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1634
1779
|
*/
|
|
1635
|
-
async _rollbackTransactionAction() {
|
|
1636
|
-
await this.query("ROLLBACK")
|
|
1780
|
+
async _rollbackTransactionAction(options = {}) {
|
|
1781
|
+
await this.query("ROLLBACK", options)
|
|
1637
1782
|
}
|
|
1638
1783
|
|
|
1639
1784
|
/**
|
|
@@ -1647,21 +1792,23 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1647
1792
|
/**
|
|
1648
1793
|
* Runs start save point.
|
|
1649
1794
|
* @param {string} savePointName - Save point name.
|
|
1795
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1650
1796
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1651
1797
|
*/
|
|
1652
|
-
async startSavePoint(savePointName) {
|
|
1798
|
+
async startSavePoint(savePointName, options = {}) {
|
|
1653
1799
|
await this._transactionsActionsMutex.sync(async () => {
|
|
1654
|
-
await this._startSavePointAction(savePointName)
|
|
1800
|
+
await this._startSavePointAction(savePointName, options)
|
|
1655
1801
|
})
|
|
1656
1802
|
}
|
|
1657
1803
|
|
|
1658
1804
|
/**
|
|
1659
1805
|
* Runs start save point action.
|
|
1660
1806
|
* @param {string} savePointName - Save point name.
|
|
1807
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1661
1808
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1662
1809
|
*/
|
|
1663
|
-
async _startSavePointAction(savePointName) {
|
|
1664
|
-
await this.query(`SAVEPOINT ${savePointName}
|
|
1810
|
+
async _startSavePointAction(savePointName, options = {}) {
|
|
1811
|
+
await this.query(`SAVEPOINT ${savePointName}`, options)
|
|
1665
1812
|
}
|
|
1666
1813
|
|
|
1667
1814
|
/**
|
|
@@ -1691,22 +1838,24 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1691
1838
|
/**
|
|
1692
1839
|
* Runs release save point.
|
|
1693
1840
|
* @param {string} savePointName - Save point name.
|
|
1841
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1694
1842
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1695
1843
|
*/
|
|
1696
|
-
async releaseSavePoint(savePointName) {
|
|
1844
|
+
async releaseSavePoint(savePointName, options = {}) {
|
|
1697
1845
|
await this._transactionsActionsMutex.sync(async () => {
|
|
1698
|
-
await this._releaseSavePointAction(savePointName)
|
|
1846
|
+
await this._releaseSavePointAction(savePointName, options)
|
|
1699
1847
|
})
|
|
1700
1848
|
}
|
|
1701
1849
|
|
|
1702
1850
|
/**
|
|
1703
1851
|
* Runs release save point action.
|
|
1704
1852
|
* @param {string} savePointName - Save point name.
|
|
1853
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1705
1854
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1706
1855
|
*/
|
|
1707
|
-
async _releaseSavePointAction(savePointName) {
|
|
1856
|
+
async _releaseSavePointAction(savePointName, options = {}) {
|
|
1708
1857
|
try {
|
|
1709
|
-
await this.query(`RELEASE SAVEPOINT ${savePointName}
|
|
1858
|
+
await this.query(`RELEASE SAVEPOINT ${savePointName}`, options)
|
|
1710
1859
|
} catch (error) {
|
|
1711
1860
|
const message = error instanceof Error ? error.message : `${error}`
|
|
1712
1861
|
|
|
@@ -1723,21 +1872,23 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1723
1872
|
/**
|
|
1724
1873
|
* Runs rollback save point.
|
|
1725
1874
|
* @param {string} savePointName - Save point name.
|
|
1875
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1726
1876
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1727
1877
|
*/
|
|
1728
|
-
async rollbackSavePoint(savePointName) {
|
|
1878
|
+
async rollbackSavePoint(savePointName, options = {}) {
|
|
1729
1879
|
await this._transactionsActionsMutex.sync(async () => {
|
|
1730
|
-
await this._rollbackSavePointAction(savePointName)
|
|
1880
|
+
await this._rollbackSavePointAction(savePointName, options)
|
|
1731
1881
|
})
|
|
1732
1882
|
}
|
|
1733
1883
|
|
|
1734
1884
|
/**
|
|
1735
1885
|
* Runs rollback save point action.
|
|
1736
1886
|
* @param {string} savePointName - Save point name.
|
|
1887
|
+
* @param {Pick<QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
1737
1888
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1738
1889
|
*/
|
|
1739
|
-
async _rollbackSavePointAction(savePointName) {
|
|
1740
|
-
await this.query(`ROLLBACK TO SAVEPOINT ${savePointName}
|
|
1890
|
+
async _rollbackSavePointAction(savePointName, options = {}) {
|
|
1891
|
+
await this.query(`ROLLBACK TO SAVEPOINT ${savePointName}`, options)
|
|
1741
1892
|
}
|
|
1742
1893
|
|
|
1743
1894
|
/**
|
|
@@ -463,8 +463,8 @@ export default class VelociousDatabaseDriversMssql extends Base{
|
|
|
463
463
|
})
|
|
464
464
|
}
|
|
465
465
|
|
|
466
|
-
async lastInsertID() {
|
|
467
|
-
const result = await this.query("SELECT SCOPE_IDENTITY() AS last_insert_id")
|
|
466
|
+
async lastInsertID(options = {}) {
|
|
467
|
+
const result = await this.query("SELECT SCOPE_IDENTITY() AS last_insert_id", options)
|
|
468
468
|
const lastInsertID = digg(result, 0, "last_insert_id")
|
|
469
469
|
|
|
470
470
|
if (lastInsertID === null) throw new Error("Couldn't get the last inserted ID")
|
|
@@ -534,29 +534,32 @@ export default class VelociousDatabaseDriversMssql extends Base{
|
|
|
534
534
|
/**
|
|
535
535
|
* Runs start save point action.
|
|
536
536
|
* @param {string} savePointName - Save point name.
|
|
537
|
+
* @param {Pick<import("../base.js").QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
537
538
|
* @returns {Promise<void>} - Resolves when complete.
|
|
538
539
|
*/
|
|
539
|
-
async _startSavePointAction(savePointName) {
|
|
540
|
-
await this.query(`SAVE TRANSACTION [${savePointName}]
|
|
540
|
+
async _startSavePointAction(savePointName, options = {}) {
|
|
541
|
+
await this.query(`SAVE TRANSACTION [${savePointName}]`, options)
|
|
541
542
|
}
|
|
542
543
|
|
|
543
544
|
/**
|
|
544
545
|
* Runs release save point action.
|
|
545
546
|
* @param {string} savePointName - Save point name.
|
|
547
|
+
* @param {Pick<import("../base.js").QueryOptions, "operationOwner">} [_options] - Transaction ownership.
|
|
546
548
|
* @returns {Promise<void>} - Resolves when complete.
|
|
547
549
|
*/
|
|
548
|
-
async _releaseSavePointAction(savePointName
|
|
550
|
+
async _releaseSavePointAction(savePointName, _options = {}) {
|
|
549
551
|
// Do nothing in MS-SQL.
|
|
550
552
|
}
|
|
551
553
|
|
|
552
554
|
/**
|
|
553
555
|
* Runs rollback save point action.
|
|
554
556
|
* @param {string} savePointName - Save point name.
|
|
557
|
+
* @param {Pick<import("../base.js").QueryOptions, "operationOwner">} [options] - Transaction ownership.
|
|
555
558
|
* @returns {Promise<void>} - Resolves when complete.
|
|
556
559
|
*/
|
|
557
|
-
async _rollbackSavePointAction(savePointName) {
|
|
560
|
+
async _rollbackSavePointAction(savePointName, options = {}) {
|
|
558
561
|
try {
|
|
559
|
-
await this.query(`ROLLBACK TRANSACTION [${savePointName}]
|
|
562
|
+
await this.query(`ROLLBACK TRANSACTION [${savePointName}]`, options)
|
|
560
563
|
} catch (error) {
|
|
561
564
|
const message = error instanceof Error ? error.message : `${error}`
|
|
562
565
|
|