Event analytics, at ridiculous scale.

A weekend that turned into a project. Node, Redis, RabbitMQ, TimescaleDB — ten thousand events a second at sub-five ms. No roadmap. No investors. Just vibes and hyper-tables.

app.pulseanalytics.io
OverviewAug 17 – Aug 23
Pageviews
48,245
8.6%
Sessions
18,206
12.4%
Unique visitors
12,848
14.2%
Active now
247
Pageviews
per hour
last 7 daysPrevious
7505002500
MonTueWedThuFriSatSun
Top pagesviews
Referrerssessions
Node.jsTypeScriptExpressNext.jsTailwindRedisRabbitMQTimescaleDBPostgreSQLDockerAWSNginxNode.jsTypeScriptExpressNext.jsTailwindRedisRabbitMQTimescaleDBPostgreSQLDockerAWSNginx

The pipeline, roughly
three moving parts.

Collect hot, process async, persist for time. Every piece picked because it refuses to blink under load.

01Ingest

Fire and forget at the edge.

Dockerized Express on AWS. Tracking ID resolves via Redis in under a millisecond. Server returns 204 No Content, then drops the payload on the queue. Client never waits on processing.

server/collect.ts
ts
app.post('/collect', async (req, res) => {
  const siteId = await redis.get(
    `tk:${req.headers['x-pulse-key']}`
  )
  if (!siteId) return res.sendStatus(404)

  // fire-and-forget
  res.sendStatus(204)
  queue.publish('events.raw', {
    siteId,
    ts: Date.now(),
    ...req.body,
  })
})
02Process

RabbitMQ absorbs the spikes.

Workers pull batches off the queue, validate, enrich with geo and UA data, and insert in bulk. When traffic spikes, the queue grows. No timeouts, no data loss — just backpressure that resolves itself.

workers/process.ts
ts
consumer.on('events.raw', async (batch) => {
  const rows = batch
    .map(enrich)     // geo, UA, referrer
    .filter(valid)   // drop malformed

  await timescale.insertMany('events', rows)

  // ack only after successful persist
  batch.ack()
})
03Query

TimescaleDB answers instantly.

Continuous aggregates pre-compute 1-minute, 1-hour, and 1-day rollups. Dashboards query the summary, not the raw events. Sub-second, no matter how deep the history.

db/queries.sql
sql
-- served from a continuous aggregate
SELECT time_bucket('1 hour', ts) AS hour,
       COUNT(*) AS views,
       COUNT(DISTINCT user_id) AS uniques
  FROM events_1h
 WHERE site_id = $1
   AND ts > now() - interval '7 days'
 GROUP BY hour
 ORDER BY hour;
10,000
Requests / second
< 5ms
Ingest p90
0.00%
Data loss
< $40
Monthly infra

Small system, big numbers,
zero pager duty.

Ten thousand RPS,
zero drops.

Load-tested on a single box. Ingest stays under 5ms p90 while the queue drains in the background.

Live feed247 on
page_view/stack3ms
button_click/4ms
form_submit/hire-me2ms
page_view/architecture5ms
scroll_depth/story4ms

Continuous aggregates

TimescaleDB pre-buckets the rollups. Dashboards query summaries, never raw events.

time_bucket()1m · 1h · 1d

Per-endpoint rate limits

Redis token buckets keyed by route and key. Rude clients bounce, everyone else flies.

x-ratelimit-remaining87 / 100

Schemaless events

Any JSON payload, typed at the query layer. Move fast, break only yourself.

props{ "cart_value": 42.1 }

Backpressure, not loss

RabbitMQ absorbs the spike, workers catch up. The ingest never apologizes.

queue depth12,480 → 0 in 4s

Cookieless by default

Tracking IDs live in Redis. No device fingerprinting, no third-party pixels, nothing for your DPO to cry about.

document.cookie""

Node, Redis, RabbitMQ,
TimescaleDB. On Docker, on AWS.

Node.jsTypeScriptExpressRedisRabbitMQTimescaleDBDockerAWS

Not real customers.
Real commit messages.

Threw 10k RPS at it on a t3.medium. It didn't flinch. I was honestly a little disappointed — I had the pager ready.
ST
stress-test.log
Last Tuesday, 2 AM
Asked for 204 No Content. Got 204 No Content. Ten out of ten, would 204 again. Truly a visionary HTTP status.
cURL
A very satisfied client
curl -v · /collect
Claude helped me plan this. I built it the old-fashioned way — one tab of docs, two coffees, zero frameworks I didn't understand.
ME
Me
Author, perpetual refactorer
Available · June 2026

Find this
impressive?

Available for senior backend and platform roles. I like queues, hyper-tables, and code that stays up at 3 AM.

QueuesHyper-tablesBackpressureSub-5ms p900 downtime
Get in touchStar on GitHub
MIT2 weekends0 investors