accounts
Version:
Tempo Accounts SDK
76 lines • 2.52 kB
JavaScript
/**
* Per-`kv`-instance in-memory L1 cache. Lives for the lifetime of the worker
* (or process) and short-circuits the L2 `kv.get` on hot keys so a request
* with N callers that share the same `cached()` key only pays for one
* remote read.
*
* `WeakMap` keys mean dropping a `kv` reference releases its L1 entries
* automatically — no need to thread a lifecycle hook through callers.
*/
const memoryStore = new WeakMap();
/**
* Per-`kv`-instance in-flight dedupe table. Multiple concurrent
* `cached(kv, key, fn)` calls for the same key share a single `fn()`
* invocation instead of stampeding the upstream.
*/
const inflightStore = new WeakMap();
/**
* Reads `key` from a two-tier cache (in-memory L1, `kv`-backed L2). If absent
* or expired in both tiers, calls `fn`, stores the result with a
* `Date.now() + ttl * 1000` expiry in both tiers, and returns it. Defaults
* to caching forever (no expiry). Kv read/write failures are swallowed so
* cache misses never break the caller.
*
* Concurrent calls for the same key share a single `fn()` invocation.
*/
export async function cached(kv, key, fn, options = {}) {
const { ttl = Infinity } = options;
// L1: in-memory.
const memory = memoryFor(kv);
const local = memory.get(key);
if (local && local.expiresAt > Date.now())
return local.value;
// Coalesce concurrent calls for the same key.
const inflight = inflightFor(kv);
const pending = inflight.get(key);
if (pending)
return pending;
const promise = (async () => {
// L2: kv.
const entry = await kv.get(key).catch(() => null);
if (entry && entry.expiresAt > Date.now()) {
memory.set(key, entry);
return entry.value;
}
const value = await fn();
const expiresAt = ttl === Infinity ? Infinity : Date.now() + ttl * 1000;
const fresh = { expiresAt, value };
memory.set(key, fresh);
await kv.set(key, fresh).catch(() => { });
return value;
})();
inflight.set(key, promise);
try {
return await promise;
}
finally {
inflight.delete(key);
}
}
function memoryFor(kv) {
let map = memoryStore.get(kv);
if (!map) {
map = new Map();
memoryStore.set(kv, map);
}
return map;
}
function inflightFor(kv) {
let map = inflightStore.get(kv);
if (!map) {
map = new Map();
inflightStore.set(kv, map);
}
return map;
}
//# sourceMappingURL=kv.js.map