mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 14:32:03 +00:00
fix(migrations): retry a marked migration that lost a deadlock
A marked migration's DO block holds the locks it takes until it commits, so an app transaction taking two of its tables in the other order can deadlock with it. The block then rolled back whole, exactly as on a lock timeout, so the boot retries 40P01 for marked migrations as it does 55P03. Corrects 0415's note that the drops hold their lock for an instant.
This commit is contained in:
1 parent
2b81b4f23e
commit
408ae35b7c
4 files changed
+108
-22
No files matched your search
@@ -47,6 +47,11 @@ const P3018_UNIQUE = P3018_LOCK.replace("55P03", "23505").replace(
|
||||
'duplicate key value violates unique constraint "x"',
|
||||
);
|
||||
|
||||
const P3018_DEADLOCK = P3018_LOCK.replace("55P03", "40P01").replace(
|
||||
"canceling statement due to lock timeout",
|
||||
"deadlock detected",
|
||||
);
|
||||
|
||||
const P3009 = `Error: P3009
|
||||
|
||||
migrate found failed migrations in the target database, new migrations will not be applied. Read more about how to resolve migration issues in a production database: https://pris.ly/d/migrate-resolve
|
||||
@@ -78,7 +83,11 @@ describe("which failure is retried", () => {
|
||||
failedRows: null,
|
||||
readMigration: reader(MARKED),
|
||||
}),
|
||||
).toEqual({ retry: true, migration: "0412_measurement_hot_updates" });
|
||||
).toEqual({
|
||||
retry: true,
|
||||
migration: "0412_measurement_hot_updates",
|
||||
code: "55P03",
|
||||
});
|
||||
});
|
||||
|
||||
it("does not retry a lock timeout of an unmarked migration", () => {
|
||||
@@ -93,6 +102,31 @@ describe("which failure is retried", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("retries a deadlock of a marked migration, which rolled back whole", () => {
|
||||
expect(lockTimeoutInReport(P3018_DEADLOCK)).toBe(
|
||||
"0412_measurement_hot_updates",
|
||||
);
|
||||
expect(
|
||||
decideLockRetry({
|
||||
output: P3018_DEADLOCK,
|
||||
failedRows: null,
|
||||
readMigration: reader(MARKED),
|
||||
}),
|
||||
).toEqual({
|
||||
retry: true,
|
||||
migration: "0412_measurement_hot_updates",
|
||||
code: "40P01",
|
||||
});
|
||||
// Unmarked, a deadlock stays fatal like a lock timeout.
|
||||
expect(
|
||||
decideLockRetry({
|
||||
output: P3018_DEADLOCK,
|
||||
failedRows: null,
|
||||
readMigration: reader(UNMARKED),
|
||||
}).retry,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("does not retry any other database error", () => {
|
||||
expect(
|
||||
decideLockRetry({
|
||||
@@ -123,7 +157,11 @@ describe("which failure is retried", () => {
|
||||
failedRows,
|
||||
readMigration: reader(MARKED),
|
||||
}),
|
||||
).toEqual({ retry: true, migration: "0412_measurement_hot_updates" });
|
||||
).toEqual({
|
||||
retry: true,
|
||||
migration: "0412_measurement_hot_updates",
|
||||
code: "55P03",
|
||||
});
|
||||
// A stored failure of another kind stays fatal.
|
||||
expect(
|
||||
decideLockRetry({
|
||||
@@ -295,6 +333,24 @@ process.exit(0);
|
||||
expect(result.stdout).toContain("timed out waiting for a lock (55P03)");
|
||||
});
|
||||
|
||||
it("does the same for a deadlock, and names it", () => {
|
||||
const { run, calls } = setup(
|
||||
[
|
||||
[1, P3018_DEADLOCK],
|
||||
[0, "All migrations have been successfully applied."],
|
||||
],
|
||||
MARKED,
|
||||
);
|
||||
const result = run();
|
||||
expect(result.status).toBe(0);
|
||||
expect(calls()).toEqual([
|
||||
"migrate deploy --schema x",
|
||||
"migrate resolve --rolled-back 0412_measurement_hot_updates --schema x",
|
||||
"migrate deploy --schema x",
|
||||
]);
|
||||
expect(result.stdout).toContain("was cancelled in a deadlock (40P01)");
|
||||
});
|
||||
|
||||
it("gives up after the last delay with Prisma's exit code", () => {
|
||||
const { run, calls } = setup(
|
||||
[
|
||||
|
||||
Reference in new issue
Block a user