PUL-22: cache the store snapshot on Upstash Redis, fix two uncached hot paths - #14
Merged
Merged
Conversation
…ot paths Real Supabase egress-quota restriction (service outage) today traced back to readSnapshot() in src/lib/woo/mirror.ts, which reassembles a store's entire order/customer/product history from Postgres on every cache miss -- tens of MB on a real store per that file's own comment. The in-process memo only helps a warm serverless instance, which per that same comment is the exception on Vercel, not the rule. Three real, uncached/undercached hot paths found and fixed: 1. /api/whatsapp/chats -- polled every 60s (was 30s) by any open Inbox tab, paid the full readSnapshot() cost every single time with zero HTTP caching, and the client explicitly forced cache: "no-store". This was almost certainly the dominant driver of today's incident: every open inbox tab, every 30-60s, for as long as it stayed open. Fixed: same private + Vary: Cookie Cache-Control already used on /api/analytics and /api/customers/[key], no-store removed client-side so the browser can actually use it, poll interval lengthened to match the cache window. 2. /api/whatsapp/products -- hit on every 250ms-debounced keystroke in the campaign product picker, no caching at all. Same treatment, max-age=120 since a catalogue changes far less often than a chat list. 3. The deeper fix: readSnapshot() now checks a Redis-backed cache (src/lib/storage/snapshot-cache.ts, Upstash's free tier, confirmed no card required) before ever touching Postgres, and writes back on a miss. forgetSnapshot() invalidates it. The cron sync route proactively warms it right after each store's sync -- the one place a full Postgres reassembly is actually expected, once per store per 10-minute cycle, not once per user-facing request. R2 was the first choice (also zero egress) but requires a payment method to enable even on the free tier; Upstash doesn't. Also fixed two stale docs found along the way: README.md's SNAPSHOT_CACHE_MINUTES default said 60, code and .env.example say (and mean) 10. .env.example's kv_store comment described a table dropped in the multi-tenant migration and never recreated -- corrected to describe where WooCommerce credentials and the mirror actually live now, and that nothing in the app currently reads SUPABASE_SERVICE_ROLE_KEY at all. Not yet verified against a real synced store -- the fresh Supabase project (today's other recovery) has no connected stores yet. Checks (typecheck, lint, the Redis REST API shape) all pass; the actual readSnapshot() cache hit/miss/warm path needs a real store connected and synced to exercise for real. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Jira issue
PUL-22
Summary
Real Supabase egress-quota restriction today traced to
readSnapshot()reassembling the whole order/customer/product mirror from Postgres on
every cache miss. Three real fixes:
/api/whatsapp/chats-- polled every 30-60s per open Inbox tab withzero caching and an explicit
no-store. Almost certainly thedominant driver of today's incident. Fixed with the same
Cache-Controlpattern already used on/api/analytics./api/whatsapp/products-- same gap, hit on debounced keystrokes.readSnapshot()now checks Upstash Redis (free, nocard required -- R2 was the first choice but demands a payment
method even on its free tier) before touching Postgres. The cron
sync route proactively warms it right after each sync.
Also fixed two stale docs found along the way (
SNAPSHOT_CACHE_MINUTESdefault mismatch, a
kv_storecomment describing a table that nolonger exists).
Checks
npm run typechecknpm run lint(0 errors, 5 pre-existing warnings unrelated)Supabase project (today's other recovery) has no connected
stores yet, so the actual cache hit/miss/warm path in
readSnapshot()hasn't been exercised end-to-end for real.