← Blog/Field Manual Series/Article 05 · Week 7

Billions of Log Rows, One Command to Make Them Disappear.

Omenabyte Intelligence·Sep 20, 2026·5 min read
Just Use Postgres — The Field Manual for Replacing MongoDB, Redis, Elasticsearch & More

Telemetry and event logs only grow. Dump them into one giant table and every query — even one scoped to a single day — ends up scanning far more data than it needs to. That's usually the exact moment a team reaches for a dedicated time-series database like InfluxDB.

Two native Postgres features cover most of what people reach for InfluxDB to get: declarative partitioning and BRIN indexes.

Split the table without splitting the app

One PARTITION BY RANGE on the time column, and the app still writes to a single table called events:

partition.sql — one logical table, many physical ones
CREATE TABLE events (
  id          bigserial,
  event_time  timestamptz NOT NULL,
  payload     jsonb
) PARTITION BY RANGE (event_time);

CREATE TABLE events_2026_06 PARTITION OF events
  FOR VALUES FROM ('2026-06-01') TO ('2026-07-01');

One note that will save you a confusing error: id is deliberately not a primary key here. On a partitioned table, any unique or primary key constraint has to include the partition column. If you need one, it's PRIMARY KEY (id, event_time).

A featherweight index built for this exact shape of data

index.sql — block range index
CREATE INDEX idx_events_time_brin
  ON events USING BRIN (event_time);

A BRIN (Block Range Index) stores just the minimum and maximum value for each physical block of disk, instead of indexing every single row the way a B-Tree does. For data that arrives roughly in time order — which almost all event data does — that's enough to skip millions of irrelevant pages instantly, at a fraction of a B-Tree's size.

query.sql — a single day of events
SELECT count(*) FROM events
WHERE event_time BETWEEN '2026-06-10' AND '2026-06-11';

Run EXPLAIN on that and you'll see the planner only touches the one relevant partition — the rest are pruned before the query even runs.

Retention becomes a metadata operation

retention.sql — the whole policy
DROP TABLE events_2026_06;

That's it. No DELETE scan, no bloat left behind to vacuum. Dropping a partition removes its rows instantly — I tested this directly: 500 seeded rows, one DROP TABLE, zero rows left, no measurable delay.

Why this beats a dedicated time-series database

  • Retention policies are a DROP TABLE, not a scheduled bulk delete job.
  • BRIN indexes cost a sliver of what an equivalent B-Tree would.
  • It's the same SQL as the rest of your schema — no separate query language, no separate client library.

Where InfluxDB still wins

Purpose-built downsampling and retention policies out of the box, at ingest rates in the millions of points per second. If you're managing your own partitions and indexes comfortably below that, you're not missing anything by staying on Postgres.

Field Manual Series · Every recipe tested

This is one of eight infrastructure swaps.

Just Use Postgres is a 24-page field manual on replacing MongoDB, Redis, Elasticsearch, Pinecone, and more with the database you're probably already running. Every recipe in it — including this one — was run against a live Postgres instance before it went in the book.

A DROP TABLE instead of a nightly bulk delete — the retention policy writes itself.

Up next in the series

Stop Recomputing Your Dashboard on Every Page Load

See the full series →