Awesome Reviewers

Cache entries must be tied to a versioned identity of the data and only served while that identity is still valid; refresh/retry must be deterministic and bounded.

Apply this as a coding standard:

Example pattern (keyed cache with TTL + bounded retry):

class KeyedCache<T> {
  private key?: string;
  private value?: T;
  private pending?: Promise<void>;
  private failedAt?: number;
  constructor(
    private readonly ttlMs: number,
    private readonly retryAfterMs: number,
    private readonly now: () => number,
  ) {}

  read(key: string, load: () => Promise<T>): T | undefined {
    this.start(key, load, false);
    return this.value;
  }

  async get(key: string, load: () => Promise<T>, refresh: boolean): Promise<T | undefined> {
    await this.start(key, load, refresh);
    return this.value;
  }

  private start(key: string, load: () => Promise<T>, refresh: boolean): Promise<void> {
    const sameKey = this.key === key;
    if (!sameKey || refresh) {
      this.key = key;
      this.value = undefined;
      this.pending = undefined;
      this.failedAt = undefined;
    }

    // Serve cached value; reload behind it on TTL expiry.
    if (this.value !== undefined && !refresh) {
      if (this.failedAt === undefined && this.now() - (this.failedAt ?? 0) < this.ttlMs) {
        return Promise.resolve();
      }
      // fallthrough: trigger reload behind
    }

    if (this.pending) return this.pending;
    if (this.failedAt !== undefined && this.now() - this.failedAt < this.retryAfterMs) {
      return Promise.resolve();
    }

    const slot = Promise.resolve();
    this.pending = slot.then(async () => {
      try {
        const v = await load();
        if (this.key === key) this.value = v;
        this.failedAt = undefined;
      } catch {
        if (this.key === key) this.failedAt = this.now();
      } finally {
        if (this.pending === slot) this.pending = undefined;
      }
    });
    return this.pending;
  }
}