Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/app/api/documents/[id]/reindex/route.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -243,12 +243,21 @@ export async function POST(request: Request, { params }: { params: Promise<{ id:
throw new Error(`Failed to enqueue reindex job: ${jobError.message}; competing-job check failed: ${competingJobsError.message}`);
}
if ((competingJobs?.length ?? 0) === 0) {
const { error: rollbackError } = await supabase
let rollbackQuery = supabase
.from("documents")
.update(rollbackDocumentPayload)
.eq("id", id)
.eq("owner_id", user.id)
.eq("updated_at", rollbackFence);
if (!atomicReindex) {
rollbackQuery = rollbackQuery
.eq("status", "queued")
.is("error_message", null)
.eq("page_count", 0)
.eq("chunk_count", 0)
.eq("image_count", 0);
}
const { error: rollbackError } = await rollbackQuery;
if (rollbackError) {
throw new Error(`Failed to enqueue reindex job: ${jobError.message}; rollback failed: ${rollbackError.message}`);
}
Expand Down
11 changes: 10 additions & 1 deletion src/app/api/documents/bulk/reindex/route.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -237,12 +237,21 @@ export async function POST(request: Request) {
);
}
if ((competingJobs?.length ?? 0) === 0) {
const { error: rollbackError } = await supabase
let rollbackQuery = supabase
.from("documents")
.update(rollbackDocumentPayload)
.eq("id", document.id)
.eq("owner_id", user.id)
.eq("updated_at", rollbackFence);
if (!atomicReindex) {
rollbackQuery = rollbackQuery
.eq("status", "queued")
.is("error_message", null)
.eq("page_count", 0)
.eq("chunk_count", 0)
.eq("image_count", 0);
}
const { error: rollbackError } = await rollbackQuery;
if (rollbackError) {
throw new Error(`Failed to enqueue bulk reindex job: ${jobError.message}; rollback failed: ${rollbackError.message}`);
}
Expand Down
10 changes: 10 additions & 0 deletions tests/private-access-routes.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1644,6 +1644,11 @@ describe("private document API access", () => {
// cannot revert a newer queue state written by an overlapping request.
const fence = (documentUpdates[0]?.updatePayload as { updated_at?: string }).updated_at;
expect(documentUpdates[1]?.filters).toContainEqual({ column: "updated_at", value: fence });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "status", value: "queued" });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "error_message", value: null });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "page_count", value: 0 });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "chunk_count", value: 0 });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "image_count", value: 0 });
});

it("skips single-document rollback when a competing active job appears after the safety check", async () => {
Expand DownExpand Up@@ -1765,6 +1770,11 @@ describe("private document API access", () => {
// reindex route — the rollback matches on the stamp this request wrote.
const fence = (documentUpdates[0]?.updatePayload as { updated_at?: string }).updated_at;
expect(documentUpdates[1]?.filters).toContainEqual({ column: "updated_at", value: fence });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "status", value: "queued" });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "error_message", value: null });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "page_count", value: 0 });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "chunk_count", value: 0 });
expect(documentUpdates[1]?.filters).toContainEqual({ column: "image_count", value: 0 });
});

it("skips bulk rollback when a competing active job appears after the safety check", async () => {
Expand Down