From 3d12c508d37a1ba724dfc68d8cf89e8b3338f3cd Mon Sep 17 00:00:00 2001 From: 134 Date: Thu, 20 Aug 2026 19:34:56 +0800 Subject: [PATCH] fix: stop retrying expired image archives forever --- lib/storage/image-archive-db.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/storage/image-archive-db.ts b/lib/storage/image-archive-db.ts index 06f56fc44..bfef74a20 100644 --- a/lib/storage/image-archive-db.ts +++ b/lib/storage/image-archive-db.ts @@ -1,12 +1,13 @@ import "server-only"; -import { and, asc, eq, inArray, lte, sql } from "drizzle-orm"; +import { and, asc, eq, inArray, lt, lte, sql } from "drizzle-orm"; import { db } from "@/lib/db"; import { imageArchiveTask, type ImageArchiveTask } from "@/lib/db/schema"; export type ImageArchiveTaskRecord = ImageArchiveTask; const DEFAULT_DISPLAY_HOLD_HOURS = 1; +const MAX_IMAGE_ARCHIVE_ATTEMPTS = 12; function getDisplayHoldHours() { const raw = Number( @@ -95,7 +96,8 @@ export async function getImageArchiveTasksToStore(params: { limit: number }) { .where( and( inArray(imageArchiveTask.status, ["pending", "failed"]), - lte(imageArchiveTask.archiveAfterAt, now) + lte(imageArchiveTask.archiveAfterAt, now), + lt(imageArchiveTask.attemptCount, MAX_IMAGE_ARCHIVE_ATTEMPTS) ) ) .orderBy(asc(imageArchiveTask.createdAt), asc(imageArchiveTask.id)) @@ -139,11 +141,18 @@ export async function markImageArchiveTaskFailed(params: { const message = String( params.error instanceof Error ? params.error.message : params.error || "Unknown error" ).slice(0, 2000); + const isPermanentSourceError = /\bHTTP (?:404|410)\b/i.test(message); await db .update(imageArchiveTask) .set({ - status: "failed", + status: isPermanentSourceError + ? "abandoned" + : sql`case + when ${imageArchiveTask.attemptCount} >= ${MAX_IMAGE_ARCHIVE_ATTEMPTS} + then 'abandoned' + else 'failed' + end`, lastError: message, archiveAfterAt: new Date(Date.now() + 10 * 60 * 1000), updatedAt: new Date(),