Skip to content

Cut Supabase egress ~99%: shared cached pubs pull, column trims, kill happy-hour browser poll#188

Merged
iamjohnnymac merged 12 commits into
mainfrom
claude/beer-reports-notifications-ul5z47
Jun 12, 2026
Merged

Cut Supabase egress ~99%: shared cached pubs pull, column trims, kill happy-hour browser poll#188
iamjohnnymac merged 12 commits into
mainfrom
claude/beer-reports-notifications-ul5z47

Conversation

@iamjohnnymac

Copy link
Copy Markdown
Owner

Why

Supabase free-tier egress warnings. Measured the actual causes by routing all Supabase traffic through a local byte-counting proxy:

  1. One production build = 167.2MB / 1,108 requests. ~20 routes each pulled the full pubs table (~2MB) via select('*'), and suburb pages pulled it up to 8x per render (getSuburbBySlug + getSuburbPubs + getNearbySuburbs + getSiteStats, in both generateMetadata and the body). We deploy ~10x on a busy dev day — builds alone were ~1.7GB/day.
  2. /happy-hour re-fetched the entire table from every visitor's browser every 60 seconds (~122MB/hour per open tab) — even though the live "on now" state is derived client-side from schedules + a ticking clock.
  3. 7 list pages cost 4.4MB per 5-minute revalidation window, every window, forever.

What

  • New src/lib/cachedPubs.tsunstable_cache-shared pulls (1h TTL, tag pubs). Raw rows are cached, toPub() maps fresh per render so live happy-hour status is never stale. The list pull excludes google_opening_hours (~735KB, 35% of a pull; only pub detail + happy-hour pages render it) which also keeps the entry under Vercel's 2MB data-cache limit. Happy-hour pages get a second small full-column pull filtered to happy-hour pubs.
  • select('*') eliminated from all pubs-table queries in src/lib/supabase.ts — explicit column lists, dropping columns nothing reads (context, created_at, phone, place_id, updated_at).
  • ~20 server routes + the suburb/pub-page aggregates switched to the cached variants (the weekly-snapshot cron deliberately keeps a fresh pull).
  • HappyHourClient poll deleted — the clock already drives the live state.
  • /api/admin/review calls revalidateTag('pubs') on approvals, so price changes appear promptly despite the 1h TTL.

Evidence (byte-counting proxy, before vs after)

Measurement Before After
One production build 1,108 requests / 167.2MB 240 requests / 1.7MB (−99%)
7 pages, one revalidation window 12 full pulls / 4.4MB 6 trimmed pulls / 1.5MB (cold stampede); 0 on the second pass
6 different pages, warm cache 12 pulls 1 request
Per-pull (compressed) 377KB 249KB
/happy-hour open in a browser tab ~122MB/hour 0

Steady state: ~6MB/day of hourly refreshes vs multiple GB/day.

Independent verification

A separate agent verified the work against the built app: tsc clean, 309 tests pass, diff review found no correctness bugs, 12 routes content-checked (prices, photos, opening hours, JSON-LD all present), and exact venue parity with production/happy-hour 168 = 168 with identical link sets, /fremantle 63 = 63 — proving the happy-hour pre-filter drops nothing. Its three minor flags (TransportHubPage still uncached, dead Pub.source field, stale "auto-refreshes every 60s" copy) are fixed in the follow-up commit.

Also in this branch (already reviewed in this session)

The branch continues from merged PR #179's history — only the egress commits (2f77fef, 47a69d3, docs) are new here.

https://claude.ai/code/session_01HRdu5eYTJoUZ5sPJqVUzkk


Generated by Claude Code

claude added 10 commits June 10, 2026 04:01
- New src/lib/slackNotify.ts: posts to a Slack incoming webhook
  (SLACK_WEBHOOK_URL env var), best-effort and never throws
- /api/price-report POST now pings Slack the moment a report lands
  (price report, happy hour report, or stale-price flag)
- Daily price-check cron now also checks the review queue and sends
  a reminder listing pending price reports (with oldest age) and pub
  submissions; silent when the queue is empty
- Piggybacks on the existing cron because Vercel Hobby caps cron jobs
  at two and both slots are taken

https://claude.ai/code/session_01HRdu5eYTJoUZ5sPJqVUzkk
- Happy hour row no longer reads 'TBC · 7 days 4pm - 5pm': the schedule
  is the value when the price is unknown, and '7 days' renders as
  'daily'; when the price IS known it shows red with the schedule on a
  right-aligned sub-line, so the dotted leader never gets crushed
- Checked row flags stale prices: aging/stale dates render amber with
  '· Nd ago' appended (recency tier now passed into the receipt)
- 'from an aggregator lead' provenance jargon is now 'spotted online',
  moved to its own SOURCE row
- Standard-pint row only renders when it differs from the hero price
  (it duplicated the big number on every non-happy-hour pub)
- Micro-type floor raised from 8px to ~10px (banner sub-line, price
  label, amenity chips, CTA); CTA flex-wraps instead of squeezing

https://claude.ai/code/session_01HRdu5eYTJoUZ5sPJqVUzkk
…r browser poll

Supabase free-tier egress warnings traced to three compounding causes:

1. ~20 server routes each pulled the full pubs table (~2.0MB) per
   revalidation via getPubs()'s select('*'), and suburb pages pulled it
   up to 8x per render (getSuburbBySlug + getSuburbPubs + getNearbySuburbs
   + getSiteStats, in both generateMetadata and the page body). One
   production build cost 167MB of egress across 1,108 requests (measured
   through a byte-counting proxy).

2. HappyHourClient re-fetched the entire table from every visitor's
   browser every 60 seconds (~122MB/hour per open tab) even though the
   live 'on now' state is derived client-side from schedules + a clock.

3. select('*') shipped columns nothing reads (context, created_at,
   phone, place_id, updated_at) plus google_opening_hours (~735KB, 35%
   of a pull) to pages that never render it.

Fixes:
- src/lib/cachedPubs.ts: unstable_cache-shared pulls, 1h TTL, tag
  'pubs'. Raw rows cached (not Pub objects) so toPub() computes live
  happy-hour status fresh per render. List pull excludes
  google_opening_hours to stay under Vercel's 2MB data-cache entry
  limit; happy-hour pages get full columns for just happy-hour pubs.
- supabase.ts: explicit column lists everywhere; aggregate helpers
  accept a pre-fetched pubs array.
- All server callers switched to the cached variants; weekly-snapshot
  cron deliberately keeps a fresh pull.
- HappyHourClient poll removed.
- /api/admin/review calls revalidateTag('pubs') on approvals so price
  changes don't wait out the TTL.

Measured: build egress 167.2MB -> 1.7MB (-99%); tsc clean; 309 tests
pass. Runtime + independent agent verification in progress before PR.

https://claude.ai/code/session_01HRdu5eYTJoUZ5sPJqVUzkk
…dead Pub.source, fix stale copy

Independent verification (agent-run, against the built app through a
byte-counting proxy) found no correctness bugs; these are its three
minor flags:
- TransportHubPage (4 pubs-near-* routes) was still on uncached getPubs()
- Pub.source was dead — no pubs.source column exists in any migration
  and nothing rendered it; removed from toPub and the Pub type
- /happy-hour blurb said 'auto-refreshes every 60s' — data no longer
  refetches; the live status updates from the clock

https://claude.ai/code/session_01HRdu5eYTJoUZ5sPJqVUzkk
@vercel

vercel Bot commented Jun 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
perthpintprices Ready Ready Preview, Comment Jun 11, 2026 5:42am

Request Review

…red pubs cache

The happy-hour declutter on main supersedes the refresh-copy tweak from
this branch; both PROJECT-STATUS timelines kept.

https://claude.ai/code/session_01HRdu5eYTJoUZ5sPJqVUzkk
@iamjohnnymac iamjohnnymac marked this pull request as ready for review June 12, 2026 02:59
@iamjohnnymac iamjohnnymac merged commit 75028af into main Jun 12, 2026
3 checks passed
iamjohnnymac added a commit that referenced this pull request Jun 12, 2026
…#176)

In-repo BYOK article drafting pipeline (scripts/draft-article.mjs):
Supabase-grounded brief -> Claude draft -> humanizer -> Pexels photos.
Dev-only /draft preview (404s in production, noindex); article body
extracted to ArticleView with the live route rewired to it.
Conflict resolution on merge: /articles/[slug] keeps the shared cached
pubs pull from #188. Verified post-merge: tsc clean, 321 tests pass.

https://claude.ai/code/session_01HRdu5eYTJoUZ5sPJqVUzkk
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants