fix(housekeeping): lock poll questions by primary key
This commit is contained in:
1 parent
6e16b948e6
commit
e9680775f9
2 files changed
+70
-23
No files matched your search
+61
-21
@@ -15,9 +15,15 @@ const sqlMocks = vi.hoisted(() => ({
|
||||
}));
|
||||
|
||||
const database = vi.hoisted(() => {
|
||||
type SelectedResult =
|
||||
| Record<string, unknown>
|
||||
| readonly Record<string, unknown>[];
|
||||
let selected: Record<string, unknown> = { id: 7, title: "Existing" };
|
||||
let selectedQueue: Record<string, unknown>[] = [];
|
||||
let selectedQueue: SelectedResult[] = [];
|
||||
let selectedTable: string | undefined;
|
||||
let selectedCondition:
|
||||
| { operator: string; column: unknown; value: unknown }
|
||||
| undefined;
|
||||
let lockedTables: Array<readonly [string | undefined, string]> = [];
|
||||
let mutationSteps: string[] = [];
|
||||
const insertedValues = vi.fn(async (_values: Record<string, unknown>) => [
|
||||
@@ -29,16 +35,33 @@ const database = vi.hoisted(() => {
|
||||
values,
|
||||
}));
|
||||
const update = vi.fn(() => ({ set }));
|
||||
const nextSelected = () => [selectedQueue.shift() ?? selected];
|
||||
const nextSelected = () => {
|
||||
const result = selectedQueue.shift() ?? selected;
|
||||
return Array.isArray(result) ? [...result] : [result];
|
||||
};
|
||||
const limit = vi.fn(async () => nextSelected());
|
||||
const lock = vi.fn(async (strength: string) => {
|
||||
lockedTables.push([selectedTable, strength]);
|
||||
mutationSteps.push(`lock:${selectedTable ?? "unknown"}`);
|
||||
mutationSteps.push(
|
||||
`lock:${selectedTable ?? "unknown"}:${selectedCondition?.operator ?? "none"}:${String(selectedCondition?.column ?? "none")}:${String(selectedCondition?.value ?? "none")}`,
|
||||
);
|
||||
return nextSelected();
|
||||
});
|
||||
const where = vi.fn(() => ({ for: lock, limit }));
|
||||
const orderBy = vi.fn(async (column: unknown) => {
|
||||
mutationSteps.push(
|
||||
`read:${selectedTable ?? "unknown"}:${selectedCondition?.operator ?? "none"}:${String(selectedCondition?.column ?? "none")}:orderBy:${String(column)}`,
|
||||
);
|
||||
return nextSelected();
|
||||
});
|
||||
const where = vi.fn(
|
||||
(condition: { operator: string; column: unknown; value: unknown }) => {
|
||||
selectedCondition = condition;
|
||||
return { for: lock, limit, orderBy };
|
||||
},
|
||||
);
|
||||
const from = vi.fn((table: unknown) => {
|
||||
selectedTable = (table as { tableName?: string }).tableName;
|
||||
selectedCondition = undefined;
|
||||
return { where };
|
||||
});
|
||||
const select = vi.fn(() => ({ from }));
|
||||
@@ -75,10 +98,11 @@ const database = vi.hoisted(() => {
|
||||
selected = value;
|
||||
selectedQueue = [];
|
||||
selectedTable = undefined;
|
||||
selectedCondition = undefined;
|
||||
lockedTables = [];
|
||||
mutationSteps = [];
|
||||
},
|
||||
queueSelected(...values: Record<string, unknown>[]) {
|
||||
queueSelected(...values: SelectedResult[]) {
|
||||
selectedQueue = [...values];
|
||||
},
|
||||
};
|
||||
@@ -86,8 +110,16 @@ const database = vi.hoisted(() => {
|
||||
|
||||
vi.mock("drizzle-orm", () => ({
|
||||
count: vi.fn(() => "count"),
|
||||
eq: vi.fn(),
|
||||
inArray: vi.fn(),
|
||||
eq: vi.fn((column: unknown, value: unknown) => ({
|
||||
operator: "eq",
|
||||
column,
|
||||
value,
|
||||
})),
|
||||
inArray: vi.fn((column: unknown, value: unknown) => ({
|
||||
operator: "inArray",
|
||||
column,
|
||||
value,
|
||||
})),
|
||||
sql: Object.assign(sqlMocks.tagged, {
|
||||
join: sqlMocks.join,
|
||||
raw: sqlMocks.raw,
|
||||
@@ -173,17 +205,22 @@ describe("Content database mutation runtime partial updates", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("deletes poll votes, questions, and the poll in child-first order", async () => {
|
||||
database.selected({
|
||||
id: 7,
|
||||
title: "Existing",
|
||||
description: null,
|
||||
status: "draft",
|
||||
showResults: 1,
|
||||
multipleChoice: 0,
|
||||
startsAt: null,
|
||||
endsAt: null,
|
||||
});
|
||||
it("locks poll questions by ascending primary key before child-first deletion", async () => {
|
||||
database.queueSelected(
|
||||
{
|
||||
id: 7,
|
||||
title: "Existing",
|
||||
description: null,
|
||||
status: "draft",
|
||||
showResults: 1,
|
||||
multipleChoice: 0,
|
||||
startsAt: null,
|
||||
endsAt: null,
|
||||
},
|
||||
[{ id: 4 }, { id: 9 }],
|
||||
{ id: 4 },
|
||||
{ id: 9 },
|
||||
);
|
||||
|
||||
await executeContentDatabaseMutation(
|
||||
"poll.change",
|
||||
@@ -200,10 +237,13 @@ describe("Content database mutation runtime partial updates", () => {
|
||||
expect(database.lockedTables()).toEqual([
|
||||
["WebsitePoll", "update"],
|
||||
["WebsitePollQuestion", "update"],
|
||||
["WebsitePollQuestion", "update"],
|
||||
]);
|
||||
expect(database.mutationSteps()).toEqual([
|
||||
"lock:WebsitePoll",
|
||||
"lock:WebsitePollQuestion",
|
||||
"lock:WebsitePoll:eq:id:7",
|
||||
"read:WebsitePollQuestion:eq:pollId:orderBy:id",
|
||||
"lock:WebsitePollQuestion:eq:id:4",
|
||||
"lock:WebsitePollQuestion:eq:id:9",
|
||||
"delete:WebsitePollVote",
|
||||
"delete:WebsitePollQuestion",
|
||||
"delete:WebsitePoll",
|
||||
@@ -241,7 +281,7 @@ describe("Content database mutation runtime partial updates", () => {
|
||||
["WebsitePollQuestion", "update"],
|
||||
]);
|
||||
expect(database.mutationSteps()).toEqual([
|
||||
"lock:WebsitePollQuestion",
|
||||
"lock:WebsitePollQuestion:eq:id:7",
|
||||
"delete:WebsitePollVote",
|
||||
"delete:WebsitePollQuestion",
|
||||
]);
|
||||
|
||||
@@ -635,11 +635,18 @@ async function pollChange(
|
||||
.for("update");
|
||||
if (!existing) throw notFound();
|
||||
if (action === "delete") {
|
||||
await connection
|
||||
const questionIds = await connection
|
||||
.select({ id: WebsitePollQuestion.id })
|
||||
.from(WebsitePollQuestion)
|
||||
.where(eq(WebsitePollQuestion.pollId, id))
|
||||
.for("update");
|
||||
.orderBy(WebsitePollQuestion.id);
|
||||
for (const question of questionIds) {
|
||||
await connection
|
||||
.select({ id: WebsitePollQuestion.id })
|
||||
.from(WebsitePollQuestion)
|
||||
.where(eq(WebsitePollQuestion.id, question.id))
|
||||
.for("update");
|
||||
}
|
||||
await connection
|
||||
.delete(WebsitePollVote)
|
||||
.where(
|
||||
|
||||
Reference in new issue
Block a user