Fix flaky document preview blob URL reuse
This commit is contained in:
parent
495c197c4c
commit
d59e911ca2
1 changed files with 43 additions and 18 deletions
61
src/lib/client/cache/previews.ts
vendored
61
src/lib/client/cache/previews.ts
vendored
|
|
@ -8,6 +8,7 @@ import { documentPreviewFallbackUrl, documentPreviewPresignUrl } from '@/lib/cli
|
||||||
|
|
||||||
const inMemoryPreviewUrlCache = new Map<string, string>();
|
const inMemoryPreviewUrlCache = new Map<string, string>();
|
||||||
const inFlightPreviewPrime = new Map<string, Promise<string | null>>();
|
const inFlightPreviewPrime = new Map<string, Promise<string | null>>();
|
||||||
|
const inFlightPersistedPreviewUrl = new Map<string, Promise<string | null>>();
|
||||||
const PREVIEW_CACHE_SCHEMA_VERSION = 4;
|
const PREVIEW_CACHE_SCHEMA_VERSION = 4;
|
||||||
|
|
||||||
function revokeIfBlobUrl(url: string | null | undefined): void {
|
function revokeIfBlobUrl(url: string | null | undefined): void {
|
||||||
|
|
@ -37,6 +38,7 @@ export function clearInMemoryDocumentPreviewCache(): void {
|
||||||
revokeIfBlobUrl(value);
|
revokeIfBlobUrl(value);
|
||||||
}
|
}
|
||||||
inMemoryPreviewUrlCache.clear();
|
inMemoryPreviewUrlCache.clear();
|
||||||
|
inFlightPersistedPreviewUrl.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPersistedDocumentPreviewUrl(
|
export async function getPersistedDocumentPreviewUrl(
|
||||||
|
|
@ -44,29 +46,52 @@ export async function getPersistedDocumentPreviewUrl(
|
||||||
lastModified: number,
|
lastModified: number,
|
||||||
cacheKey: string,
|
cacheKey: string,
|
||||||
): Promise<string | null> {
|
): Promise<string | null> {
|
||||||
const row = await getDocumentPreviewCache(docId);
|
const cachedUrl = getInMemoryDocumentPreviewUrl(cacheKey);
|
||||||
if (!row) return null;
|
if (cachedUrl) return cachedUrl;
|
||||||
|
|
||||||
if (Number((row as { previewVersion?: number }).previewVersion ?? 1) !== PREVIEW_CACHE_SCHEMA_VERSION) {
|
const persistedKey = `${cacheKey}:${Number(lastModified)}`;
|
||||||
await removeDocumentPreviewCache(docId).catch(() => {});
|
const existing = inFlightPersistedPreviewUrl.get(persistedKey);
|
||||||
return null;
|
if (existing) {
|
||||||
|
return existing;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number(row.lastModified) !== Number(lastModified)) {
|
const promise = (async (): Promise<string | null> => {
|
||||||
await removeDocumentPreviewCache(docId).catch(() => {});
|
const row = await getDocumentPreviewCache(docId);
|
||||||
return null;
|
if (!row) return null;
|
||||||
}
|
|
||||||
|
|
||||||
const contentType = row.contentType || 'image/jpeg';
|
if (Number((row as { previewVersion?: number }).previewVersion ?? 1) !== PREVIEW_CACHE_SCHEMA_VERSION) {
|
||||||
const bytes = row.data;
|
await removeDocumentPreviewCache(docId).catch(() => {});
|
||||||
if (!(bytes instanceof ArrayBuffer) || bytes.byteLength === 0) {
|
return null;
|
||||||
await removeDocumentPreviewCache(docId).catch(() => {});
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = URL.createObjectURL(new Blob([bytes], { type: contentType }));
|
if (Number(row.lastModified) !== Number(lastModified)) {
|
||||||
setInMemoryDocumentPreviewUrl(cacheKey, url);
|
await removeDocumentPreviewCache(docId).catch(() => {});
|
||||||
return url;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const latestCachedUrl = getInMemoryDocumentPreviewUrl(cacheKey);
|
||||||
|
if (latestCachedUrl) return latestCachedUrl;
|
||||||
|
|
||||||
|
const contentType = row.contentType || 'image/jpeg';
|
||||||
|
const bytes = row.data;
|
||||||
|
if (!(bytes instanceof ArrayBuffer) || bytes.byteLength === 0) {
|
||||||
|
await removeDocumentPreviewCache(docId).catch(() => {});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = URL.createObjectURL(new Blob([bytes], { type: contentType }));
|
||||||
|
setInMemoryDocumentPreviewUrl(cacheKey, url);
|
||||||
|
return url;
|
||||||
|
})();
|
||||||
|
|
||||||
|
inFlightPersistedPreviewUrl.set(persistedKey, promise);
|
||||||
|
try {
|
||||||
|
return await promise;
|
||||||
|
} finally {
|
||||||
|
if (inFlightPersistedPreviewUrl.get(persistedKey) === promise) {
|
||||||
|
inFlightPersistedPreviewUrl.delete(persistedKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function primeDocumentPreviewCache(
|
export async function primeDocumentPreviewCache(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue