forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpayload-cache.ts
More file actions
59 lines (52 loc) · 2.43 KB
/
Copy pathpayload-cache.ts
File metadata and controls
59 lines (52 loc) · 2.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// Firestore payload cache for precomputed admin stats payloads.
//
// Separate from posthog.ts's per-query cache (`admin_stats_cache`). This stores
// whole computed route payloads (profitability, infra-costs) keyed by their
// request params, written off the request path by the cron precompute endpoint.
//
// The admin service's configured Redis (light-eel-27878.upstash.io) is dead, so
// the old `@/lib/redis` caching was a silent no-op. Firestore is always
// reachable here via firebase-admin (same path posthog.ts + macos-versions use).
// Payloads are JSON-stringified to sidestep Firestore's no-nested-arrays rule.
import { createHash } from "crypto";
import { getDb } from "@/lib/firebase/admin";
const CACHE_COLLECTION = "admin_stats_payload_cache";
const MAX_PAYLOAD_CHARS = 900_000; // Firestore field cap ~1 MB; skip oversized
type CacheDoc = { payload: string; freshAt: number };
// Doc ids can't contain `/`. Callers use keys like
// `profitability:v1:90:1.2:0.3` which are safe, but if a key ever contains a
// slash we sha1-hash it to keep the write valid.
function docId(key: string): string {
return key.includes("/") ? createHash("sha1").update(key).digest("hex") : key;
}
export async function getPayload<T>(key: string): Promise<{ data: T; freshAt: number } | null> {
try {
const snap = await getDb().collection(CACHE_COLLECTION).doc(docId(key)).get();
if (!snap.exists) return null;
const d = snap.data() as CacheDoc;
if (!d?.payload) return null;
return { data: JSON.parse(d.payload) as T, freshAt: d.freshAt ?? 0 };
} catch {
return null; // best-effort cache read; never throws
}
}
// Stamp a payload with the age of the data it carries. Every consumer route
// serves precomputed payloads at any age (no TTL eviction), so without this a
// broken precompute cron looks identical to live data forever. `freshAt` is
// epoch ms: the moment the payload was computed on a cache hit, or `Date.now()`
// on a fresh inline compute.
export function withFreshness<T extends object>(
data: T,
freshAt: number,
): T & { freshAt: number } {
return { ...data, freshAt };
}
export async function setPayload(key: string, data: unknown): Promise<void> {
try {
const payload = JSON.stringify(data);
if (payload.length > MAX_PAYLOAD_CHARS) return;
await getDb().collection(CACHE_COLLECTION).doc(docId(key)).set({ payload, freshAt: Date.now() });
} catch {
// best-effort cache write; never block the response
}
}