velocious 1.0.617 → 1.0.619
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 +5 -1
- package/build/background-jobs/client.js +12 -3
- package/build/background-jobs/socket-request.js +4 -0
- package/build/database/migration/index.js +2 -1
- package/build/database/pool/async-tracked-multi-connection.js +101 -4
- package/build/database/pool/base.js +52 -10
- package/build/database/pool/single-multi-use.js +65 -4
- package/build/environment-handlers/base.js +1 -1
- package/build/environment-handlers/node.js +10 -4
- package/build/src/background-jobs/client.d.ts +4 -1
- package/build/src/background-jobs/client.d.ts.map +1 -1
- package/build/src/background-jobs/client.js +12 -4
- package/build/src/background-jobs/socket-request.d.ts.map +1 -1
- package/build/src/background-jobs/socket-request.js +4 -1
- package/build/src/database/migration/index.d.ts +3 -2
- package/build/src/database/migration/index.d.ts.map +1 -1
- package/build/src/database/migration/index.js +3 -2
- package/build/src/database/pool/async-tracked-multi-connection.d.ts +39 -1
- package/build/src/database/pool/async-tracked-multi-connection.d.ts.map +1 -1
- package/build/src/database/pool/async-tracked-multi-connection.js +100 -5
- package/build/src/database/pool/base.d.ts +37 -1
- package/build/src/database/pool/base.d.ts.map +1 -1
- package/build/src/database/pool/base.js +49 -11
- package/build/src/database/pool/single-multi-use.d.ts +30 -3
- package/build/src/database/pool/single-multi-use.d.ts.map +1 -1
- package/build/src/database/pool/single-multi-use.js +58 -5
- package/build/src/environment-handlers/base.d.ts +2 -1
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +2 -2
- package/build/src/environment-handlers/node.d.ts +3 -2
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +13 -5
- package/build/src/testing/shared-transaction-broker-client.d.ts +4 -2
- package/build/src/testing/shared-transaction-broker-client.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-broker-client.js +5 -3
- package/build/src/testing/shared-transaction-broker.d.ts +21 -0
- package/build/src/testing/shared-transaction-broker.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-broker.js +61 -18
- package/build/src/testing/shared-transaction-connection-coordinator.d.ts +5 -0
- package/build/src/testing/shared-transaction-connection-coordinator.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-connection-coordinator.js +28 -4
- package/build/src/testing/shared-transaction-proxy-driver.d.ts +22 -3
- package/build/src/testing/shared-transaction-proxy-driver.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-proxy-driver.js +51 -14
- package/build/src/testing/test-runner.d.ts +75 -0
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +201 -10
- package/build/src/testing/test-transaction-session.d.ts +129 -0
- package/build/src/testing/test-transaction-session.d.ts.map +1 -0
- package/build/src/testing/test-transaction-session.js +255 -0
- package/build/testing/shared-transaction-broker-client.js +4 -2
- package/build/testing/shared-transaction-broker.js +58 -16
- package/build/testing/shared-transaction-connection-coordinator.js +29 -3
- package/build/testing/shared-transaction-proxy-driver.js +52 -13
- package/build/testing/test-runner.js +198 -9
- package/build/testing/test-transaction-session.js +240 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/background-jobs/client.js +12 -3
- package/src/background-jobs/socket-request.js +4 -0
- package/src/database/migration/index.js +2 -1
- package/src/database/pool/async-tracked-multi-connection.js +101 -4
- package/src/database/pool/base.js +52 -10
- package/src/database/pool/single-multi-use.js +65 -4
- package/src/environment-handlers/base.js +1 -1
- package/src/environment-handlers/node.js +10 -4
- package/src/testing/shared-transaction-broker-client.js +4 -2
- package/src/testing/shared-transaction-broker.js +58 -16
- package/src/testing/shared-transaction-connection-coordinator.js +29 -3
- package/src/testing/shared-transaction-proxy-driver.js +52 -13
- package/src/testing/test-runner.js +198 -9
- package/src/testing/test-transaction-session.js +240 -0
package/README.md
CHANGED
|
@@ -2162,7 +2162,9 @@ option; the default configuration keeps sequential `TRUNCATE TABLE` requests. Se
|
|
|
2162
2162
|
|
|
2163
2163
|
Request tests share transaction-active, non-tenant database connections with their in-process HTTP handlers. Eligibility is evaluated when each request is dispatched, so a hook can start a transaction and issue a request in the same callback. This makes uncommitted setup visible to handlers while preserving rollback isolation. Without an active transaction, handlers use independent pooled connections, so concurrency and locking tests can opt out of transaction cleanup and exercise production-style connections. Shared connection state is scoped to the test lifecycle and cleared around each test. See [docs/testing-guidelines.md](docs/testing-guidelines.md#request-test-database-connections).
|
|
2164
2164
|
|
|
2165
|
-
Transactional tests also share active non-tenant connections with real forked, reusable pooled, and spawned background-job child runners through a per-attempt test-only loopback broker. Parent setup and child writes therefore occupy the same physical transaction and roll back together, including background-job persistence.
|
|
2165
|
+
Transactional tests also share active non-tenant connections with real forked, reusable pooled, and spawned background-job child runners through a per-attempt test-only loopback broker. Parent setup and child writes therefore occupy the same physical transaction and roll back together, including background-job persistence. Backend harnesses can use [`TestTransactionSession`](docs/test-transaction-sessions.md) to propagate an ephemeral capability to already-running services and lazily enroll exact tenant physical identities. Tests using `{transaction: false, truncate: true}` retain ordinary independent connections for DDL, lock contention, independent commits, and genuine concurrency. See [docs/testing-guidelines.md](docs/testing-guidelines.md#request-test-database-connections).
|
|
2166
|
+
|
|
2167
|
+
Multiple configured databases route by identifier. Tenant-only databases remain excluded by default; a test can explicitly call `registerTransactionalTenant({databaseIdentifier, tenant})` from its attempt args to share one transaction with same-process paths resolving that exact physical tenant configuration. That registration remains active through `afterEach` and is revoked, rolled back, and released afterward. Emergency cleanup for a lifecycle hung beyond timeout grace revokes pending setup before it can publish stale state, bounds cleanup waits, and discards its physical tenant connection, so stale resumed work cannot use a driver recycled into a successor attempt. See [docs/testing-guidelines.md](docs/testing-guidelines.md#in-process-test-database-connections).
|
|
2166
2168
|
|
|
2167
2169
|
Warm pooled children receive the active broker capability per job, discard retained proxy state when the capability changes, and fail closed if a transactional dispatch lacks coordinates. Child transaction/savepoint work holds a FIFO lease on the parent physical connection until the matching root release or rollback.
|
|
2168
2170
|
|
|
@@ -2520,6 +2522,8 @@ Set `deduplicateWhileQueued: true` to coalesce an enqueue onto the earliest iden
|
|
|
2520
2522
|
|
|
2521
2523
|
Use `options: {idempotencyKey}` when producer replay must converge on the original durable job across every state and even after terminal-job pruning. Ownership is scoped to the resolved job class name, resolved queue, and key; reusing that scope with changed canonical arguments or behavior-affecting options fails. This is distinct from queued-only deduplication, and ownership rows are intentionally retained until a future explicit reconciliation/deletion policy. See [durable idempotent enqueue](docs/background-jobs.md#durable-idempotent-enqueue).
|
|
2522
2524
|
|
|
2525
|
+
The Node producer rejects and destroys its one-shot socket when the main closes before acknowledging or when an enqueue acknowledgement stalls for 5 seconds. Because the main may already have committed the job, this is an ambiguous outcome: replay with the same durable `idempotencyKey` to recover the original job id without creating a duplicate. Direct `BackgroundJobsClient` users can set a different bounded `enqueueTimeoutMs` constructor option. See [durable idempotent enqueue](docs/background-jobs.md#durable-idempotent-enqueue).
|
|
2526
|
+
|
|
2523
2527
|
Select a non-default runtime explicitly with `options: {executionMode: "inline" | "forked" | "spawned"}`.
|
|
2524
2528
|
|
|
2525
2529
|
Inline jobs share the worker process and run concurrently up to `maxConcurrentInlineJobs`, so a single slow inline job no longer blocks the queue. A single worker can also override the configured cap explicitly:
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import timeout from "awaitery/build/timeout.js"
|
|
3
4
|
import configurationResolver from "../configuration-resolver.js"
|
|
4
5
|
import BackgroundJobsSocketRequest from "./socket-request.js"
|
|
5
6
|
|
|
7
|
+
const DEFAULT_ENQUEUE_TIMEOUT_MS = 5000
|
|
8
|
+
|
|
6
9
|
export default class BackgroundJobsClient {
|
|
7
10
|
/**
|
|
8
11
|
* Runs constructor.
|
|
9
12
|
* @param {object} [args] - Options.
|
|
10
13
|
* @param {import("../configuration.js").default} [args.configuration] - Configuration.
|
|
14
|
+
* @param {number} [args.enqueueTimeoutMs] - Maximum time to wait for an enqueue acknowledgement in milliseconds (default: 5000).
|
|
11
15
|
*/
|
|
12
|
-
constructor({configuration} = {}) {
|
|
16
|
+
constructor({configuration, enqueueTimeoutMs = DEFAULT_ENQUEUE_TIMEOUT_MS} = {}) {
|
|
13
17
|
this.configurationPromise = configuration ? Promise.resolve(configuration) : configurationResolver()
|
|
18
|
+
this.enqueueTimeoutMs = enqueueTimeoutMs
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
/**
|
|
@@ -35,7 +40,11 @@ export default class BackgroundJobsClient {
|
|
|
35
40
|
async enqueue({jobName, args, options}) {
|
|
36
41
|
const request = await this._request()
|
|
37
42
|
|
|
38
|
-
return await
|
|
43
|
+
return await timeout({
|
|
44
|
+
errorMessage: `Background job enqueue acknowledgement timed out after ${this.enqueueTimeoutMs}ms`,
|
|
45
|
+
timeout: this.enqueueTimeoutMs
|
|
46
|
+
}, async ({control}) => await request.run({
|
|
47
|
+
signal: control.signal,
|
|
39
48
|
onConnect: (jsonSocket) => {
|
|
40
49
|
jsonSocket.send({
|
|
41
50
|
type: "enqueue",
|
|
@@ -54,7 +63,7 @@ export default class BackgroundJobsClient {
|
|
|
54
63
|
reject(new Error(message.error || "Failed to enqueue job"))
|
|
55
64
|
}
|
|
56
65
|
}
|
|
57
|
-
})
|
|
66
|
+
}))
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
/**
|
|
@@ -89,6 +89,10 @@ export default class BackgroundJobsSocketRequest {
|
|
|
89
89
|
finish({}, () => reject(error))
|
|
90
90
|
})
|
|
91
91
|
|
|
92
|
+
jsonSocket.on("close", () => {
|
|
93
|
+
finish({destroy: true}, () => reject(new Error("Background jobs socket closed before the request was acknowledged")))
|
|
94
|
+
})
|
|
95
|
+
|
|
92
96
|
/**
|
|
93
97
|
* Handles the socket response message.
|
|
94
98
|
* @param {import("./types.js").BackgroundJobSocketMessage} message - Socket message.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/** @typedef {import("../table-data/table-column.js").TableColumnArgsType} AddColumnArgsType */
|
|
3
4
|
/**
|
|
4
5
|
* CreateTableIdArgsType type.
|
|
5
6
|
* @typedef {object} CreateTableIdArgsType
|
|
@@ -109,7 +110,7 @@ export default class VelociousDatabaseMigration {
|
|
|
109
110
|
* @param {string} tableName - Table name.
|
|
110
111
|
* @param {string} columnName - Column name.
|
|
111
112
|
* @param {string} columnType - Column type.
|
|
112
|
-
* @param {
|
|
113
|
+
* @param {AddColumnArgsType} [args] - Options object.
|
|
113
114
|
* @returns {Promise<void>} - Resolves when complete.
|
|
114
115
|
*/
|
|
115
116
|
async addColumn(tableName, columnName, columnType, args) {
|
|
@@ -55,6 +55,15 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
55
55
|
*/
|
|
56
56
|
_testSharedConnectionRegistration = undefined
|
|
57
57
|
|
|
58
|
+
/** Attempt-owned shared connections keyed by resolved physical configuration. */
|
|
59
|
+
_testSharedConnectionsByReuseKey = new Map()
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Concurrent providers selected by live async join context.
|
|
63
|
+
* @type {Map<import("./base.js").TestSharedConnectionRegistration, {matches: () => boolean, provider: () => import("../drivers/base.js").default | undefined}>}
|
|
64
|
+
*/
|
|
65
|
+
_testSharedConnectionProviders = new Map()
|
|
66
|
+
|
|
58
67
|
/**
|
|
59
68
|
* Connections.
|
|
60
69
|
* @type {import("../drivers/base.js").default[]} */
|
|
@@ -173,15 +182,16 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
173
182
|
/**
|
|
174
183
|
* Spawns and times a physical connection without retaining its configuration.
|
|
175
184
|
* @param {import("../../configuration-types.js").DatabaseConfigurationType} config - Resolved database configuration.
|
|
185
|
+
* @param {string} [reuseKey] - Exact resolved physical identity.
|
|
176
186
|
* @returns {Promise<import("../drivers/base.js").default>} - Connected driver.
|
|
177
187
|
*/
|
|
178
|
-
async spawnConnectionWithConfiguration(config) {
|
|
188
|
+
async spawnConnectionWithConfiguration(config, reuseKey) {
|
|
179
189
|
const startedAt = this.nowMs()
|
|
180
190
|
const profileContext = currentTestProfileContext(this.configuration)
|
|
181
191
|
let failed = true
|
|
182
192
|
|
|
183
193
|
try {
|
|
184
|
-
const connection = await super.spawnConnectionWithConfiguration(config)
|
|
194
|
+
const connection = await super.spawnConnectionWithConfiguration(config, reuseKey)
|
|
185
195
|
|
|
186
196
|
failed = false
|
|
187
197
|
const liveConnectionCount = this.liveConnectionCount() - this.connectionsBeingSpawned + 1
|
|
@@ -249,6 +259,30 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
249
259
|
if (this.connections.includes(connection)) await this.handleCheckedInIdleConnection()
|
|
250
260
|
}
|
|
251
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Permanently removes and closes a checked-out connection.
|
|
264
|
+
* @param {import("../drivers/base.js").default} connection - Connection that must not return to the pool.
|
|
265
|
+
*/
|
|
266
|
+
async discard(connection) {
|
|
267
|
+
const id = connection.getIdSeq()
|
|
268
|
+
const errors = []
|
|
269
|
+
|
|
270
|
+
this.untrackConnectionInUse(connection, id)
|
|
271
|
+
try {
|
|
272
|
+
await this.closeConnection(connection)
|
|
273
|
+
} catch (error) {
|
|
274
|
+
errors.push(error)
|
|
275
|
+
}
|
|
276
|
+
try {
|
|
277
|
+
await this.drainPendingCheckouts()
|
|
278
|
+
} catch (error) {
|
|
279
|
+
errors.push(error)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (errors.length === 1) throw errors[0]
|
|
283
|
+
if (errors.length > 1) throw new AggregateError(errors, "Failed to discard a database connection")
|
|
284
|
+
}
|
|
285
|
+
|
|
252
286
|
/**
|
|
253
287
|
* Runs close checked out connection after checkin failure.
|
|
254
288
|
* @param {import("../drivers/base.js").default} connection - Connection that failed check-in cleanup.
|
|
@@ -517,7 +551,7 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
517
551
|
try {
|
|
518
552
|
const environmentHandler = this.configuration.getEnvironmentHandler()
|
|
519
553
|
const connection = await environmentHandler.runWithTestProfileContext(profileContext, async () => {
|
|
520
|
-
return await this.spawnConnectionWithConfiguration(databaseConfig)
|
|
554
|
+
return await this.spawnConnectionWithConfiguration(databaseConfig, this.getConfigurationReuseKey(databaseConfig))
|
|
521
555
|
})
|
|
522
556
|
|
|
523
557
|
this.stampConnectionForConfigurationReuseKey(connection, reuseKey)
|
|
@@ -885,6 +919,13 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
885
919
|
|
|
886
920
|
if (!actualCallback) throw new Error("withConnection requires a callback")
|
|
887
921
|
|
|
922
|
+
const testSharedConnection = this.activeTestSharedConnection()
|
|
923
|
+
if (testSharedConnection && this.connectionMatchesCurrentConfiguration(testSharedConnection)) {
|
|
924
|
+
return await this.asyncLocalStorage.run(testSharedConnection.getIdSeq(), async () => {
|
|
925
|
+
return await actualCallback(testSharedConnection)
|
|
926
|
+
})
|
|
927
|
+
}
|
|
928
|
+
|
|
888
929
|
const connection = await this.checkout(options)
|
|
889
930
|
const id = connection.getIdSeq()
|
|
890
931
|
|
|
@@ -1079,12 +1120,46 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
1079
1120
|
return registration
|
|
1080
1121
|
}
|
|
1081
1122
|
|
|
1123
|
+
/**
|
|
1124
|
+
* Registers a provider selected by the current live async join context.
|
|
1125
|
+
* @param {{matches: () => boolean, provider: () => import("../drivers/base.js").default | undefined}} args - Context selector and provider.
|
|
1126
|
+
* @returns {import("./base.js").TestSharedConnectionRegistration} - Opaque scoped registration handle.
|
|
1127
|
+
*/
|
|
1128
|
+
registerTestSharedConnectionProvider(args) {
|
|
1129
|
+
const registration = {owner: Symbol("test-shared-connection-context-provider")}
|
|
1130
|
+
this._testSharedConnectionProviders.set(registration, args)
|
|
1131
|
+
return registration
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* Registers an attempt-owned connection for exactly one physical configuration.
|
|
1136
|
+
* @param {import("../drivers/base.js").default} connection - Attempt-owned connection.
|
|
1137
|
+
* @param {string} reuseKey - Resolved physical configuration identity.
|
|
1138
|
+
* @returns {import("./base.js").TestSharedConnectionRegistration} - Opaque registration handle.
|
|
1139
|
+
*/
|
|
1140
|
+
setTestSharedConnectionForConfiguration(connection, reuseKey) {
|
|
1141
|
+
const registration = {owner: Symbol("test-shared-physical-connection")}
|
|
1142
|
+
|
|
1143
|
+
this._testSharedConnectionsByReuseKey.set(reuseKey, {connection, registration})
|
|
1144
|
+
return registration
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1082
1147
|
/**
|
|
1083
1148
|
* Clears the current shared connection registration. A supplied stale registration
|
|
1084
1149
|
* cannot clear a provider installed by a newer lifecycle.
|
|
1085
1150
|
* @param {import("./base.js").TestSharedConnectionRegistration} [registration] - Opaque registration handle to clear conditionally.
|
|
1086
1151
|
* @returns {void} */
|
|
1087
1152
|
clearTestSharedConnection(registration) {
|
|
1153
|
+
if (registration && this._testSharedConnectionProviders.delete(registration)) return
|
|
1154
|
+
if (registration) {
|
|
1155
|
+
for (const [reuseKey, entry] of this._testSharedConnectionsByReuseKey) {
|
|
1156
|
+
if (entry.registration !== registration) continue
|
|
1157
|
+
this._testSharedConnectionsByReuseKey.delete(reuseKey)
|
|
1158
|
+
return
|
|
1159
|
+
}
|
|
1160
|
+
} else {
|
|
1161
|
+
this._testSharedConnectionsByReuseKey.clear()
|
|
1162
|
+
}
|
|
1088
1163
|
if (registration && registration !== this._testSharedConnectionRegistration) return
|
|
1089
1164
|
|
|
1090
1165
|
this._testSharedConnection = undefined
|
|
@@ -1103,18 +1178,40 @@ export default class VelociousDatabasePoolAsyncTrackedMultiConnection extends Ba
|
|
|
1103
1178
|
* @returns {T} - Callback result.
|
|
1104
1179
|
*/
|
|
1105
1180
|
runWithTestSharedConnection(callback) {
|
|
1106
|
-
const connection = this.
|
|
1181
|
+
const connection = this.activeTestSharedConnection()
|
|
1107
1182
|
|
|
1108
1183
|
if (!connection) return callback()
|
|
1109
1184
|
|
|
1110
1185
|
return this.asyncLocalStorage.run(connection.getIdSeq(), callback)
|
|
1111
1186
|
}
|
|
1112
1187
|
|
|
1188
|
+
/**
|
|
1189
|
+
* Resolves a test-shared connection only while its checkout ID is still owned by this pool.
|
|
1190
|
+
* Fallback-only registrations have no checkout ID and must enter the normal checkout path.
|
|
1191
|
+
* @returns {import("../drivers/base.js").default | undefined} - Active shared connection.
|
|
1192
|
+
*/
|
|
1193
|
+
activeTestSharedConnection() {
|
|
1194
|
+
const connection = this.testSharedConnection()
|
|
1195
|
+
const id = connection?.getIdSeq()
|
|
1196
|
+
|
|
1197
|
+
if (typeof id !== "number") return
|
|
1198
|
+
if (this.connectionsInUse[id] !== connection) return
|
|
1199
|
+
|
|
1200
|
+
return connection
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1113
1203
|
/**
|
|
1114
1204
|
* Resolves the connection currently eligible for in-process test request sharing.
|
|
1115
1205
|
* @returns {import("../drivers/base.js").default | undefined} - Shared connection.
|
|
1116
1206
|
*/
|
|
1117
1207
|
testSharedConnection() {
|
|
1208
|
+
for (const {matches, provider} of this._testSharedConnectionProviders.values()) {
|
|
1209
|
+
if (matches()) return provider()
|
|
1210
|
+
}
|
|
1211
|
+
const reuseKey = this.getConfigurationReuseKey()
|
|
1212
|
+
const physicalRegistration = this._testSharedConnectionsByReuseKey.get(reuseKey)
|
|
1213
|
+
|
|
1214
|
+
if (physicalRegistration) return physicalRegistration.connection
|
|
1118
1215
|
return this._testSharedConnectionProvider
|
|
1119
1216
|
? this._testSharedConnectionProvider()
|
|
1120
1217
|
: this._testSharedConnection
|
|
@@ -126,6 +126,16 @@ class VelociousDatabasePoolBase {
|
|
|
126
126
|
throw new Error("'checkin' not implemented")
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Permanently discards an attempt-owned checked-out connection.
|
|
131
|
+
* @abstract
|
|
132
|
+
* @param {import("../drivers/base.js").default} _connection - Connection that must not return to the pool.
|
|
133
|
+
* @returns {Promise<void>} - Resolves after the connection is closed and removed from pool ownership.
|
|
134
|
+
*/
|
|
135
|
+
discard(_connection) {
|
|
136
|
+
throw new Error("'discard' not implemented")
|
|
137
|
+
}
|
|
138
|
+
|
|
129
139
|
/**
|
|
130
140
|
* Runs checkout.
|
|
131
141
|
* @abstract
|
|
@@ -175,6 +185,26 @@ class VelociousDatabasePoolBase {
|
|
|
175
185
|
return undefined
|
|
176
186
|
}
|
|
177
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Registers a test shared connection selected by the caller's live async context.
|
|
190
|
+
* Base pools that do not track async context ignore it.
|
|
191
|
+
* @param {{matches: () => boolean, provider: () => import("../drivers/base.js").default | undefined}} _args - Context selector and connection provider.
|
|
192
|
+
* @returns {TestSharedConnectionRegistration | undefined} - Opaque scoped registration handle.
|
|
193
|
+
*/
|
|
194
|
+
registerTestSharedConnectionProvider(_args) {
|
|
195
|
+
return undefined
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Registers a test connection for one resolved physical database configuration.
|
|
200
|
+
* @param {import("../drivers/base.js").default} _connection - Attempt-owned connection.
|
|
201
|
+
* @param {string} _reuseKey - Resolved physical configuration identity.
|
|
202
|
+
* @returns {TestSharedConnectionRegistration | undefined} - Opaque registration handle when supported.
|
|
203
|
+
*/
|
|
204
|
+
setTestSharedConnectionForConfiguration(_connection, _reuseKey) {
|
|
205
|
+
return undefined
|
|
206
|
+
}
|
|
207
|
+
|
|
178
208
|
/**
|
|
179
209
|
* Clears the shared connection or provider set for in-process test requests. No-op by default.
|
|
180
210
|
* When a registration is provided, clears only if it is still the active registration.
|
|
@@ -337,7 +367,7 @@ class VelociousDatabasePoolBase {
|
|
|
337
367
|
*/
|
|
338
368
|
async spawnConnectionForConfiguration(databaseConfiguration) {
|
|
339
369
|
const reuseKey = this.getConfigurationReuseKey(databaseConfiguration)
|
|
340
|
-
const connection = await this.spawnConnectionWithConfiguration(databaseConfiguration)
|
|
370
|
+
const connection = await this.spawnConnectionWithConfiguration(databaseConfiguration, reuseKey)
|
|
341
371
|
|
|
342
372
|
this.stampConnectionForConfigurationReuseKey(connection, reuseKey)
|
|
343
373
|
|
|
@@ -368,21 +398,21 @@ class VelociousDatabasePoolBase {
|
|
|
368
398
|
/**
|
|
369
399
|
* Runs spawn connection with configuration.
|
|
370
400
|
* @param {import("../../configuration-types.js").DatabaseConfigurationType} config - Configuration object.
|
|
401
|
+
* @param {string} [reuseKey] - Exact resolved physical identity.
|
|
371
402
|
* @returns {Promise<import("../drivers/base.js").default>} - Resolves with the spawn connection with configuration.
|
|
372
403
|
*/
|
|
373
|
-
async spawnConnectionWithConfiguration(config) {
|
|
404
|
+
async spawnConnectionWithConfiguration(config, reuseKey) {
|
|
374
405
|
const DriverClass = config.driver || this.driverClass
|
|
375
406
|
|
|
376
407
|
if (!DriverClass) throw new Error("No driver class set in database pool or in given config")
|
|
377
408
|
|
|
378
|
-
const sharedConnection =
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
})
|
|
409
|
+
const sharedConnection = await this.configuration.getEnvironmentHandler().createTestSharedTransactionConnection({
|
|
410
|
+
DriverClass,
|
|
411
|
+
config,
|
|
412
|
+
configuration: this.configuration,
|
|
413
|
+
databaseIdentifier: this.identifier,
|
|
414
|
+
reuseKey
|
|
415
|
+
})
|
|
386
416
|
const connection = sharedConnection || new DriverClass(config, this.configuration)
|
|
387
417
|
|
|
388
418
|
try {
|
|
@@ -395,6 +425,18 @@ class VelociousDatabasePoolBase {
|
|
|
395
425
|
return connection
|
|
396
426
|
}
|
|
397
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Checks out a connection for an already-resolved physical configuration.
|
|
430
|
+
* Multi-configuration pools override this for explicit tenant registrations.
|
|
431
|
+
* @param {import("../../configuration-types.js").DatabaseConfigurationType} _databaseConfiguration - Captured configuration.
|
|
432
|
+
* @param {ConnectionCheckoutOptions} [_options] - Checkout options.
|
|
433
|
+
* @param {{retain: boolean}} [_args] - Pool-specific retention behavior.
|
|
434
|
+
* @returns {Promise<import("../drivers/base.js").default>} - Checked-out connection.
|
|
435
|
+
*/
|
|
436
|
+
async checkoutForConfiguration(_databaseConfiguration, _options, _args) {
|
|
437
|
+
throw new Error("Database pool does not support captured configuration checkout")
|
|
438
|
+
}
|
|
439
|
+
|
|
398
440
|
/**
|
|
399
441
|
* Runs close connection after failed connect.
|
|
400
442
|
* @param {import("../drivers/base.js").default} connection - Connection to close.
|
|
@@ -45,6 +45,8 @@ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
|
|
|
45
45
|
/** @type {Set<() => boolean>} */
|
|
46
46
|
capacityWaiters = new Set()
|
|
47
47
|
closeGeneration = 0
|
|
48
|
+
/** @type {Map<string, {connection: import("../drivers/base.js").default, registration: import("./base.js").TestSharedConnectionRegistration}>} */
|
|
49
|
+
testSharedConnectionsByReuseKey = new Map()
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
* Checks a connection back into its keyed physical entry.
|
|
@@ -80,6 +82,24 @@ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
|
|
|
80
82
|
if (!entry.lifecycleRetained && (!entry.retained || this.capacityWaiters.size > 0)) await this.removeAndCloseEntry(entry)
|
|
81
83
|
}
|
|
82
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Permanently removes and closes a checked-out connection.
|
|
87
|
+
* @param {import("../drivers/base.js").default} connection - Connection that must not return to the pool.
|
|
88
|
+
*/
|
|
89
|
+
async discard(connection) {
|
|
90
|
+
const entry = this.entryForConnection(connection)
|
|
91
|
+
|
|
92
|
+
if (entry) {
|
|
93
|
+
this.activeCheckoutCount -= entry.activeCheckoutCount
|
|
94
|
+
entry.activeCheckoutCount = 0
|
|
95
|
+
entry.checkoutNames = []
|
|
96
|
+
await this.removeAndCloseEntry(entry)
|
|
97
|
+
return
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await connection.close()
|
|
101
|
+
}
|
|
102
|
+
|
|
83
103
|
/**
|
|
84
104
|
* Checks out the ambient configuration and retains it as the single mutable
|
|
85
105
|
* browser fallback connection.
|
|
@@ -93,11 +113,11 @@ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
|
|
|
93
113
|
/**
|
|
94
114
|
* Checks out an explicitly captured physical configuration.
|
|
95
115
|
* @param {import("../../configuration-types.js").DatabaseConfigurationType} databaseConfiguration - Captured configuration.
|
|
96
|
-
* @param {import("./base.js").ConnectionCheckoutOptions} options - Checkout options.
|
|
97
|
-
* @param {{retain: boolean}} args - Whether this becomes the ambient retained connection.
|
|
116
|
+
* @param {import("./base.js").ConnectionCheckoutOptions} [options] - Checkout options.
|
|
117
|
+
* @param {{retain: boolean}} [args] - Whether this becomes the ambient retained connection.
|
|
98
118
|
* @returns {Promise<import("../drivers/base.js").default>} - Checked-out connection.
|
|
99
119
|
*/
|
|
100
|
-
async checkoutForConfiguration(databaseConfiguration, options, {retain}) {
|
|
120
|
+
async checkoutForConfiguration(databaseConfiguration, options = {}, {retain} = {retain: false}) {
|
|
101
121
|
const reuseKey = this.getConfigurationReuseKey(databaseConfiguration)
|
|
102
122
|
let entry = this.connectionEntries.get(reuseKey)
|
|
103
123
|
|
|
@@ -460,6 +480,7 @@ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
|
|
|
460
480
|
|
|
461
481
|
this.connectionEntries.clear()
|
|
462
482
|
this.connectionEntrySpawnPromises.clear()
|
|
483
|
+
this.testSharedConnectionsByReuseKey.clear()
|
|
463
484
|
this.connection = undefined
|
|
464
485
|
this.activeCheckoutCount = 0
|
|
465
486
|
|
|
@@ -476,6 +497,46 @@ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
|
|
|
476
497
|
return this.connection
|
|
477
498
|
}
|
|
478
499
|
|
|
500
|
+
/**
|
|
501
|
+
* Registers an attempt-owned connection for exactly one physical configuration.
|
|
502
|
+
* @param {import("../drivers/base.js").default} connection - Attempt-owned connection.
|
|
503
|
+
* @param {string} reuseKey - Resolved physical configuration identity.
|
|
504
|
+
* @returns {import("./base.js").TestSharedConnectionRegistration} - Opaque registration handle.
|
|
505
|
+
*/
|
|
506
|
+
setTestSharedConnectionForConfiguration(connection, reuseKey) {
|
|
507
|
+
const registration = {owner: Symbol("test-shared-physical-connection")}
|
|
508
|
+
|
|
509
|
+
this.testSharedConnectionsByReuseKey.set(reuseKey, {connection, registration})
|
|
510
|
+
return registration
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Clears an attempt-owned shared physical connection without revoking a newer owner.
|
|
515
|
+
* @param {import("./base.js").TestSharedConnectionRegistration} [registration] - Registration to clear conditionally.
|
|
516
|
+
*/
|
|
517
|
+
clearTestSharedConnection(registration) {
|
|
518
|
+
if (!registration) {
|
|
519
|
+
this.testSharedConnectionsByReuseKey.clear()
|
|
520
|
+
return
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
for (const [reuseKey, entry] of this.testSharedConnectionsByReuseKey) {
|
|
524
|
+
if (entry.registration !== registration) continue
|
|
525
|
+
this.testSharedConnectionsByReuseKey.delete(reuseKey)
|
|
526
|
+
return
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
/**
|
|
531
|
+
* Resolves the attempt-owned connection for the current physical configuration.
|
|
532
|
+
* @returns {import("../drivers/base.js").default | undefined} - Shared connection.
|
|
533
|
+
*/
|
|
534
|
+
testSharedConnection() {
|
|
535
|
+
const reuseKey = this.getConfigurationReuseKey()
|
|
536
|
+
|
|
537
|
+
return this.testSharedConnectionsByReuseKey.get(reuseKey)?.connection
|
|
538
|
+
}
|
|
539
|
+
|
|
479
540
|
/**
|
|
480
541
|
* Returns the current context fallback connection when it is not suppressed.
|
|
481
542
|
* @returns {import("../drivers/base.js").default | undefined} - Current fallback connection.
|
|
@@ -483,7 +544,7 @@ export default class VelociousDatabasePoolSingleMultiUser extends BasePool {
|
|
|
483
544
|
getCurrentContextConnection() {
|
|
484
545
|
if (this.suppressedConnectionContextCount > 0) return undefined
|
|
485
546
|
|
|
486
|
-
return this.connection
|
|
547
|
+
return this.testSharedConnection() || this.connection
|
|
487
548
|
}
|
|
488
549
|
|
|
489
550
|
/**
|
|
@@ -31,7 +31,7 @@ export default class VelociousEnvironmentHandlerBase {
|
|
|
31
31
|
/**
|
|
32
32
|
* Node test runtimes may replace a physical child connection with a broker
|
|
33
33
|
* proxy. Other environments never participate in this test-only protocol.
|
|
34
|
-
* @param {{DriverClass: typeof import("../database/drivers/base.js").default, config: import("../configuration-types.js").DatabaseConfigurationType, configuration: import("../configuration.js").default, databaseIdentifier: string}} _args - Connection details.
|
|
34
|
+
* @param {{DriverClass: typeof import("../database/drivers/base.js").default, config: import("../configuration-types.js").DatabaseConfigurationType, configuration: import("../configuration.js").default, databaseIdentifier: string, reuseKey?: string}} _args - Connection details.
|
|
35
35
|
* @returns {Promise<import("../database/drivers/base.js").default | undefined>} - Optional proxy.
|
|
36
36
|
*/
|
|
37
37
|
async createTestSharedTransactionConnection(_args) { return undefined }
|
|
@@ -40,7 +40,7 @@ import InitializerFromRequireContext from "../database/initializer-from-require-
|
|
|
40
40
|
import toImportSpecifier from "../utils/to-import-specifier.js"
|
|
41
41
|
import {validateTimeZone} from "../time-zone.js"
|
|
42
42
|
import AttachmentPathSource from "./node/attachment-path-source.js"
|
|
43
|
-
import { createSharedTransactionProxyDriver, sharedTransactionBrokerConfig } from "../testing/shared-transaction-proxy-driver.js"
|
|
43
|
+
import { automaticSharedTransactionBrokerOmits, createSharedTransactionProxyDriver, sharedTransactionBrokerConfig } from "../testing/shared-transaction-proxy-driver.js"
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* Defines this typedef.
|
|
@@ -95,6 +95,8 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
95
95
|
* @returns {typeof import("../database/pool/base.js").default} - Pool type for this context.
|
|
96
96
|
*/
|
|
97
97
|
resolveTestSharedTransactionPoolType({configuredPoolType, databaseIdentifier}) {
|
|
98
|
+
const databaseConfiguration = this.getConfiguration().getDatabaseIdentifier(databaseIdentifier)
|
|
99
|
+
if (databaseConfiguration.tenantOnly && automaticSharedTransactionBrokerOmits(databaseIdentifier)) return configuredPoolType
|
|
98
100
|
if (!sharedTransactionBrokerConfig(databaseIdentifier)) return configuredPoolType
|
|
99
101
|
|
|
100
102
|
return AsyncTrackedMultiConnectionPool
|
|
@@ -102,13 +104,17 @@ export default class VelociousEnvironmentHandlerNode extends Base{
|
|
|
102
104
|
|
|
103
105
|
/**
|
|
104
106
|
* Creates a test-only child proxy when TestRunner supplied an active broker.
|
|
105
|
-
* @param {{DriverClass: typeof import("../database/drivers/base.js").default, config: import("../configuration-types.js").DatabaseConfigurationType, configuration: import("../configuration.js").default, databaseIdentifier: string}} args - Connection details.
|
|
107
|
+
* @param {{DriverClass: typeof import("../database/drivers/base.js").default, config: import("../configuration-types.js").DatabaseConfigurationType, configuration: import("../configuration.js").default, databaseIdentifier: string, reuseKey?: string}} args - Connection details.
|
|
106
108
|
* @returns {Promise<import("../database/drivers/base.js").default | undefined>} - Optional proxy.
|
|
107
109
|
*/
|
|
108
|
-
async createTestSharedTransactionConnection({DriverClass, config, configuration, databaseIdentifier}) {
|
|
110
|
+
async createTestSharedTransactionConnection({DriverClass, config, configuration, databaseIdentifier, reuseKey}) {
|
|
111
|
+
if (config.tenantOnly && automaticSharedTransactionBrokerOmits(databaseIdentifier)) return undefined
|
|
109
112
|
const brokerConfig = sharedTransactionBrokerConfig(databaseIdentifier)
|
|
110
113
|
if (!brokerConfig) return undefined
|
|
111
|
-
return createSharedTransactionProxyDriver(DriverClass, config, configuration, databaseIdentifier,
|
|
114
|
+
return createSharedTransactionProxyDriver(DriverClass, config, configuration, databaseIdentifier, {
|
|
115
|
+
...brokerConfig,
|
|
116
|
+
reuseKey: brokerConfig.allowDynamicIdentities ? reuseKey : undefined
|
|
117
|
+
})
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
/**
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import BackgroundJobsSocketRequest from "./socket-request.js";
|
|
2
2
|
export default class BackgroundJobsClient {
|
|
3
3
|
configurationPromise: Promise<import("../configuration.js").default>;
|
|
4
|
+
enqueueTimeoutMs: number;
|
|
4
5
|
/**
|
|
5
6
|
* Runs constructor.
|
|
6
7
|
* @param {object} [args] - Options.
|
|
7
8
|
* @param {import("../configuration.js").default} [args.configuration] - Configuration.
|
|
9
|
+
* @param {number} [args.enqueueTimeoutMs] - Maximum time to wait for an enqueue acknowledgement in milliseconds (default: 5000).
|
|
8
10
|
*/
|
|
9
|
-
constructor({ configuration }?: {
|
|
11
|
+
constructor({ configuration, enqueueTimeoutMs }?: {
|
|
10
12
|
configuration?: import("../configuration.js").default;
|
|
13
|
+
enqueueTimeoutMs?: number;
|
|
11
14
|
});
|
|
12
15
|
/**
|
|
13
16
|
* Builds a one-shot client socket request from the resolved configuration.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/client.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/background-jobs/client.js"],"names":[],"mappings":"AAIA,OAAO,2BAA2B,MAAM,qBAAqB,CAAA;AAI7D,MAAM,CAAC,OAAO,OAAO,oBAAoB;IAQhC,oBAAoB;IACpB,gBAAgB;IARvB;;;;;OAKG;IACH,YAAY,EAAC,aAAa,EAAE,gBAA6C,EAAC,AAJvE,CAGF,EAFE;QAAqD,aAAa,AAAlE,CACA,EADQ,OAAO,qBAAqB,EAAE,OAAO,CAC7C;QAAsB,gBAAgB,AAAtC,CACF,EADU,MAAM,CAChB;KAC8E,EAG9E;IAED;;;OAGG;IACG,QAAQ,IAFD,OAAO,CAAC,2BAA2B,CAAC,CAOhD;IAED;;;;;;;OAOG;IACG,OAAO,CAAC,EAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAC,EALnC;QAAqB,OAAO,EAApB,MAAM,CACd;QAAmD,IAAI,EAA/C,KAAK,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAC5C;QAAyD,OAAO,AAAhE,CACA,EADQ,OAAO,YAAY,EAAE,oBAAoB,CACjD;KAEmC,GAFzB,OAAO,CAAC,MAAM,CAAC,CA6B3B;IAED;;;;;;;;OAQG;IACG,gBAAgB,CAAC,EAAC,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAC,EANzD;QAAqB,WAAW,EAAxB,MAAM,CACd;QAAqB,OAAO,EAApB,MAAM,CACd;QAAmD,IAAI,EAA/C,KAAK,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAC5C;QAAyD,OAAO,AAAhE,CACA,EADQ,OAAO,YAAY,EAAE,oBAAoB,CACjD;KAEyD,GAF/C,OAAO,CAAC,OAAO,YAAY,EAAE,8BAA8B,CAAC,CAwBxE;IAED;;;;;OAKG;IACG,eAAe,CAAC,EAAC,WAAW,EAAC,EAHhC;QAAqB,WAAW,EAAxB,MAAM,CACd;KAEgC,GAFtB,OAAO,CAAC,OAAO,YAAY,EAAE,+BAA+B,CAAC,CAoBzE;CACF"}
|