perf(cache): bound reported-failures set and halve default in-process budget
CI / check (push) Successful in 45s
CI / tests-integration (push) Successful in 2m22s
CI / tests-unit (push) Successful in 2m35s
CI / tests-quality (push) Successful in 1m5s
CI / tests-imaging-browsers (push) Successful in 1m57s
CI / tests-ui (push) Successful in 6m9s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 22s

This commit is contained in:
openhands committed 2026-10-22 18:31:33 +02:00
1 parent e4564abc5e
commit e96c408ac1
1 file changed
+14 -3
+14 -3
View File
@@ -49,7 +49,7 @@ function readPositiveInt(name: string, fallback: number): number {
// In-process budget. Entries are evicted least-recently-*used* (see getMemory), // In-process budget. Entries are evicted least-recently-*used* (see getMemory),
// so a hot key only leaves when a hotter one takes its place. The default is // so a hot key only leaves when a hotter one takes its place. The default is
// generous because the layer exists to keep origin calls off the hot path. // generous because the layer exists to keep origin calls off the hot path.
const MAX_MEMORY_ENTRIES = readPositiveInt("CACHE_MEMORY_MAX_ENTRIES", 2_000); const MAX_MEMORY_ENTRIES = readPositiveInt("CACHE_MEMORY_MAX_ENTRIES", 1_000);
// Single-flight: a key being (re)computed is awaited by concurrent callers // Single-flight: a key being (re)computed is awaited by concurrent callers
// instead of each starting its own `fn()` (cache-stampede protection). // instead of each starting its own `fn()` (cache-stampede protection).
@@ -62,9 +62,20 @@ const generations = new Map<string, number>();
const MAX_GENERATION_ENTRIES = MAX_MEMORY_ENTRIES * 2; const MAX_GENERATION_ENTRIES = MAX_MEMORY_ENTRIES * 2;
// Keys whose most recent background refresh failed, so a persistently broken // Keys whose most recent background refresh failed, so a persistently broken
// origin reports once instead of on every read of a hot key. // origin reports once instead of on every read of a hot key. Bounded so a
// high-cardinality stream of failing keys can never grow this without limit.
const MAX_REPORTED_FAILURES = 500;
const reportedFailures = new Set<string>(); const reportedFailures = new Set<string>();
function recordReportedFailure(key: string): void {
if (reportedFailures.has(key)) return;
if (reportedFailures.size >= MAX_REPORTED_FAILURES) {
const oldest = reportedFailures.values().next().value;
if (oldest !== undefined) reportedFailures.delete(oldest);
}
reportedFailures.add(key);
}
/** Drop a key from the in-process cache (used when an upstream value changes). */ /** Drop a key from the in-process cache (used when an upstream value changes). */
export function invalidateMemory(key: string): void { export function invalidateMemory(key: string): void {
generations.set(key, (generations.get(key) ?? 0) + 1); generations.set(key, (generations.get(key) ?? 0) + 1);
@@ -180,7 +191,7 @@ export async function cached<T>(
// read: a broken origin would otherwise flood the log from a // read: a broken origin would otherwise flood the log from a
// single hot key. // single hot key.
if (reportedFailures.has(key)) return; if (reportedFailures.has(key)) return;
reportedFailures.add(key); recordReportedFailure(key);
logger.warn("[cache] background refresh failed", { logger.warn("[cache] background refresh failed", {
key, key,
error: String(error), error: String(error),