fix(imaging): stop caching fallback renders
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 29s
CI / tests-integration (push) Successful in 1m40s
CI / tests-unit (push) Successful in 1m53s
CI / tests-ui (push) Successful in 2m35s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m59s
Gitea Actions Runner Test / test-job (push) Successful in 1s
CI / check (push) Successful in 29s
CI / tests-integration (push) Successful in 1m40s
CI / tests-unit (push) Successful in 1m53s
CI / tests-ui (push) Successful in 2m35s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m59s
A fallback render drops the requested effect and is only a degraded stand-in, so writing it to the 30 day disk cache kept serving the worse image long after the local renderer recovered. Cache primary renders only and let the next request pick up the real render.
This commit is contained in:
1 parent
fe5a7a6185
commit
f490fcc9da
2 files changed
+74
-7
No files matched your search
@@ -133,13 +133,11 @@ export async function fetchAvatarImage(
|
||||
buildFallbackParams(params),
|
||||
FALLBACK_TIMEOUT_MS,
|
||||
);
|
||||
// Deliberately not cached: a fallback render drops the effect and is only a
|
||||
// degraded stand-in, so persisting it would keep serving the worse image
|
||||
// for the whole cache lifetime. The local renderer caches its own renders,
|
||||
// so the next request for this figure is cheap and returns the real one.
|
||||
if (fallbackResult) {
|
||||
await writeImagingCache(
|
||||
avatarCacheDir(),
|
||||
cacheKey,
|
||||
fallbackResult.body,
|
||||
fallbackResult.contentType,
|
||||
);
|
||||
return { ...fallbackResult, source: "fallback" };
|
||||
}
|
||||
|
||||
|
||||
+70
-1
@@ -1,7 +1,14 @@
|
||||
// @ts-nocheck
|
||||
import { mkdtempSync, readdirSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { getAvatarUrl } from "./imager";
|
||||
import { FALLBACK_TIMEOUT_MS, PRIMARY_TIMEOUT_MS } from "./imager-upstream";
|
||||
import {
|
||||
FALLBACK_TIMEOUT_MS,
|
||||
fetchAvatarImage,
|
||||
PRIMARY_TIMEOUT_MS,
|
||||
} from "./imager-upstream";
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
@@ -73,3 +80,65 @@ describe("upstream render budgets", () => {
|
||||
expect(FALLBACK_TIMEOUT_MS).toBeLessThan(PRIMARY_TIMEOUT_MS);
|
||||
});
|
||||
});
|
||||
|
||||
describe("fetchAvatarImage caching", () => {
|
||||
let cacheRoot = "";
|
||||
|
||||
beforeEach(() => {
|
||||
cacheRoot = mkdtempSync(join(tmpdir(), "imager-cache-"));
|
||||
vi.stubEnv("IMAGING_CACHE_ROOT", cacheRoot);
|
||||
vi.stubEnv("IMAGER_URL", "http://primary.invalid/avatarimage");
|
||||
vi.stubEnv("IMAGING_UPSTREAM_URL", "http://primary.invalid/avatarimage");
|
||||
vi.stubEnv("APP_URL", "https://cms.example");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
rmSync(cacheRoot, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
const params = () =>
|
||||
new URLSearchParams({
|
||||
figure: "hd-180-1",
|
||||
direction: "2",
|
||||
head_direction: "3",
|
||||
size: "m",
|
||||
img_format: "png",
|
||||
});
|
||||
|
||||
it("persists renders from the primary renderer", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(
|
||||
async () =>
|
||||
new Response(new Uint8Array([1, 2, 3]), {
|
||||
headers: { "content-type": "image/png" },
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const first = await fetchAvatarImage("https://cms.example", params());
|
||||
expect(first.source).toBe("primary");
|
||||
expect(readdirSync(join(cacheRoot, "avatars")).length).toBe(2);
|
||||
|
||||
const second = await fetchAvatarImage("https://cms.example", params());
|
||||
expect(second.source).toBe("cache");
|
||||
});
|
||||
|
||||
it("never persists a fallback render", async () => {
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async (url: string | URL) =>
|
||||
String(url).includes("primary.invalid")
|
||||
? new Response(null, { status: 500 })
|
||||
: new Response(new Uint8Array([4, 5, 6]), {
|
||||
headers: { "content-type": "image/png" },
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const result = await fetchAvatarImage("https://cms.example", params());
|
||||
expect(result.source).toBe("fallback");
|
||||
expect(() => readdirSync(join(cacheRoot, "avatars"))).toThrow();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user