test(db): release gated transactions in finally so a red assertion holds no lock
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
08946e6d08
commit
8efe99aa20
1 changed files with 37 additions and 25 deletions
|
|
@ -130,11 +130,15 @@ describe("db serialization (database-locked fix)", () => {
|
|||
await tx.execute("COMMIT");
|
||||
await gate;
|
||||
});
|
||||
await vi.waitFor(() => expect(events).toContain("end:COMMIT"));
|
||||
expect(events).not.toContain("start:NESTED");
|
||||
release();
|
||||
await txn;
|
||||
await nested;
|
||||
try {
|
||||
await vi.waitFor(() => expect(events).toContain("end:COMMIT"));
|
||||
expect(events).not.toContain("start:NESTED");
|
||||
} finally {
|
||||
// Released even on failure, so a red assertion leaves no held lock.
|
||||
release();
|
||||
await txn;
|
||||
await nested;
|
||||
}
|
||||
expect(events).toEqual(["start:BEGIN", "end:BEGIN", "start:COMMIT", "end:COMMIT", "start:NESTED", "end:NESTED"]);
|
||||
});
|
||||
|
||||
|
|
@ -358,20 +362,23 @@ describe("blocking alert (#408)", () => {
|
|||
nested = (await getDb()).execute("NESTED");
|
||||
await gate;
|
||||
});
|
||||
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS - 1);
|
||||
expect(logWarn).not.toHaveBeenCalled();
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||
const message = String(vi.mocked(logWarn).mock.calls[0][0]);
|
||||
expect(message).toContain("a query has waited 15 s");
|
||||
expect(message).toContain(`held by a transaction since ${startedAt}`);
|
||||
// Once per operation: still one warning much later.
|
||||
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS * 3);
|
||||
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||
release();
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
await txn;
|
||||
await nested;
|
||||
try {
|
||||
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS - 1);
|
||||
expect(logWarn).not.toHaveBeenCalled();
|
||||
await vi.advanceTimersByTimeAsync(1);
|
||||
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||
const message = String(vi.mocked(logWarn).mock.calls[0][0]);
|
||||
expect(message).toContain("a query has waited 15 s");
|
||||
expect(message).toContain(`held by a transaction since ${startedAt}`);
|
||||
// Once per operation: still one warning much later.
|
||||
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS * 3);
|
||||
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||
} finally {
|
||||
release();
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
await txn;
|
||||
await nested;
|
||||
}
|
||||
});
|
||||
|
||||
it("says nothing for an operation that got the lock in time", async () => {
|
||||
|
|
@ -393,12 +400,17 @@ describe("blocking alert (#408)", () => {
|
|||
const switching = connectToProfile("next.db");
|
||||
// Settled outcome caught at once: the rejection lands while timers advance.
|
||||
const waiting = withTransaction(async () => "late").catch((e: unknown) => e);
|
||||
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS);
|
||||
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||
expect(String(vi.mocked(logWarn).mock.calls[0][0])).toContain("a transaction has waited 15 s, the lock is held by a profile opening");
|
||||
finish();
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
await switching;
|
||||
try {
|
||||
await vi.advanceTimersByTimeAsync(BLOCKED_ALERT_MS);
|
||||
expect(logWarn).toHaveBeenCalledTimes(1);
|
||||
expect(String(vi.mocked(logWarn).mock.calls[0][0])).toContain(
|
||||
"a transaction has waited 15 s, the lock is held by a profile opening"
|
||||
);
|
||||
} finally {
|
||||
finish();
|
||||
await vi.advanceTimersByTimeAsync(10);
|
||||
await switching;
|
||||
}
|
||||
expect(await waiting).toBeInstanceOf(DbConnectionClosedError);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue