Avoid caching failed scan snapshots

This commit is contained in:
2569718930@qq.com
2026-06-09 22:47:23 +08:00
parent bd88f717fb
commit 3a1b2dfd6d
5 changed files with 54 additions and 9 deletions
+1 -1
View File
@@ -112,7 +112,7 @@ services:
POLYWEATHER_OBSERVATION_COLLECTOR_INITIAL_DELAY_SEC: ${POLYWEATHER_OBSERVATION_COLLECTOR_INITIAL_DELAY_SEC:-5}
POLYWEATHER_OBSERVATION_COLLECTOR_MADIS_SEC: ${POLYWEATHER_OBSERVATION_COLLECTOR_MADIS_SEC:-300}
POLYWEATHER_OBSERVATION_COLLECTOR_TICK_SEC: ${POLYWEATHER_OBSERVATION_COLLECTOR_TICK_SEC:-30}
POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED: ${POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED:-true}
POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED: 'true'
POLYWEATHER_SERVICE_ROLE: web
UVICORN_WORKERS: ${UVICORN_WORKERS:-2}
healthcheck:
+9 -1
View File
@@ -1,6 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { proxyBackendJsonGet } from "@/lib/api-proxy";
import { buildForceRefreshProxyCachePolicy } from "@/lib/proxy-cache-policy";
import {
buildForceRefreshProxyCachePolicy,
buildScanTerminalResponseCacheControl,
} from "@/lib/proxy-cache-policy";
import { DASHBOARD_REFRESH_POLICY_SEC } from "@/lib/refresh-policy";
import {
createProxyTimer,
@@ -64,6 +67,11 @@ export async function GET(req: NextRequest) {
try {
return await proxyBackendJsonGet(req, {
cacheControl: cachePolicy.responseCacheControl,
cacheControlForData: (data) =>
buildScanTerminalResponseCacheControl(
data,
cachePolicy.responseCacheControl,
),
fetchCache:
cachePolicy.fetchMode === "no-store" ? "no-store" : undefined,
publicMessage: "Failed to fetch scan terminal data",
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
import fs from "node:fs";
import path from "node:path";
import {
buildScanTerminalResponseCacheControl,
buildCityDetailProxyCachePolicy,
buildForceRefreshProxyCachePolicy,
isForceRefreshValue,
@@ -26,6 +27,24 @@ export function runTests() {
const scanForced = buildForceRefreshProxyCachePolicy("true", 10);
assert.equal(scanForced.fetchMode, "no-store");
const normalScanCache = "public, max-age=0, s-maxage=300, stale-while-revalidate=900";
assert.equal(
buildScanTerminalResponseCacheControl({ status: "ready", stale: false }, normalScanCache),
normalScanCache,
);
assert.equal(
buildScanTerminalResponseCacheControl({ status: "failed", stale: false }, normalScanCache),
"no-store, max-age=0",
);
assert.equal(
buildScanTerminalResponseCacheControl({ status: "partial", stale: false }, normalScanCache),
"no-store, max-age=0",
);
assert.equal(
buildScanTerminalResponseCacheControl({ status: "ready", stale: true }, normalScanCache),
"no-store, max-age=0",
);
const scanTerminalProxySource = fs.readFileSync(
path.join(process.cwd(), "app", "api", "scan", "terminal", "route.ts"),
"utf8",
@@ -40,6 +59,11 @@ export function runTests() {
/buildForceRefreshProxyCachePolicy\(forceRefresh,\s*10\)/,
"scan terminal proxy must not use the old 10 second edge cache because it over-drives the slow scan endpoint",
);
assert.match(
scanTerminalProxySource,
/cacheControlForData:\s*\(data\)\s*=>\s*buildScanTerminalResponseCacheControl/,
"scan terminal proxy must not CDN-cache failed, stale, or partial business payloads",
);
const scanTerminalClientSource = fs.readFileSync(
path.join(
+19 -2
View File
@@ -4,6 +4,8 @@ export type ProxyCachePolicy = {
revalidateSeconds?: number;
};
const NO_STORE_CACHE_CONTROL = "no-store, max-age=0";
export function isForceRefreshValue(value: string | null | undefined) {
return String(value || "").trim().toLowerCase() === "true";
}
@@ -15,7 +17,7 @@ export function buildForceRefreshProxyCachePolicy(
if (isForceRefreshValue(forceRefresh)) {
return {
fetchMode: "no-store",
responseCacheControl: "no-store, max-age=0",
responseCacheControl: NO_STORE_CACHE_CONTROL,
};
}
return {
@@ -35,7 +37,7 @@ export function buildCityDetailProxyCachePolicy(
if (isForceRefreshValue(forceRefresh)) {
return {
fetchMode: "no-store",
responseCacheControl: "no-store, max-age=0",
responseCacheControl: NO_STORE_CACHE_CONTROL,
};
}
return {
@@ -47,3 +49,18 @@ export function buildCityDetailProxyCachePolicy(
revalidateSeconds,
};
}
export function buildScanTerminalResponseCacheControl(
data: unknown,
readyCacheControl: string,
) {
if (!data || typeof data !== "object") {
return NO_STORE_CACHE_CONTROL;
}
const payload = data as { stale?: unknown; status?: unknown };
const status = String(payload.status || "").trim().toLowerCase();
if (payload.stale === true || (status && status !== "ready")) {
return NO_STORE_CACHE_CONTROL;
}
return readyCacheControl;
}
+1 -5
View File
@@ -91,11 +91,7 @@ def test_docker_compose_isolates_collector_from_web_and_bot_services():
assert "POLYWEATHER_SERVICE_ROLE: bot" in compose
assert "POLYWEATHER_SERVICE_ROLE: collector" in collector_block
assert "POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED: 'false'" in bot_block
assert (
"POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED: "
"${POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED:-true}"
in web_block
)
assert "POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED: 'true'" in web_block
assert "POLYWEATHER_SCAN_TERMINAL_PREWARM_ENABLED: 'false'" in collector_block
assert "POLYWEATHER_OBSERVATION_COLLECTOR_ENABLED: 'false'" in bot_block
assert "POLYWEATHER_OBSERVATION_COLLECTOR_ENABLED: 'false'" in web_block