Files
anonymous_github/src/core/async-cache.ts
T
Thomas Durieux 6434337923 perf: bound request work and resource usage (#866)
* perf: bound request work and resource usage

* fix: address performance review regressions

* fix: guard content expiration and dashboard state

* fix: tolerate cache outages and retire derived paths

* fix: revalidate expiration claims and status pagination

* fix: retry interrupted repository metadata cleanup

* fix: retire obsolete content cache storage

* fix: activate tree metadata and recheck expiration claims

* fix: fence cache publication across expiration

* fix: recover staged trees and enforce cache lifecycle cleanup

* fix: prune expired transforms during database outages

* fix: finish conference removals and reload expiration candidates
2026-10-08 03:38:19 +01:00

23 lines
1.0 KiB
TypeScript

/** Bounded TTL cache with one calculation per key and retry after failure. */
export class AsyncCache<T> {
private entries = new Map<string, { expires: number; value: Promise<T> }>();
constructor(private ttl: number, private capacity: number, private now = () => Date.now()) {}
get size() { this.prune(); return this.entries.size; }
clear() { this.entries.clear(); }
private prune() {
for (const [key, entry] of this.entries) if (entry.expires <= this.now()) this.entries.delete(key);
}
get(key: string, calculate: () => Promise<T>): Promise<T> {
this.prune();
const cached = this.entries.get(key);
if (cached) return cached.value;
if (this.entries.size >= this.capacity) this.entries.delete(this.entries.keys().next().value!);
const entry = { expires: Infinity, value: Promise.resolve().then(calculate) };
this.entries.set(key, entry);
entry.value.then(() => { entry.expires = this.now() + this.ttl; }, () => {
if (this.entries.get(key) === entry) this.entries.delete(key);
});
return entry.value;
}
}