velocious 1.0.617 → 1.0.618
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/build/background-jobs/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 +78 -1
- package/build/database/pool/base.js +32 -0
- package/build/database/pool/single-multi-use.js +65 -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 +20 -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 +75 -2
- package/build/src/database/pool/base.d.ts +25 -0
- package/build/src/database/pool/base.d.ts.map +1 -1
- package/build/src/database/pool/base.js +30 -1
- 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/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/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/testing/shared-transaction-connection-coordinator.js +29 -3
- package/build/testing/test-runner.js +198 -9
- 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 +78 -1
- package/src/database/pool/base.js +32 -0
- package/src/database/pool/single-multi-use.js +65 -4
- package/src/testing/shared-transaction-connection-coordinator.js +29 -3
- package/src/testing/test-runner.js +198 -9
|
@@ -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
|
/**
|
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
/** @
|
|
3
|
+
/** @typedef {{coordinator: (callback: () => Promise<unknown>) => Promise<unknown>, ownedQueue: Promise<void>, owner: symbol}} CoordinatorRegistration */
|
|
4
|
+
|
|
5
|
+
/** @type {WeakMap<object, CoordinatorRegistration>} */
|
|
4
6
|
const coordinators = new WeakMap()
|
|
5
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Serializes sibling work that inherited one coordinator owner without re-entering the broker queue.
|
|
10
|
+
* @template T
|
|
11
|
+
* @param {CoordinatorRegistration} registration - Physical connection registration.
|
|
12
|
+
* @param {() => Promise<T>} callback - Owned operation.
|
|
13
|
+
* @returns {Promise<T>} - Operation result.
|
|
14
|
+
*/
|
|
15
|
+
async function coordinateOwnedSharedTransactionConnection(registration, callback) {
|
|
16
|
+
const previous = registration.ownedQueue
|
|
17
|
+
/**
|
|
18
|
+
* Releases the next owned sibling operation.
|
|
19
|
+
* @type {() => void}
|
|
20
|
+
*/
|
|
21
|
+
let release = () => {}
|
|
22
|
+
|
|
23
|
+
registration.ownedQueue = new Promise((resolve) => { release = resolve })
|
|
24
|
+
await previous
|
|
25
|
+
try {
|
|
26
|
+
return await callback()
|
|
27
|
+
} finally {
|
|
28
|
+
release()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
6
32
|
/**
|
|
7
33
|
* Registers test-only serialization owned by the active broker.
|
|
8
34
|
* @param {object} connection - Parent physical connection.
|
|
@@ -12,7 +38,7 @@ const coordinators = new WeakMap()
|
|
|
12
38
|
export function setSharedTransactionCoordinator(connection, coordinator) {
|
|
13
39
|
const owner = Symbol("shared-transaction-coordinator")
|
|
14
40
|
|
|
15
|
-
coordinators.set(connection, {coordinator, owner})
|
|
41
|
+
coordinators.set(connection, {coordinator, ownedQueue: Promise.resolve(), owner})
|
|
16
42
|
return owner
|
|
17
43
|
}
|
|
18
44
|
|
|
@@ -43,7 +69,7 @@ export async function coordinateSharedTransactionConnection(connection, callback
|
|
|
43
69
|
const environmentHandler = connection.configuration.getEnvironmentHandler()
|
|
44
70
|
|
|
45
71
|
if (environmentHandler.getSharedTransactionCoordinatorOwner(connection) === registration.owner) {
|
|
46
|
-
return await callback
|
|
72
|
+
return await coordinateOwnedSharedTransactionConnection(registration, callback)
|
|
47
73
|
}
|
|
48
74
|
|
|
49
75
|
return /** @type {T} */ (await registration.coordinator(async () => {
|
|
@@ -39,6 +39,7 @@ import { SHARED_TRANSACTION_BROKER_ENV } from "./shared-transaction-proxy-driver
|
|
|
39
39
|
* @property {string[] | string} [tags] - Tags for filtering.
|
|
40
40
|
* @property {number} [timeoutSeconds] - Timeout in seconds for the test.
|
|
41
41
|
* @property {string} [type] - Test type identifier.
|
|
42
|
+
* @property {(args: {databaseIdentifier: string, tenant: object}) => Promise<void>} [registerTransactionalTenant] - Registers one resolved tenant database transaction for this attempt.
|
|
42
43
|
*/
|
|
43
44
|
/**
|
|
44
45
|
* TestData type.
|
|
@@ -118,6 +119,18 @@ import { SHARED_TRANSACTION_BROKER_ENV } from "./shared-transaction-proxy-driver
|
|
|
118
119
|
* @property {boolean} environmentPublished - Whether child-process coordinates were published.
|
|
119
120
|
* @property {string | undefined} previousEnvironment - Environment value to restore after publication.
|
|
120
121
|
*/
|
|
122
|
+
/**
|
|
123
|
+
* TransactionalTenantRegistration type.
|
|
124
|
+
* @typedef {object} TransactionalTenantRegistration
|
|
125
|
+
* @property {Promise<{connection: import("../database/drivers/base.js").default | undefined, error: Error | undefined}> | undefined} [checkoutPromise] - Attempt-owned physical checkout outcome.
|
|
126
|
+
* @property {import("../database/drivers/base.js").default | undefined} connection - Attempt-owned physical connection once checkout resolves.
|
|
127
|
+
* @property {Promise<void> | undefined} [cleanupPromise] - Single cleanup operation shared by emergency and eventual lifecycle cleanup.
|
|
128
|
+
* @property {boolean | undefined} [discardOnCleanup] - Whether timeout emergency cleanup must quarantine this connection.
|
|
129
|
+
* @property {import("../database/pool/base.js").default} pool - Owning logical pool.
|
|
130
|
+
* @property {boolean} revoked - Whether this attempt may still publish the physical registration.
|
|
131
|
+
* @property {string} reuseKey - Resolved physical configuration identity.
|
|
132
|
+
* @property {import("../database/pool/base.js").TestSharedConnectionRegistration | undefined} sharedRegistration - Physical-key shared registration once published.
|
|
133
|
+
*/
|
|
121
134
|
|
|
122
135
|
/**
|
|
123
136
|
* Runs run with timeout.
|
|
@@ -641,6 +654,138 @@ export default class TestRunner {
|
|
|
641
654
|
}
|
|
642
655
|
}
|
|
643
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Checks out and registers one physical tenant transaction for the current attempt.
|
|
659
|
+
* @param {{databaseIdentifier: string, tenant: object}} args - Logical identifier and tenant descriptor.
|
|
660
|
+
* @param {TransactionalTenantRegistration[]} registrations - Current attempt registrations.
|
|
661
|
+
* @returns {Promise<void>}
|
|
662
|
+
*/
|
|
663
|
+
async registerTransactionalTenant({databaseIdentifier, tenant, ...restArgs}, registrations) {
|
|
664
|
+
restArgsError(restArgs)
|
|
665
|
+
if (!databaseIdentifier) throw new Error("registerTransactionalTenant requires a databaseIdentifier")
|
|
666
|
+
if (!tenant) throw new Error("registerTransactionalTenant requires a tenant")
|
|
667
|
+
|
|
668
|
+
const configuration = this.getConfiguration()
|
|
669
|
+
const pool = configuration.getDatabasePool(databaseIdentifier)
|
|
670
|
+
const databaseConfiguration = configuration.resolveDatabaseConfiguration(databaseIdentifier, tenant)
|
|
671
|
+
if (!databaseConfiguration.tenantOnly) {
|
|
672
|
+
throw new Error(`registerTransactionalTenant requires a tenantOnly database: ${databaseIdentifier}`)
|
|
673
|
+
}
|
|
674
|
+
const reuseKey = pool.getConfigurationReuseKey(databaseConfiguration)
|
|
675
|
+
if (registrations.some((registration) => registration.pool === pool && registration.reuseKey === reuseKey)) return
|
|
676
|
+
|
|
677
|
+
/** @type {TransactionalTenantRegistration} */
|
|
678
|
+
const registration = {
|
|
679
|
+
connection: undefined,
|
|
680
|
+
pool,
|
|
681
|
+
reuseKey,
|
|
682
|
+
revoked: false,
|
|
683
|
+
sharedRegistration: undefined
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
registrations.push(registration)
|
|
687
|
+
registration.checkoutPromise = pool
|
|
688
|
+
.checkoutForConfiguration(databaseConfiguration, {name: "Transactional tenant test registration"})
|
|
689
|
+
.then(
|
|
690
|
+
(connection) => ({connection, error: undefined}),
|
|
691
|
+
(error) => ({
|
|
692
|
+
connection: undefined,
|
|
693
|
+
error: error instanceof Error ? error : new Error("Transactional tenant connection checkout failed", {cause: error})
|
|
694
|
+
})
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
try {
|
|
698
|
+
const checkoutOutcome = await registration.checkoutPromise
|
|
699
|
+
|
|
700
|
+
if (checkoutOutcome.error) throw checkoutOutcome.error
|
|
701
|
+
if (!checkoutOutcome.connection) throw new Error("Transactional tenant connection checkout returned no connection")
|
|
702
|
+
registration.connection = checkoutOutcome.connection
|
|
703
|
+
if (registration.revoked) throw new Error("Transactional tenant test registration attempt is no longer active")
|
|
704
|
+
|
|
705
|
+
await registration.connection.startTransaction()
|
|
706
|
+
if (registration.revoked) throw new Error("Transactional tenant test registration attempt is no longer active")
|
|
707
|
+
|
|
708
|
+
const sharedRegistration = pool.setTestSharedConnectionForConfiguration(registration.connection, reuseKey)
|
|
709
|
+
if (!sharedRegistration) throw new Error(`Database pool does not support transactional tenant test connections: ${databaseIdentifier}`)
|
|
710
|
+
registration.sharedRegistration = sharedRegistration
|
|
711
|
+
if (registration.revoked) {
|
|
712
|
+
pool.clearTestSharedConnection(sharedRegistration)
|
|
713
|
+
throw new Error("Transactional tenant test registration attempt is no longer active")
|
|
714
|
+
}
|
|
715
|
+
} catch (error) {
|
|
716
|
+
registration.revoked = true
|
|
717
|
+
try {
|
|
718
|
+
await this.cleanupTransactionalTenants([registration], {discard: registration.discardOnCleanup === true})
|
|
719
|
+
} catch (cleanupError) {
|
|
720
|
+
throw new AggregateError([error, cleanupError], "Failed to register and clean up a transactional tenant test connection", {cause: cleanupError})
|
|
721
|
+
}
|
|
722
|
+
throw error
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Revokes attempt registrations before rolling back and releasing their connections.
|
|
728
|
+
* @param {TransactionalTenantRegistration[]} registrations - Attempt registrations.
|
|
729
|
+
* @param {{discard?: boolean}} [options] - Whether connections must be discarded instead of returned to the pool.
|
|
730
|
+
* @returns {Promise<void>}
|
|
731
|
+
*/
|
|
732
|
+
async cleanupTransactionalTenants(registrations, {discard = false} = {}) {
|
|
733
|
+
for (const registration of registrations) {
|
|
734
|
+
registration.revoked = true
|
|
735
|
+
if (discard) registration.discardOnCleanup = true
|
|
736
|
+
if (registration.sharedRegistration) registration.pool.clearTestSharedConnection(registration.sharedRegistration)
|
|
737
|
+
}
|
|
738
|
+
const cleanupResults = await Promise.allSettled([...registrations].reverse().map((registration) => {
|
|
739
|
+
registration.cleanupPromise ??= this.cleanupTransactionalTenantRegistration(registration)
|
|
740
|
+
|
|
741
|
+
return registration.cleanupPromise
|
|
742
|
+
}))
|
|
743
|
+
const errors = cleanupResults
|
|
744
|
+
.filter((result) => result.status === "rejected")
|
|
745
|
+
.map((result) => result.reason)
|
|
746
|
+
|
|
747
|
+
if (errors.length === 1) throw errors[0]
|
|
748
|
+
if (errors.length > 1) throw new AggregateError(errors, "Failed to clean up transactional tenant test connections")
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Cleans one attempt registration exactly once, including a checkout that was still pending at revocation.
|
|
753
|
+
* @param {TransactionalTenantRegistration} registration - Attempt-owned registration.
|
|
754
|
+
* @returns {Promise<void>} - Resolves after rollback and release or quarantine.
|
|
755
|
+
*/
|
|
756
|
+
async cleanupTransactionalTenantRegistration(registration) {
|
|
757
|
+
let connection = registration.connection
|
|
758
|
+
|
|
759
|
+
if (!connection && registration.checkoutPromise) {
|
|
760
|
+
const checkoutOutcome = await registration.checkoutPromise
|
|
761
|
+
|
|
762
|
+
if (checkoutOutcome.error) return
|
|
763
|
+
connection = checkoutOutcome.connection
|
|
764
|
+
registration.connection = connection
|
|
765
|
+
}
|
|
766
|
+
if (!connection) return
|
|
767
|
+
|
|
768
|
+
const errors = []
|
|
769
|
+
|
|
770
|
+
try {
|
|
771
|
+
if (connection.insideTransaction()) await connection.rollbackTransaction()
|
|
772
|
+
} catch (error) {
|
|
773
|
+
errors.push(error)
|
|
774
|
+
} finally {
|
|
775
|
+
try {
|
|
776
|
+
if (registration.discardOnCleanup) {
|
|
777
|
+
await registration.pool.discard(connection)
|
|
778
|
+
} else {
|
|
779
|
+
await registration.pool.checkin(connection)
|
|
780
|
+
}
|
|
781
|
+
} catch (error) {
|
|
782
|
+
errors.push(error)
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (errors.length === 1) throw errors[0]
|
|
786
|
+
if (errors.length > 1) throw new AggregateError(errors, "Failed to clean up a transactional tenant test connection")
|
|
787
|
+
}
|
|
788
|
+
|
|
644
789
|
/**
|
|
645
790
|
* Selects the current non-tenant connections eligible for shared transaction work.
|
|
646
791
|
* @param {{transactionsOnly: boolean}} args - Selection options.
|
|
@@ -1014,6 +1159,29 @@ export default class TestRunner {
|
|
|
1014
1159
|
console.error(error)
|
|
1015
1160
|
}
|
|
1016
1161
|
|
|
1162
|
+
/**
|
|
1163
|
+
* Records a rollback/discard failure that settled after timeout cleanup moved on.
|
|
1164
|
+
* @param {unknown} reason - Detached cleanup rejection.
|
|
1165
|
+
* @returns {void}
|
|
1166
|
+
*/
|
|
1167
|
+
recordTransactionalTenantCleanupFailure(reason) {
|
|
1168
|
+
const error = reason instanceof Error ? reason : new Error(`Transactional tenant cleanup failed: ${String(reason)}`)
|
|
1169
|
+
const near = this._lastTestContext
|
|
1170
|
+
const attribution = near ? `, near test: ${near.fullDescription} (${near.filePath}:${near.line})` : ""
|
|
1171
|
+
|
|
1172
|
+
this._failedTests = (this._failedTests || 0) + 1
|
|
1173
|
+
this._failedTestDetails.push({
|
|
1174
|
+
fullDescription: `<transactional tenant emergency cleanup failure${attribution}>`,
|
|
1175
|
+
filePath: near ? near.filePath : "<test runner>",
|
|
1176
|
+
line: near ? near.line : 0,
|
|
1177
|
+
error,
|
|
1178
|
+
consoleOutput: undefined
|
|
1179
|
+
})
|
|
1180
|
+
|
|
1181
|
+
console.error(picocolors.red(`\n[test-runner] transactional tenant emergency cleanup failed after the bounded timeout grace.${attribution}`))
|
|
1182
|
+
console.error(error)
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1017
1185
|
async run() {
|
|
1018
1186
|
/**
|
|
1019
1187
|
* Handles a process-level unhandled rejection during the run.
|
|
@@ -1232,6 +1400,11 @@ export default class TestRunner {
|
|
|
1232
1400
|
let sharedTransactionBrokerRegistration
|
|
1233
1401
|
/** @type {SharedTransactionBrokerRegistration | undefined} */
|
|
1234
1402
|
let sharedTransactionBrokerPreparation
|
|
1403
|
+
/** @type {TransactionalTenantRegistration[]} */
|
|
1404
|
+
const transactionalTenantRegistrations = []
|
|
1405
|
+
testArgs.registerTransactionalTenant = async (args) => {
|
|
1406
|
+
await this.registerTransactionalTenant(args, transactionalTenantRegistrations)
|
|
1407
|
+
}
|
|
1235
1408
|
const stopConsoleCapture = this.startConsoleCapture({
|
|
1236
1409
|
passthrough: testConfig.consoleOutput === "live"
|
|
1237
1410
|
})
|
|
@@ -1324,15 +1497,19 @@ export default class TestRunner {
|
|
|
1324
1497
|
sharedTransactionBrokerRegistration = undefined
|
|
1325
1498
|
sharedTransactionBrokerPreparation = undefined
|
|
1326
1499
|
} finally {
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1500
|
+
try {
|
|
1501
|
+
for (const afterEachData of newAfterEaches) {
|
|
1502
|
+
await this.runProfileSpan({
|
|
1503
|
+
phase: "afterEach",
|
|
1504
|
+
declarationIndex: afterEachData.declarationIndex,
|
|
1505
|
+
declarationScopeId: afterEachData.declarationScopeId,
|
|
1506
|
+
filePath: afterEachData.ownerFilePath
|
|
1507
|
+
}, async () => {
|
|
1508
|
+
await afterEachData.callback({configuration: this.getConfiguration(), testArgs, testData})
|
|
1509
|
+
})
|
|
1510
|
+
}
|
|
1511
|
+
} finally {
|
|
1512
|
+
await this.cleanupTransactionalTenants(transactionalTenantRegistrations)
|
|
1336
1513
|
}
|
|
1337
1514
|
}
|
|
1338
1515
|
}
|
|
@@ -1401,6 +1578,18 @@ export default class TestRunner {
|
|
|
1401
1578
|
await this.stopSharedTransactionBroker(sharedTransactionBrokerRegistration || sharedTransactionBrokerPreparation)
|
|
1402
1579
|
sharedTransactionBrokerRegistration = undefined
|
|
1403
1580
|
sharedTransactionBrokerPreparation = undefined
|
|
1581
|
+
const emergencyCleanup = this.cleanupTransactionalTenants(transactionalTenantRegistrations, {discard: true})
|
|
1582
|
+
const emergencyCleanupSettled = await awaitSettledOrGrace(emergencyCleanup, timeoutMs ?? 60000)
|
|
1583
|
+
|
|
1584
|
+
if (emergencyCleanupSettled) {
|
|
1585
|
+
await emergencyCleanup
|
|
1586
|
+
} else {
|
|
1587
|
+
// The timed-out attempt must not block the runner indefinitely, but a
|
|
1588
|
+
// later rollback/discard failure still becomes a visible test failure.
|
|
1589
|
+
void emergencyCleanup.catch((cleanupError) => {
|
|
1590
|
+
this.recordTransactionalTenantCleanupFailure(cleanupError)
|
|
1591
|
+
})
|
|
1592
|
+
}
|
|
1404
1593
|
}
|
|
1405
1594
|
|
|
1406
1595
|
willRetry = retriesUsed < retryCount
|