Day 54 of 100·Project Deep-Dive Day·475 words

What Postgres gives you for free — the interview version

Every engine-level feature your projects lean on, named and claimed.

Know your own system

Yesterday's engine theory, converted into your talking points. Walk through what you use and what it cost you to not build:

You usePostgres machineryWhat you didn't build
conditional updates (CAS)row locks + predicate re-eval (EvalPlanQual)distributed lock service
SKIP LOCKED claimsrow-lock skipping in the plannerleader election / job scheduler
unique constraintsB-tree unique indexes + error codesapplication-level dedup coordinators
pg_advisory_lock at bootlock manager keyed by bigintmigration orchestration service
transactions spanning business+outboxWAL + 2-phase within the commitdual-write reconciliation
recursive CTEiterative execution enginegraph database
JSONB policiesTOASTed binary JSON + operatorsconfig service
pgvector + HNSWANN graph indexvector database
sequencesMVCC-safe countersticket server
PITR (traverse)WAL archivingevent sourcing backbone

The pitch line: "My architecture is 'lean on the database until it measurably hurts.' Every layer people bolt on — locks, queues, schedulers, dedup, vector search — Postgres already provides in transactional form. The discipline is knowing which one becomes the bottleneck first: connections (pool), write throughput (partitions), or query shape (indexes) — and I can rank them for my workloads."

The costs you accepted knowingly (say these too — nothing is free):

  1. Single-writer bottleneck: all writes serialize through one primary's WAL — the ceiling for both projects. The remedy ladder: batching, partitioning (doesn't remove the WAL), then replication/sharding (moves the ceiling).
  2. VACUUM dependence: your write-heavy tables (submissions, outbox, notification_delivery with attempts churn) produce dead tuples; autovacuum tuning is the first operational knob under write growth. The notification table's UPDATE-heavy claim pattern (attempts+1, next_attempt_at per drain) is exactly the update-churn that makes autovacuum visible — worth monitoring n_dead_tup there.
  3. Connection process model: each connection = a backend process (~10 MB); 3 services × pools must stay modest — that's why pool sizes of 10–25, not fashion.
  4. Operational surface: backups/replication/monitoring are yours — traverse answered with S3 WAL archive + Aiven; zoth's gap is known (Day 33/34).

One more claimable detail — the FOR UPDATE SKIP LOCKED engine view: the lock manager keeps a wait queue per row; SKIP LOCKED opts out of queueing (returns "not eligible") instead of blocking — that's why the drain scales linearly with workers: no lock convoy, just disjoint set operations.

The zoth-vs-traverse engine pressure comparison

  • Zoth: tiny data, human-paced writes, transactional correctness first — engine headroom is enormous; the audit chain (append-only + read-last) is the one pattern that stresses its single-writer serialization if scaled (Day 50's fix).
  • Traverse: 100+ users writing submissions + workers updating derived state + notification churn — the realistic first engine pressure is autovacuum/connections at maybe 10–100× current load. The remedy ladder is already scoped (partitions by month, pool caps, read replicas for stats).

Bulletproof your resume

Say these answers aloud before moving on

These are the follow-ups an interviewer will actually ask about your projects.

01

Which Postgres feature would you say you exploited most?

Conditional updates as compare-and-swap. Every state machine transition (JIT lifecycle), every idempotent claim (set-once topics, ring closure), and every race resolution rides on the engine re-evaluating the WHERE clause against the current row version after lock acquisition. It replaced a lock service, a leader elector, and a dedup coordinator in one idiom — and it's testable with plain SQL concurrency tests.

02

Where does Postgres become your bottleneck, concretely?

The single primary's WAL is the global serialization point — fine at my write rates (events per minute), with a clear ladder: batch writes (broadcast fanout already batches), partition hot tables for retention and pruning, replicas for read load, and only then shard. The nearer-term engine issues are autovacuum on my update-churn tables (notification_delivery attempts) and connection count as replicas multiply — both monitoring knobs, both already on my list.

03

Why did you skip Redis for zoth entirely?

Because every job Redis would do was already done transactionally: caching would serve ~1 rps reads (nothing hot), a cache adds an invalidation problem I don't have, and queueing is RabbitMQ's job in that stack. The honest framework: add a component when it removes a measured bottleneck. Traverse needed Redis because its job pipeline and cache serve a real latency contract; zoth's constraints were correctness and cost — both better served by one Postgres.