Reduce terminal detail refresh traffic

This commit is contained in:
2569718930@qq.com
2026-05-31 21:01:11 +08:00
parent 8d552a9279
commit 071d749ef0
4 changed files with 44 additions and 7 deletions
@@ -20,6 +20,7 @@ export function runTests() {
const cached = buildCityDetailProxyCachePolicy("false", 15);
assert.equal(cached.fetchMode, "revalidate");
assert.equal(cached.revalidateSeconds, 15);
assert.match(cached.responseCacheControl, /max-age=15/);
assert.match(cached.responseCacheControl, /s-maxage=15/);
const scanForced = buildForceRefreshProxyCachePolicy("true", 10);
@@ -76,6 +76,12 @@ export async function runTests() {
chartSource.includes("setHourly(data)"),
"visible chart fallback must refresh the full city detail payload at the current chart resolution when SSE patches stop",
);
assert(
chartLogicSource.includes("HOURLY_FORCE_REFRESH_DEDUP_MS") &&
chartLogicSource.includes("maxAgeMs: HOURLY_FORCE_REFRESH_DEDUP_MS") &&
chartLogicSource.includes("recentlyRefreshed.data"),
"forced chart detail refreshes should reuse a very recent full-detail payload instead of refetching repeatedly",
);
assert(
__shouldPollLiveChartForTest({ city: "shanghai", compact: true, isActive: false, isMaximized: false }) === true,
"compact grid slots are visible charts and should run the no-patch fallback guard",
@@ -353,6 +353,7 @@ type LocalDayBounds = { start: number; end: number };
const MAX_OBS_POINTS = 1440;
const HOURLY_CACHE_TTL_MS = DASHBOARD_REFRESH_POLICY_MS.metar;
const HOURLY_FORCE_REFRESH_DEDUP_MS = 60_000;
const _hourlyCache = new Map<string, { ts: number; data: HourlyForecast }>();
const _hourlyRequestCache = new Map<string, Promise<HourlyForecast>>();
const MAX_HOURLY_DETAIL_CONCURRENT_REQUESTS = 3;
@@ -366,13 +367,16 @@ const SESSION_CACHE_TTL_MS = DASHBOARD_REFRESH_POLICY_MS.metar;
type HourlyCacheEntry = { ts: number; data: HourlyForecast };
function isFreshHourlyCacheEntry(entry: HourlyCacheEntry | null | undefined) {
return Boolean(entry && Date.now() - Number(entry.ts || 0) < SESSION_CACHE_TTL_MS);
function isFreshHourlyCacheEntry(
entry: HourlyCacheEntry | null | undefined,
maxAgeMs = SESSION_CACHE_TTL_MS,
) {
return Boolean(entry && Date.now() - Number(entry.ts || 0) < maxAgeMs);
}
function readSessionCache(
city: string,
options: { allowStale?: boolean } = {},
options: { allowStale?: boolean; maxAgeMs?: number } = {},
): HourlyCacheEntry | null {
if (typeof window === "undefined") return null;
try {
@@ -382,7 +386,7 @@ function readSessionCache(
if (
item &&
item.ts &&
(options.allowStale || Date.now() - item.ts < SESSION_CACHE_TTL_MS)
(options.allowStale || Date.now() - item.ts < (options.maxAgeMs ?? SESSION_CACHE_TTL_MS))
) {
return item;
}
@@ -392,10 +396,10 @@ function readSessionCache(
function readHourlyCacheEntry(
cacheKey: string,
options: { allowStale?: boolean } = {},
options: { allowStale?: boolean; maxAgeMs?: number } = {},
): HourlyCacheEntry | null {
const cached = _hourlyCache.get(cacheKey);
if (cached && (options.allowStale || isFreshHourlyCacheEntry(cached))) {
if (cached && (options.allowStale || isFreshHourlyCacheEntry(cached, options.maxAgeMs))) {
return cached;
}
@@ -1193,6 +1197,13 @@ async function fetchHourlyForecastForCity(
if (cached) {
return cached.data;
}
} else {
const recentlyRefreshed = readHourlyCacheEntry(cacheKey, {
maxAgeMs: HOURLY_FORCE_REFRESH_DEDUP_MS,
});
if (recentlyRefreshed) {
return recentlyRefreshed.data;
}
}
const requestKey = options.ignoreCache ? `${city}:${resParam}:live` : `${city}:${resParam}`;
@@ -2459,6 +2470,7 @@ export {
MAX_HOURLY_DETAIL_CONCURRENT_REQUESTS,
HOURLY_DETAIL_REQUEST_TIMEOUT_MS,
HOURLY_CACHE_TTL_MS,
HOURLY_FORCE_REFRESH_DEDUP_MS,
_hourlyCache,
__readHourlyCacheEntryForTest,
resolveCityDetailFromBatch as __resolveCityDetailFromBatchForTest,
+19 -1
View File
@@ -28,4 +28,22 @@ export function buildForceRefreshProxyCachePolicy(
};
}
export const buildCityDetailProxyCachePolicy = buildForceRefreshProxyCachePolicy;
export function buildCityDetailProxyCachePolicy(
forceRefresh: string | null | undefined,
revalidateSeconds = 15,
): ProxyCachePolicy {
if (isForceRefreshValue(forceRefresh)) {
return {
fetchMode: "no-store",
responseCacheControl: "no-store, max-age=0",
};
}
return {
fetchMode: "revalidate",
responseCacheControl: `public, max-age=${revalidateSeconds}, s-maxage=${revalidateSeconds}, stale-while-revalidate=${Math.max(
revalidateSeconds * 3,
30,
)}`,
revalidateSeconds,
};
}