velocious 1.0.631 → 1.0.633
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 +18 -5
- package/build/application.js +30 -10
- package/build/background-jobs/forked-runner-child.js +26 -30
- package/build/background-jobs/job-runner.js +5 -7
- package/build/background-jobs/lifecycle-client.js +3 -3
- package/build/background-jobs/local-dispatcher.js +29 -18
- package/build/background-jobs/main.js +33 -28
- package/build/background-jobs/pooled-runner-child.js +17 -9
- package/build/background-jobs/runner-graceful-shutdown.js +26 -10
- package/build/background-jobs/worker.js +34 -13
- package/build/configuration-types.js +8 -0
- package/build/configuration.js +267 -32
- package/build/database/drivers/base.js +163 -119
- package/build/database/drivers/sqlite/index.web.js +19 -11
- package/build/database/migrator.js +8 -5
- package/build/database/pool/async-tracked-multi-connection.js +73 -24
- package/build/database/pool/base.js +5 -0
- package/build/database/pool/single-multi-use.js +43 -11
- package/build/database/record/attachments/store.js +35 -26
- package/build/environment-handlers/base.js +122 -9
- package/build/environment-handlers/node/cli/commands/background-jobs-worker.js +47 -16
- package/build/environment-handlers/node/cli/commands/server.js +7 -2
- package/build/environment-handlers/node.js +15 -9
- package/build/error-reporting/request-details.js +28 -31
- package/build/frontend-model-controller.js +19 -13
- package/build/http-server/client/index.js +5 -13
- package/build/http-server/client/request-runner.js +26 -9
- package/build/http-server/client/request-timing.js +19 -0
- package/build/http-server/client/websocket-session.js +54 -14
- package/build/http-server/websocket-events-host.js +10 -8
- package/build/http-server/worker-handler/worker-thread.js +13 -6
- package/build/initializer.js +19 -1
- package/build/log-redactor.js +341 -0
- package/build/routes/resolver.js +32 -4
- package/build/src/application.d.ts +7 -0
- package/build/src/application.d.ts.map +1 -1
- package/build/src/application.js +32 -12
- package/build/src/background-jobs/forked-runner-child.js +25 -31
- package/build/src/background-jobs/job-runner.d.ts +3 -1
- package/build/src/background-jobs/job-runner.d.ts.map +1 -1
- package/build/src/background-jobs/job-runner.js +6 -9
- package/build/src/background-jobs/lifecycle-client.d.ts +1 -0
- package/build/src/background-jobs/lifecycle-client.d.ts.map +1 -1
- package/build/src/background-jobs/lifecycle-client.js +4 -4
- package/build/src/background-jobs/local-dispatcher.d.ts.map +1 -1
- package/build/src/background-jobs/local-dispatcher.js +31 -20
- package/build/src/background-jobs/main.d.ts +0 -4
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +34 -32
- package/build/src/background-jobs/pooled-runner-child.js +18 -11
- package/build/src/background-jobs/runner-graceful-shutdown.d.ts +9 -0
- package/build/src/background-jobs/runner-graceful-shutdown.d.ts.map +1 -1
- package/build/src/background-jobs/runner-graceful-shutdown.js +27 -13
- package/build/src/background-jobs/worker.d.ts +5 -0
- package/build/src/background-jobs/worker.d.ts.map +1 -1
- package/build/src/background-jobs/worker.js +31 -18
- package/build/src/configuration-types.d.ts +21 -0
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +8 -1
- package/build/src/configuration.d.ts +94 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +239 -31
- package/build/src/database/drivers/base.d.ts +5 -1
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +159 -115
- package/build/src/database/drivers/sqlite/index.web.d.ts +2 -0
- package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.web.js +17 -11
- package/build/src/database/migrator.d.ts.map +1 -1
- package/build/src/database/migrator.js +9 -6
- package/build/src/database/pool/async-tracked-multi-connection.d.ts +15 -0
- package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
- package/build/src/database/pool/async-tracked-multi-connection.js +71 -21
- package/build/src/database/pool/base.d.ts +2 -0
- package/build/src/database/pool/base.d.ts.map +1 -1
- package/build/src/database/pool/base.js +5 -1
- package/build/src/database/pool/single-multi-use.d.ts.map +1 -1
- package/build/src/database/pool/single-multi-use.js +47 -13
- package/build/src/database/record/attachments/store.d.ts +6 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +31 -23
- package/build/src/environment-handlers/base.d.ts +79 -8
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +106 -10
- package/build/src/environment-handlers/node/cli/commands/background-jobs-worker.d.ts +36 -0
- package/build/src/environment-handlers/node/cli/commands/background-jobs-worker.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/background-jobs-worker.js +45 -16
- package/build/src/environment-handlers/node/cli/commands/server.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/server.js +8 -3
- package/build/src/environment-handlers/node.d.ts +3 -6
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +13 -10
- package/build/src/error-reporting/request-details.d.ts +8 -1
- package/build/src/error-reporting/request-details.d.ts.map +1 -1
- package/build/src/error-reporting/request-details.js +30 -32
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +20 -14
- package/build/src/http-server/client/index.d.ts.map +1 -1
- package/build/src/http-server/client/index.js +5 -12
- package/build/src/http-server/client/request-runner.d.ts.map +1 -1
- package/build/src/http-server/client/request-runner.js +23 -10
- package/build/src/http-server/client/request-timing.d.ts +13 -0
- package/build/src/http-server/client/request-timing.d.ts.map +1 -1
- package/build/src/http-server/client/request-timing.js +19 -1
- package/build/src/http-server/client/websocket-session.d.ts +9 -2
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +46 -15
- package/build/src/http-server/websocket-events-host.d.ts.map +1 -1
- package/build/src/http-server/websocket-events-host.js +11 -8
- package/build/src/http-server/worker-handler/worker-thread.d.ts.map +1 -1
- package/build/src/http-server/worker-handler/worker-thread.js +13 -6
- package/build/src/initializer.d.ts +14 -1
- package/build/src/initializer.d.ts.map +1 -1
- package/build/src/initializer.js +18 -2
- package/build/src/log-redactor.d.ts +97 -0
- package/build/src/log-redactor.d.ts.map +1 -0
- package/build/src/log-redactor.js +297 -0
- package/build/src/routes/resolver.d.ts +7 -0
- package/build/src/routes/resolver.d.ts.map +1 -1
- package/build/src/routes/resolver.js +26 -5
- package/build/src/testing/sqljs-test-database.d.ts +41 -0
- package/build/src/testing/sqljs-test-database.d.ts.map +1 -0
- package/build/src/testing/sqljs-test-database.js +72 -0
- package/build/src/testing/test-runner.d.ts +79 -6
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +498 -139
- package/build/src/testing/test.js +2 -2
- package/build/src/utils/shutdown-lifecycle.d.ts +13 -0
- package/build/src/utils/shutdown-lifecycle.d.ts.map +1 -1
- package/build/src/utils/shutdown-lifecycle.js +35 -26
- package/build/testing/sqljs-test-database.js +73 -0
- package/build/testing/test-runner.js +529 -141
- package/build/testing/test.js +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils/shutdown-lifecycle.js +33 -30
- package/package.json +1 -1
- package/scripts/test-browser.js +16 -29
- package/src/application.js +30 -10
- package/src/background-jobs/forked-runner-child.js +26 -30
- package/src/background-jobs/job-runner.js +5 -7
- package/src/background-jobs/lifecycle-client.js +3 -3
- package/src/background-jobs/local-dispatcher.js +29 -18
- package/src/background-jobs/main.js +33 -28
- package/src/background-jobs/pooled-runner-child.js +17 -9
- package/src/background-jobs/runner-graceful-shutdown.js +26 -10
- package/src/background-jobs/worker.js +34 -13
- package/src/configuration-types.js +8 -0
- package/src/configuration.js +267 -32
- package/src/database/drivers/base.js +163 -119
- package/src/database/drivers/sqlite/index.web.js +19 -11
- package/src/database/migrator.js +8 -5
- package/src/database/pool/async-tracked-multi-connection.js +73 -24
- package/src/database/pool/base.js +5 -0
- package/src/database/pool/single-multi-use.js +43 -11
- package/src/database/record/attachments/store.js +35 -26
- package/src/environment-handlers/base.js +122 -9
- package/src/environment-handlers/node/cli/commands/background-jobs-worker.js +47 -16
- package/src/environment-handlers/node/cli/commands/server.js +7 -2
- package/src/environment-handlers/node.js +15 -9
- package/src/error-reporting/request-details.js +28 -31
- package/src/frontend-model-controller.js +19 -13
- package/src/http-server/client/index.js +5 -13
- package/src/http-server/client/request-runner.js +26 -9
- package/src/http-server/client/request-timing.js +19 -0
- package/src/http-server/client/websocket-session.js +54 -14
- package/src/http-server/websocket-events-host.js +10 -8
- package/src/http-server/worker-handler/worker-thread.js +13 -6
- package/src/initializer.js +19 -1
- package/src/log-redactor.js +341 -0
- package/src/routes/resolver.js +32 -4
- package/src/testing/sqljs-test-database.js +73 -0
- package/src/testing/test-runner.js +529 -141
- package/src/testing/test.js +1 -1
- package/src/utils/shutdown-lifecycle.js +33 -30
|
@@ -170,7 +170,7 @@ import TableData from "../table-data/index.js"
|
|
|
170
170
|
import TableColumn from "../table-data/table-column.js"
|
|
171
171
|
import TableForeignKey from "../table-data/table-foreign-key.js"
|
|
172
172
|
import wait from "awaitery/build/wait.js"
|
|
173
|
-
import { optionalPositiveInteger } from "typanic"
|
|
173
|
+
import { ensureError, optionalPositiveInteger } from "typanic"
|
|
174
174
|
import {coordinateSharedTransactionConnection, runWithoutSharedTransactionCoordinatorOwner} from "../../testing/shared-transaction-connection-coordinator.js"
|
|
175
175
|
import { currentTestProfileContext } from "../../testing/test-profile-context.js"
|
|
176
176
|
import sha256Hex from "../../utils/sha256-hex.js"
|
|
@@ -1376,63 +1376,65 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1376
1376
|
async transaction(callback, options = {}) {
|
|
1377
1377
|
await this._waitForOperationLease(options.operationOwner)
|
|
1378
1378
|
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
const args = this.getArgs()
|
|
1384
|
-
const maxAttempts = optionalPositiveInteger(args.deadlockMaxRetries, "deadlockMaxRetries") ?? 8
|
|
1385
|
-
const configuredBaseWaitMs = optionalPositiveInteger(args.deadlockBaseWaitMs, "deadlockBaseWaitMs")
|
|
1386
|
-
const deadlockMaxWaitMs = optionalPositiveInteger(args.deadlockMaxWaitMs, "deadlockMaxWaitMs") ?? 1000
|
|
1387
|
-
let attempt = 0
|
|
1379
|
+
return await coordinateSharedTransactionConnection(this, async () => {
|
|
1380
|
+
if (this._transactionsCount > 0) {
|
|
1381
|
+
return await this._runTransactionAttempt(callback, options)
|
|
1382
|
+
}
|
|
1388
1383
|
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
const
|
|
1384
|
+
const args = this.getArgs()
|
|
1385
|
+
const maxAttempts = optionalPositiveInteger(args.deadlockMaxRetries, "deadlockMaxRetries") ?? 8
|
|
1386
|
+
const configuredBaseWaitMs = optionalPositiveInteger(args.deadlockBaseWaitMs, "deadlockBaseWaitMs")
|
|
1387
|
+
const deadlockMaxWaitMs = optionalPositiveInteger(args.deadlockMaxWaitMs, "deadlockMaxWaitMs") ?? 1000
|
|
1388
|
+
let attempt = 0
|
|
1392
1389
|
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
if (error instanceof VelociousDatabaseAfterCommitCallbackError) throw error.callbackError
|
|
1397
|
-
if (!(error instanceof Error)) throw error
|
|
1390
|
+
while (true) {
|
|
1391
|
+
attempt++
|
|
1392
|
+
const attemptStartedAtMs = this._nowMs()
|
|
1398
1393
|
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1394
|
+
try {
|
|
1395
|
+
return await this._runTransactionAttempt(callback, options)
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
if (error instanceof VelociousDatabaseAfterCommitCallbackError) throw error.callbackError
|
|
1398
|
+
if (!(error instanceof Error)) throw error
|
|
1399
|
+
|
|
1400
|
+
const retryInfo = this.retryableDatabaseError(error)
|
|
1401
|
+
const willRetry = Boolean(retryInfo.deadlock && attempt < maxAttempts && this._transactionsCount == 0)
|
|
1402
|
+
|
|
1403
|
+
if (willRetry) {
|
|
1404
|
+
this._reportDeadlockRetryDiagnostic({
|
|
1405
|
+
attempt,
|
|
1406
|
+
contentionKind: retryInfo.contentionKind || "deadlock",
|
|
1407
|
+
error,
|
|
1408
|
+
maxAttempts,
|
|
1409
|
+
transactionAttemptDurationMs: Math.max(0, this._nowMs() - attemptStartedAtMs),
|
|
1410
|
+
willRetry
|
|
1411
|
+
})
|
|
1412
|
+
|
|
1413
|
+
// An explicitly-configured base wins so the tuning knob is effective even on drivers
|
|
1414
|
+
// whose classifier supplies its own `waitMs` (MySQL/MariaDB return a fixed 50ms for
|
|
1415
|
+
// deadlocks); otherwise honor that classifier hint, then fall back to 50ms.
|
|
1416
|
+
const baseWaitMs = configuredBaseWaitMs ?? (typeof retryInfo.waitMs == "number" && retryInfo.waitMs > 0 ? retryInfo.waitMs : 50)
|
|
1417
|
+
|
|
1418
|
+
// Full-jitter exponential backoff: wait a uniform-random duration in
|
|
1419
|
+
// [0, min(base * 2^(attempt-1), cap)]. The doubling ceiling spreads retries out as
|
|
1420
|
+
// contention persists, and the jitter de-correlates transactions that deadlocked in
|
|
1421
|
+
// lockstep so they stop re-colliding on the same wait (the linear `base * attempt`
|
|
1422
|
+
// this replaces had every victim retry after an identical delay). `attempt` is
|
|
1423
|
+
// 1-based here, so 2^(attempt-1) is 1, 2, 4, ... The cap keeps the tail sub-second.
|
|
1424
|
+
const ceilingWaitMs = Math.min(baseWaitMs * (2 ** (attempt - 1)), deadlockMaxWaitMs)
|
|
1425
|
+
const jitteredWaitMs = Math.floor(Math.random() * (ceilingWaitMs + 1))
|
|
1426
|
+
|
|
1427
|
+
const loggedContentionKind = retryInfo.contentionKind || "transaction contention"
|
|
1428
|
+
|
|
1429
|
+
this.logger.warn(`Retrying transaction after ${loggedContentionKind} (attempt ${attempt}/${maxAttempts})`)
|
|
1430
|
+
await this._waitMs(jitteredWaitMs)
|
|
1431
|
+
continue
|
|
1432
|
+
}
|
|
1411
1433
|
|
|
1412
|
-
|
|
1413
|
-
// whose classifier supplies its own `waitMs` (MySQL/MariaDB return a fixed 50ms for
|
|
1414
|
-
// deadlocks); otherwise honor that classifier hint, then fall back to 50ms.
|
|
1415
|
-
const baseWaitMs = configuredBaseWaitMs ?? (typeof retryInfo.waitMs == "number" && retryInfo.waitMs > 0 ? retryInfo.waitMs : 50)
|
|
1416
|
-
|
|
1417
|
-
// Full-jitter exponential backoff: wait a uniform-random duration in
|
|
1418
|
-
// [0, min(base * 2^(attempt-1), cap)]. The doubling ceiling spreads retries out as
|
|
1419
|
-
// contention persists, and the jitter de-correlates transactions that deadlocked in
|
|
1420
|
-
// lockstep so they stop re-colliding on the same wait (the linear `base * attempt`
|
|
1421
|
-
// this replaces had every victim retry after an identical delay). `attempt` is
|
|
1422
|
-
// 1-based here, so 2^(attempt-1) is 1, 2, 4, ... The cap keeps the tail sub-second.
|
|
1423
|
-
const ceilingWaitMs = Math.min(baseWaitMs * (2 ** (attempt - 1)), deadlockMaxWaitMs)
|
|
1424
|
-
const jitteredWaitMs = Math.floor(Math.random() * (ceilingWaitMs + 1))
|
|
1425
|
-
|
|
1426
|
-
const loggedContentionKind = retryInfo.contentionKind || "transaction contention"
|
|
1427
|
-
|
|
1428
|
-
this.logger.warn(`Retrying transaction after ${loggedContentionKind} (attempt ${attempt}/${maxAttempts})`)
|
|
1429
|
-
await this._waitMs(jitteredWaitMs)
|
|
1430
|
-
continue
|
|
1434
|
+
throw error
|
|
1431
1435
|
}
|
|
1432
|
-
|
|
1433
|
-
throw error
|
|
1434
1436
|
}
|
|
1435
|
-
}
|
|
1437
|
+
}, options.operationOwner)
|
|
1436
1438
|
}
|
|
1437
1439
|
|
|
1438
1440
|
/**
|
|
@@ -1769,34 +1771,36 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1769
1771
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1770
1772
|
*/
|
|
1771
1773
|
async startTransaction(options = {}) {
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
await this._transactionsActionsMutex.sync(async () => {
|
|
1777
|
-
const operationLease = this._operationLease
|
|
1774
|
+
await coordinateSharedTransactionConnection(this, async () => {
|
|
1775
|
+
while (true) {
|
|
1776
|
+
/** @type {import("../operation-lease.js").default | undefined} */
|
|
1777
|
+
let blockingOperationLease
|
|
1778
1778
|
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
return
|
|
1782
|
-
}
|
|
1779
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
1780
|
+
const operationLease = this._operationLease
|
|
1783
1781
|
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1782
|
+
if (operationLease && options.operationOwner !== operationLease.owner) {
|
|
1783
|
+
blockingOperationLease = operationLease
|
|
1784
|
+
return
|
|
1785
|
+
}
|
|
1788
1786
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
this._resolveTransactionCompletion = resolve
|
|
1787
|
+
await this._runProfiledTransactionAction("start", async () => {
|
|
1788
|
+
await this._startTransactionAction(options)
|
|
1792
1789
|
})
|
|
1793
|
-
|
|
1794
|
-
})
|
|
1790
|
+
this._transactionsCount++
|
|
1795
1791
|
|
|
1796
|
-
|
|
1792
|
+
if (this._transactionsCount === 1) {
|
|
1793
|
+
this._transactionCompletionPromise = new Promise((resolve) => {
|
|
1794
|
+
this._resolveTransactionCompletion = resolve
|
|
1795
|
+
})
|
|
1796
|
+
}
|
|
1797
|
+
})
|
|
1797
1798
|
|
|
1798
|
-
|
|
1799
|
-
|
|
1799
|
+
if (!blockingOperationLease) return
|
|
1800
|
+
|
|
1801
|
+
await blockingOperationLease.wait(options.operationOwner)
|
|
1802
|
+
}
|
|
1803
|
+
}, options.operationOwner)
|
|
1800
1804
|
}
|
|
1801
1805
|
|
|
1802
1806
|
/**
|
|
@@ -1814,13 +1818,15 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1814
1818
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1815
1819
|
*/
|
|
1816
1820
|
async commitTransaction(options = {}) {
|
|
1817
|
-
await this
|
|
1818
|
-
await this.
|
|
1819
|
-
await this.
|
|
1821
|
+
await coordinateSharedTransactionConnection(this, async () => {
|
|
1822
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
1823
|
+
await this._runProfiledTransactionAction("commit", async () => {
|
|
1824
|
+
await this._commitTransactionAction(options)
|
|
1825
|
+
})
|
|
1826
|
+
this._transactionsCount--
|
|
1827
|
+
this._resolveCompletedTransaction()
|
|
1820
1828
|
})
|
|
1821
|
-
|
|
1822
|
-
this._resolveCompletedTransaction()
|
|
1823
|
-
})
|
|
1829
|
+
}, options.operationOwner)
|
|
1824
1830
|
}
|
|
1825
1831
|
|
|
1826
1832
|
/** Resolves the current outer transaction completion when it has finished. */
|
|
@@ -1998,7 +2004,10 @@ export default class VelociousDatabaseDriversBase {
|
|
|
1998
2004
|
const waitMs = typeof retryInfo.waitMs === "number" && Number.isFinite(retryInfo.waitMs) ? retryInfo.waitMs : 100
|
|
1999
2005
|
|
|
2000
2006
|
if (waitMs > 0) await wait(waitMs)
|
|
2001
|
-
|
|
2007
|
+
const sensitiveValues = requestTiming ? requestTiming.getLogSensitiveValues() : new Set()
|
|
2008
|
+
const loggedError = this.configuration.getLogRedactor().redactString(error.stack || error.message, sensitiveValues)
|
|
2009
|
+
|
|
2010
|
+
this.logger.warn(`Retrying query because failed with: ${loggedError}`)
|
|
2002
2011
|
// Retry
|
|
2003
2012
|
} else {
|
|
2004
2013
|
throw error
|
|
@@ -2064,17 +2073,32 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2064
2073
|
let result
|
|
2065
2074
|
|
|
2066
2075
|
try {
|
|
2067
|
-
|
|
2076
|
+
try {
|
|
2077
|
+
const runQueryActualWithHooks = async () => await this._queryActualWithHooks(querySql, options, originalSql)
|
|
2068
2078
|
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2079
|
+
if (requestTiming && tries === 1) {
|
|
2080
|
+
result = await requestTiming.measureDbQuery(runQueryActualWithHooks)
|
|
2081
|
+
} else if (requestTiming) {
|
|
2082
|
+
result = await requestTiming.measure("db", runQueryActualWithHooks)
|
|
2083
|
+
} else {
|
|
2084
|
+
result = await runQueryActualWithHooks()
|
|
2085
|
+
}
|
|
2086
|
+
} finally {
|
|
2087
|
+
this._activeQuery = previousActiveQuery
|
|
2075
2088
|
}
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2089
|
+
} catch (error) {
|
|
2090
|
+
if (options.logQuery !== false) {
|
|
2091
|
+
await this._logQuery({
|
|
2092
|
+
elapsedMs: nowMs() - startedAtMs,
|
|
2093
|
+
error: ensureError(error),
|
|
2094
|
+
logName: options.logName || "SQL",
|
|
2095
|
+
requestTiming,
|
|
2096
|
+
sourceStack: options.sourceStack,
|
|
2097
|
+
sql: originalSql
|
|
2098
|
+
})
|
|
2099
|
+
}
|
|
2100
|
+
|
|
2101
|
+
throw error
|
|
2078
2102
|
}
|
|
2079
2103
|
|
|
2080
2104
|
const elapsedMs = nowMs() - startedAtMs
|
|
@@ -2083,6 +2107,7 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2083
2107
|
await this._logQuery({
|
|
2084
2108
|
elapsedMs,
|
|
2085
2109
|
logName: options.logName || "SQL",
|
|
2110
|
+
requestTiming,
|
|
2086
2111
|
sourceStack: options.sourceStack,
|
|
2087
2112
|
sql: originalSql
|
|
2088
2113
|
})
|
|
@@ -2374,17 +2399,25 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2374
2399
|
* Runs log query.
|
|
2375
2400
|
* @param {object} args - Options object.
|
|
2376
2401
|
* @param {number} args.elapsedMs - Elapsed milliseconds.
|
|
2402
|
+
* @param {Error} [args.error] - Query failure, when the driver call failed.
|
|
2377
2403
|
* @param {string} args.logName - Query log subject.
|
|
2404
|
+
* @param {import("../../http-server/client/request-timing.js").default | undefined} args.requestTiming - Request timing.
|
|
2378
2405
|
* @param {string | undefined} args.sourceStack - Source stack.
|
|
2379
2406
|
* @param {string} args.sql - SQL string.
|
|
2380
2407
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2381
2408
|
*/
|
|
2382
|
-
async _logQuery({elapsedMs, logName, sourceStack, sql}) {
|
|
2409
|
+
async _logQuery({elapsedMs, error, logName, requestTiming, sourceStack, sql}) {
|
|
2383
2410
|
const logger = new Logger(logName, {configuration: this.configuration})
|
|
2384
2411
|
const sourceLine = this._querySourceLine(sourceStack)
|
|
2412
|
+
const sensitiveValues = requestTiming ? requestTiming.getLogSensitiveValues() : new Set()
|
|
2413
|
+
const redactor = this.configuration.getLogRedactor()
|
|
2414
|
+
const loggedSql = redactor.redactString(sql, sensitiveValues)
|
|
2415
|
+
const failure = error
|
|
2416
|
+
? ` FAILED ${error.name}: ${redactor.redactString(error.message, sensitiveValues)}`
|
|
2417
|
+
: ""
|
|
2385
2418
|
const message = sourceLine
|
|
2386
|
-
? `(${formatElapsedMs(elapsedMs)}) ${
|
|
2387
|
-
: `(${formatElapsedMs(elapsedMs)}) ${
|
|
2419
|
+
? `(${formatElapsedMs(elapsedMs)})${failure} ${loggedSql}\n ↳ ${sourceLine}`
|
|
2420
|
+
: `(${formatElapsedMs(elapsedMs)})${failure} ${loggedSql}`
|
|
2388
2421
|
|
|
2389
2422
|
await logger.info(message)
|
|
2390
2423
|
}
|
|
@@ -2533,23 +2566,28 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2533
2566
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2534
2567
|
*/
|
|
2535
2568
|
async rollbackTransaction(options = {}) {
|
|
2536
|
-
await this
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
await this.
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2569
|
+
await coordinateSharedTransactionConnection(this, async () => {
|
|
2570
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
2571
|
+
try {
|
|
2572
|
+
await this._runProfiledTransactionAction("rollback", async () => {
|
|
2573
|
+
await this._rollbackTransactionAction(options)
|
|
2574
|
+
})
|
|
2575
|
+
} finally {
|
|
2576
|
+
// Driver recovery may need to clear a stale physical transaction when
|
|
2577
|
+
// no logical transaction is active. Never let that cleanup underflow
|
|
2578
|
+
// the logical depth and turn the next root transaction into a savepoint.
|
|
2579
|
+
if (this._transactionsCount > 0) this._transactionsCount--
|
|
2580
|
+
this._resolveCompletedTransaction()
|
|
2581
|
+
|
|
2582
|
+
// A rolled-back transaction may have reverted DDL (e.g. a CREATE TABLE
|
|
2583
|
+
// run lazily inside the transaction), so any cached schema metadata is
|
|
2584
|
+
// now stale and must be invalidated. Without this, a later tableExists()
|
|
2585
|
+
// check can report a table that the rollback already removed, so callers
|
|
2586
|
+
// skip recreating it and then fail with "no such table".
|
|
2587
|
+
this.clearSchemaCache()
|
|
2588
|
+
}
|
|
2589
|
+
})
|
|
2590
|
+
}, options.operationOwner)
|
|
2553
2591
|
}
|
|
2554
2592
|
|
|
2555
2593
|
/**
|
|
@@ -2576,9 +2614,11 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2576
2614
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2577
2615
|
*/
|
|
2578
2616
|
async startSavePoint(savePointName, options = {}) {
|
|
2579
|
-
await this
|
|
2580
|
-
await this.
|
|
2581
|
-
|
|
2617
|
+
await coordinateSharedTransactionConnection(this, async () => {
|
|
2618
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
2619
|
+
await this._startSavePointAction(savePointName, options)
|
|
2620
|
+
})
|
|
2621
|
+
}, options.operationOwner)
|
|
2582
2622
|
}
|
|
2583
2623
|
|
|
2584
2624
|
/**
|
|
@@ -2622,9 +2662,11 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2622
2662
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2623
2663
|
*/
|
|
2624
2664
|
async releaseSavePoint(savePointName, options = {}) {
|
|
2625
|
-
await this
|
|
2626
|
-
await this.
|
|
2627
|
-
|
|
2665
|
+
await coordinateSharedTransactionConnection(this, async () => {
|
|
2666
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
2667
|
+
await this._releaseSavePointAction(savePointName, options)
|
|
2668
|
+
})
|
|
2669
|
+
}, options.operationOwner)
|
|
2628
2670
|
}
|
|
2629
2671
|
|
|
2630
2672
|
/**
|
|
@@ -2656,9 +2698,11 @@ export default class VelociousDatabaseDriversBase {
|
|
|
2656
2698
|
* @returns {Promise<void>} - Resolves when complete.
|
|
2657
2699
|
*/
|
|
2658
2700
|
async rollbackSavePoint(savePointName, options = {}) {
|
|
2659
|
-
await this
|
|
2660
|
-
await this.
|
|
2661
|
-
|
|
2701
|
+
await coordinateSharedTransactionConnection(this, async () => {
|
|
2702
|
+
await this._transactionsActionsMutex.sync(async () => {
|
|
2703
|
+
await this._rollbackSavePointAction(savePointName, options)
|
|
2704
|
+
})
|
|
2705
|
+
}, options.operationOwner)
|
|
2662
2706
|
}
|
|
2663
2707
|
|
|
2664
2708
|
/**
|
|
@@ -17,6 +17,9 @@ export default class VelociousDatabaseDriversSqliteWeb extends Base {
|
|
|
17
17
|
* @type {ConnectionSqlJs | undefined} */
|
|
18
18
|
_connection = undefined
|
|
19
19
|
|
|
20
|
+
/** @type {SqliteWebConnection | undefined} */
|
|
21
|
+
_externalConnection = undefined
|
|
22
|
+
|
|
20
23
|
/**
|
|
21
24
|
* Runs sql js locate file.
|
|
22
25
|
* @returns {(file: string) => string} - locateFile callback for sql.js.
|
|
@@ -34,18 +37,21 @@ export default class VelociousDatabaseDriversSqliteWeb extends Base {
|
|
|
34
37
|
async connect() {
|
|
35
38
|
this.args = this.getArgs()
|
|
36
39
|
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const persistence = await createSqliteWebPersistence({databaseName: this.databaseName()})
|
|
43
|
-
const SQL = await initSqlJs({locateFile: this.sqlJsLocateFile()})
|
|
44
|
-
const databaseContent = await persistence.load()
|
|
45
|
-
const connectionSqlJs = new ConnectionSqlJs(this, new SQL.Database(databaseContent), persistence)
|
|
40
|
+
if (this.args.getConnection) {
|
|
41
|
+
this._externalConnection = this.args.getConnection()
|
|
42
|
+
return
|
|
43
|
+
}
|
|
46
44
|
|
|
47
|
-
|
|
45
|
+
if (this.args.reset) {
|
|
46
|
+
await deleteSqliteWebPersistences({databaseName: this.databaseName()})
|
|
48
47
|
}
|
|
48
|
+
|
|
49
|
+
const persistence = await createSqliteWebPersistence({databaseName: this.databaseName()})
|
|
50
|
+
const SQL = await initSqlJs({locateFile: this.sqlJsLocateFile()})
|
|
51
|
+
const databaseContent = await persistence.load()
|
|
52
|
+
const connectionSqlJs = new ConnectionSqlJs(this, new SQL.Database(databaseContent), persistence)
|
|
53
|
+
|
|
54
|
+
this._connection = connectionSqlJs
|
|
49
55
|
}
|
|
50
56
|
|
|
51
57
|
async _close() {
|
|
@@ -152,7 +158,9 @@ export default class VelociousDatabaseDriversSqliteWeb extends Base {
|
|
|
152
158
|
*/
|
|
153
159
|
getConnection() {
|
|
154
160
|
if (this.args?.getConnection) {
|
|
155
|
-
|
|
161
|
+
if (!this._externalConnection) throw new Error("SQLite web external connection has not been initialized")
|
|
162
|
+
|
|
163
|
+
return this._externalConnection
|
|
156
164
|
} else {
|
|
157
165
|
if (!this._connection) throw new Error("SQLite web connection has not been initialized")
|
|
158
166
|
return this._connection
|
package/src/database/migrator.js
CHANGED
|
@@ -265,15 +265,18 @@ export default class VelociousDatabaseMigrator {
|
|
|
265
265
|
const environmentHandler = this.configuration.getEnvironmentHandler()
|
|
266
266
|
const dbs = await this.configuration.getCurrentConnections()
|
|
267
267
|
const filteredDbs = Object.fromEntries(
|
|
268
|
-
Object.entries(dbs).filter(([dbIdentifier]) =>
|
|
268
|
+
Object.entries(dbs).filter(([dbIdentifier]) => {
|
|
269
|
+
if (!this.handlesDatabaseIdentifier(dbIdentifier)) return false
|
|
270
|
+
|
|
271
|
+
return Boolean(this.configuration.getDatabaseIdentifier(dbIdentifier).migrations)
|
|
272
|
+
})
|
|
269
273
|
)
|
|
270
274
|
|
|
271
275
|
if (!environmentHandler || Object.keys(filteredDbs).length == 0) return
|
|
272
276
|
|
|
273
|
-
// Ensure
|
|
274
|
-
//
|
|
275
|
-
// framework
|
|
276
|
-
// schema:load of the dumped SQL) produces a complete DB in every environment.
|
|
277
|
+
// Ensure Velocious' own framework schema before the structure dump. The dump is
|
|
278
|
+
// gated to enabled environments, but migration-enabled databases must include
|
|
279
|
+
// framework tables so `db:migrate` and schema:load produce a complete database.
|
|
277
280
|
await environmentHandler.ensureFrameworkSchema({dbs: filteredDbs})
|
|
278
281
|
await environmentHandler.afterMigrations({dbs: filteredDbs})
|
|
279
282
|
}
|
|
@@ -17,6 +17,7 @@ import { currentTestProfileContext } from "../../testing/test-profile-context.js
|
|
|
17
17
|
* @property {number | null} timeoutAt - Timestamp when the checkout will time out, or null when disabled.
|
|
18
18
|
* @property {number | null} timeoutMillis - Milliseconds to wait before rejecting, or null when disabled.
|
|
19
19
|
* @property {ReturnType<typeof setTimeout> | undefined} timeoutTimer - Timer that rejects the pending checkout.
|
|
20
|
+
* @property {{revoked: boolean} | undefined} [testDatabaseAccessScope] - Database-access scope captured at enqueue.
|
|
20
21
|
* @property {import("../../testing/test-profiler.js").TestProfileAsyncContext | undefined} [testProfileContext] - Async-safe profile attribution captured at enqueue.
|
|
21
22
|
*/
|
|
22
23
|
export const CLOSED_CONNECTION = Symbol("velociousClosedConnection")
|
|
@@ -227,7 +228,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
227
228
|
const trackedConnection = /** @type {import("../drivers/base.js").default & {[CLOSED_CONNECTION]?: boolean, [CONNECTION_CHECKED_OUT_AT]?: number, [IDLE_CONNECTION_CHECKED_IN_AT]?: number}} */ (connection)
|
|
228
229
|
|
|
229
230
|
if (trackedConnection[CLOSED_CONNECTION]) {
|
|
230
|
-
this.untrackConnectionInUse(connection, id)
|
|
231
|
+
if (typeof id === "number") this.untrackConnectionInUse(connection, id)
|
|
231
232
|
await this.drainPendingCheckouts()
|
|
232
233
|
return
|
|
233
234
|
}
|
|
@@ -343,6 +344,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
343
344
|
* @returns {Promise<import("../drivers/base.js").default>} - Resolves with the checkout.
|
|
344
345
|
*/
|
|
345
346
|
async checkout(options = {}) {
|
|
347
|
+
this.assertDatabaseAccessAllowed()
|
|
346
348
|
let databaseConfig = this.getConfiguration()
|
|
347
349
|
let reuseKey = this.getConfigurationReuseKey(databaseConfig)
|
|
348
350
|
let connection = this.takeIdleConnectionForReuseKey(reuseKey)
|
|
@@ -380,6 +382,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
380
382
|
* @returns {Promise<import("../drivers/base.js").default>} - Activated pooled connection.
|
|
381
383
|
*/
|
|
382
384
|
async checkoutForConfiguration(databaseConfig, options = {}) {
|
|
385
|
+
this.assertDatabaseAccessAllowed()
|
|
383
386
|
const reuseKey = this.getConfigurationReuseKey(databaseConfig)
|
|
384
387
|
const lifecycleRetainedConnection = this.lifecycleRetainedConnections.get(reuseKey)
|
|
385
388
|
|
|
@@ -446,6 +449,11 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
446
449
|
* @returns {Promise<import("../drivers/base.js").default>} - Activated connection.
|
|
447
450
|
*/
|
|
448
451
|
async activateConnection(connection, options = {}) {
|
|
452
|
+
try {
|
|
453
|
+
this.assertDatabaseAccessAllowed()
|
|
454
|
+
} catch (error) {
|
|
455
|
+
await this.closeRejectedCheckoutAndThrow(connection, error)
|
|
456
|
+
}
|
|
449
457
|
if (connection.getIdSeq() !== undefined) throw new Error(`Connection already has an ID-seq - is it in use? ${connection.getIdSeq()}`)
|
|
450
458
|
|
|
451
459
|
const id = this.idSeq++
|
|
@@ -459,15 +467,48 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
459
467
|
|
|
460
468
|
try {
|
|
461
469
|
await connection.setConnectionCheckoutName(options.name)
|
|
470
|
+
this.assertDatabaseAccessAllowed()
|
|
462
471
|
} catch (error) {
|
|
463
|
-
|
|
464
|
-
|
|
472
|
+
await this.closeRejectedCheckoutAndThrow(connection, error, id)
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return connection
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Closes a rejected checkout, then hands freed capacity to queued callers.
|
|
480
|
+
* @param {import("../drivers/base.js").default} connection - Rejected connection.
|
|
481
|
+
* @param {ReturnType<typeof JSON.parse>} error - Access revocation error.
|
|
482
|
+
* @param {number} [id] - Assigned checkout id, if activation reached that stage.
|
|
483
|
+
* @returns {Promise<never>} - Always rejects with the access or cleanup errors.
|
|
484
|
+
*/
|
|
485
|
+
async closeRejectedCheckoutAndThrow(connection, error, id) {
|
|
486
|
+
if (id !== undefined) this.untrackConnectionInUse(connection, id)
|
|
487
|
+
|
|
488
|
+
/** @type {ReturnType<typeof JSON.parse>[]} */
|
|
489
|
+
const cleanupErrors = []
|
|
490
|
+
|
|
491
|
+
try {
|
|
465
492
|
await this.closeConnection(connection)
|
|
493
|
+
} catch (closeError) {
|
|
494
|
+
cleanupErrors.push(closeError)
|
|
495
|
+
}
|
|
466
496
|
|
|
467
|
-
|
|
497
|
+
try {
|
|
498
|
+
if (this.pendingCheckoutDrainPromise) {
|
|
499
|
+
this.pendingCheckoutDrainRequested = true
|
|
500
|
+
} else {
|
|
501
|
+
await this.drainPendingCheckouts()
|
|
502
|
+
}
|
|
503
|
+
} catch (drainError) {
|
|
504
|
+
cleanupErrors.push(drainError)
|
|
468
505
|
}
|
|
469
506
|
|
|
470
|
-
|
|
507
|
+
if (cleanupErrors.length > 0) {
|
|
508
|
+
throw new AggregateError([error, ...cleanupErrors], "Database checkout rejection cleanup failed", {cause: error})
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
throw error
|
|
471
512
|
}
|
|
472
513
|
|
|
473
514
|
/**
|
|
@@ -588,6 +629,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
588
629
|
timeoutAt: timeoutMillis === null ? null : enqueuedAt + timeoutMillis,
|
|
589
630
|
timeoutMillis,
|
|
590
631
|
timeoutTimer: undefined,
|
|
632
|
+
testDatabaseAccessScope: this.configuration.getEnvironmentHandler().currentTestDatabaseAccessScope(),
|
|
591
633
|
testProfileContext: currentTestProfileContext(this.configuration)
|
|
592
634
|
}
|
|
593
635
|
|
|
@@ -887,20 +929,23 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
887
929
|
const environmentHandler = this.configuration.getEnvironmentHandler()
|
|
888
930
|
|
|
889
931
|
return await environmentHandler.runWithTestProfileContext(checkout.testProfileContext, async () => {
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
932
|
+
return await environmentHandler.runWithCapturedTestDatabaseAccessScope(checkout.testDatabaseAccessScope, async () => {
|
|
933
|
+
let connection
|
|
934
|
+
|
|
935
|
+
try {
|
|
936
|
+
this.assertDatabaseAccessAllowed()
|
|
937
|
+
connection = await this.spawnConnectionForCheckout(
|
|
938
|
+
checkout.databaseConfig,
|
|
939
|
+
checkout.reuseKey,
|
|
940
|
+
checkout.testProfileContext
|
|
941
|
+
)
|
|
942
|
+
} catch (error) {
|
|
943
|
+
checkout.reject(error instanceof Error ? error : new Error("Failed to spawn database connection.", {cause: error}))
|
|
944
|
+
return
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
await this.resolvePendingCheckout(checkout, connection)
|
|
948
|
+
})
|
|
904
949
|
})
|
|
905
950
|
}
|
|
906
951
|
|
|
@@ -914,11 +959,13 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
914
959
|
const environmentHandler = this.configuration.getEnvironmentHandler()
|
|
915
960
|
|
|
916
961
|
return await environmentHandler.runWithTestProfileContext(checkout.testProfileContext, async () => {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
962
|
+
return await environmentHandler.runWithCapturedTestDatabaseAccessScope(checkout.testDatabaseAccessScope, async () => {
|
|
963
|
+
try {
|
|
964
|
+
checkout.resolve(await this.activateConnection(connection, checkout.options))
|
|
965
|
+
} catch (error) {
|
|
966
|
+
checkout.reject(error instanceof Error ? error : new Error("Failed to activate database connection.", {cause: error}))
|
|
967
|
+
}
|
|
968
|
+
})
|
|
922
969
|
})
|
|
923
970
|
}
|
|
924
971
|
|
|
@@ -945,6 +992,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
945
992
|
* @returns {Promise<T>} - Resolves with the callback result.
|
|
946
993
|
*/
|
|
947
994
|
async withConnection(optionsOrCallback, callback) {
|
|
995
|
+
this.assertDatabaseAccessAllowed()
|
|
948
996
|
const options = typeof optionsOrCallback == "function" ? {} : optionsOrCallback
|
|
949
997
|
const actualCallback = typeof optionsOrCallback == "function" ? optionsOrCallback : callback
|
|
950
998
|
|
|
@@ -1046,6 +1094,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
1046
1094
|
* @returns {import("../drivers/base.js").default} - The current connection.
|
|
1047
1095
|
*/
|
|
1048
1096
|
getCurrentConnection() {
|
|
1097
|
+
this.assertDatabaseAccessAllowed()
|
|
1049
1098
|
const id = this.asyncLocalStorage.getStore()
|
|
1050
1099
|
|
|
1051
1100
|
if (id === undefined) return this.currentFallbackConnectionOrFail()
|
|
@@ -120,6 +120,11 @@ class VelociousDatabasePoolBase {
|
|
|
120
120
|
this.logger = new Logger(this)
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/** Throws when a revoked test attempt tries to acquire database access. */
|
|
124
|
+
assertDatabaseAccessAllowed() {
|
|
125
|
+
this.configuration.assertDatabaseAccessAllowed()
|
|
126
|
+
}
|
|
127
|
+
|
|
123
128
|
/**
|
|
124
129
|
* Runs checkin.
|
|
125
130
|
* @abstract
|