Skip to content

perf: batch event/ad tracking into beacon requests - #172

Merged
snackman merged 1 commit into
masterfrom
perf/batch-tracking
Sep 30, 2026
Merged

snackman merged 1 commit into
masterfrom
perf/batch-tracking

Conversation

@snackman

Copy link
Copy Markdown
Owner

Summary

Every impression used to fire its own POST (~10 on /kbw2026 load). Now they're batched.

Client — new src/lib/tracking-queue.ts, used by event-tracking.ts and ad-tracking.ts:

  • Queues items and flushes every 3s, once the queue hits 25 items, or right away on visibilitychange → hidden / pagehide
  • Sends with navigator.sendBeacon using an application/json Blob. Falls back to fetch with keepalive: true when sendBeacon is missing or returns false
  • Clicks and pin-clicks flush immediately, along with any pending impressions, so outbound link clicks survive navigation
  • Per-session dedup is the same as before (events: event_id:event_type for all types; ads: impressions deduped, clicks always sent). visitor_id logic is unchanged. SSR-safe and never throws

Server — /api/events/track and /api/ads/track:

  • Body is either a single object (legacy, still returns 400 if invalid) or { events: [...] } with up to 50 items
  • Invalid items in a batch are dropped and the request still returns 200 (inserted: n). More than 50 items returns 413
  • One bulk .insert(rows). The 42P01 graceful handling is kept
  • req.json() ignores content-type, so sendBeacon Blobs parse fine
  • Schemas moved to src/lib/api-validation.ts (EventTrackSchema, AdTrackSchema) with new parseBatch / parseBatchBody helpers

Verification

  • tsc --noEmit, eslint and next build pass
  • npm test: all 279 tests pass, including new src/lib/__tests__/tracking-queue.test.ts (batching, size-triggered flush, immediate click flush, visibility/pagehide flush, fetch fallback, dedup, batch schema parsing)
  • Playwright on next dev at /kbw2026: before 9 events + 1 ads = 10 requests; after 1 + 1 = 2 requests (inserted: 9 and inserted: 1 rows confirmed)
  • curl: a mixed valid/invalid batch returns inserted: 1; a 51-item batch returns 413

🤖 Generated with Claude Code

Impressions are now queued client-side (src/lib/tracking-queue.ts) and
flushed every 3s / at 25 items / on visibilitychange-hidden and pagehide
via navigator.sendBeacon (fetch keepalive fallback). Clicks flush
immediately. Per-session dedup semantics unchanged.

/api/events/track and /api/ads/track now accept either a single object
(legacy) or { events: [...] } (max 50, invalid items dropped, 413 over
cap) and do a single bulk insert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
sheeets Ready Ready Preview Sep 30, 2026 12:15am UTC

Request Review

@snackman
snackman merged commit ff48f7c into master Sep 30, 2026
3 checks passed
@snackman
snackman deleted the perf/batch-tracking branch September 30, 2026 00:51

This branch was successfully deployed

1 active deployment
Preview — 829c5c80 Deployed Sep 30, 2026 by vercel[bot]
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.

1 participant