Reduce terminal detail refresh traffic
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user