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
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:
1 parent
e4564abc5e
commit
e96c408ac1
1 file changed
+14
-3
+14
-3
@@ -49,7 +49,7 @@ function readPositiveInt(name: string, fallback: number): number {
|
||||
// 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
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
// 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>();
|
||||
|
||||
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). */
|
||||
export function invalidateMemory(key: string): void {
|
||||
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
|
||||
// single hot key.
|
||||
if (reportedFailures.has(key)) return;
|
||||
reportedFailures.add(key);
|
||||
recordReportedFailure(key);
|
||||
logger.warn("[cache] background refresh failed", {
|
||||
key,
|
||||
error: String(error),
|
||||
|
||||
Reference in new issue
Block a user