perf(canvas): reuse durable prepared PNGs in batch and fallback downloads

This commit is contained in:
134 2026-10-12 15:34:11 +08:00
parent 61455df9d2
commit dbdaab74e5
3 changed files with 17 additions and 3 deletions

View File

@ -297,6 +297,20 @@ export async function fetchCanvasDownloadAsset(params: {
params.requestOrigin
);
if (process.env.POSTGRES_URL) {
// ZIPs and binary fallback also reuse the durable prepared object after the
// local byte cache expires, instead of converting the original JPEG again.
const { findImageArchiveDownloadTask } = await import("@/lib/storage/image-archive-db");
const task = await findImageArchiveDownloadTask(candidateUrls).catch(() => null);
const asset = (task?.metadata as { downloadAsset?: { status?: string; url?: string; contentType?: string } } | null)?.downloadAsset;
if (asset?.status === "ready" && asset.url && asset.contentType === "image/png") {
const readyUrl = resolveHttpUrl(asset.url, params.requestOrigin);
if (readyUrl && !candidateUrls.includes(readyUrl)) candidateUrls.unshift(readyUrl);
} else if (task?.targetUrl && !candidateUrls.includes(task.targetUrl)) {
candidateUrls.unshift(task.targetUrl);
}
}
for (const candidateUrl of candidateUrls) {
for (let attempt = 1; attempt <= retryCount; attempt += 1) {
try {

View File

@ -8,7 +8,7 @@ export async function resolveCanvasCdnDownload(urls: string[], filename: string)
const source = extractImageProxySourceUrl(url) || url;
return [source, inferOriginalImageUrl(source) || source];
})));
const task = await findImageArchiveDownloadTask(sources);
const task = await findImageArchiveDownloadTask(sources, true);
const asset = (task?.metadata as { downloadAsset?: { status?: string; url?: string; contentType?: string; bytes?: number } } | null)?.downloadAsset;
if (asset?.status !== "ready" || !asset.url || asset.contentType !== "image/png") return { task, direct: null };
const config = await getDefaultStorageConfig();

View File

@ -317,11 +317,11 @@ export async function resolveArchivedImageUrls<T>(value: T): Promise<T> {
// Download assets use an independent JSONB lease; original archiving/replacement
// can finish even if PNG preparation or thumbnail storage is temporarily unavailable.
export async function findImageArchiveDownloadTask(urls: string[]) {
export async function findImageArchiveDownloadTask(urls: string[], prepareOnDemand = false) {
const [task] = await db.select().from(imageArchiveTask).where(or(
inArray(imageArchiveTask.sourceUrl, urls), inArray(imageArchiveTask.targetUrl, urls),
)).limit(1);
if (task?.targetUrl && !(task.metadata as { downloadAsset?: unknown } | null)?.downloadAsset) {
if (prepareOnDemand && task?.targetUrl && !(task.metadata as { downloadAsset?: unknown } | null)?.downloadAsset) {
await db.update(imageArchiveTask).set({
metadata: sql`coalesce(${imageArchiveTask.metadata}, '{}'::jsonb) || jsonb_build_object('downloadAsset', jsonb_build_object('status', 'pending'))`,
}).where(and(eq(imageArchiveTask.id, task.id), sql`${imageArchiveTask.metadata}->'downloadAsset' is null`));