Hacker News loves a rewrite story. This week’s spark: a Postgres-compatible database, rewritten in Rust, claiming to pass 100% of the Postgres regression tests. Impressive. But if you’re the CTO holding the RPO/RTO bag, you know 100% of test suites isn’t 100% of production. Your job isn’t to cheer for memory safety—it’s to keep revenue flowing and data boring. This post gives you a concrete, opinionated risk map before you even think about pointing production traffic at a greenfield Postgres-in-Rust.
What “100% of regression tests” actually means
The Postgres regression suite is a treasure: it checks SQL syntax, planner and executor behavior, datatypes, indexes, some MVCC semantics, and a long tail of core functionality. Passing it is table stakes for protocol and SQL compatibility.
But it isn’t a proxy for operational readiness. What those tests largely do not cover:
- Durability under real power loss: fsync behaviors across filesystems (ext4, XFS, ZFS), group commit timing, torn page detection, WAL segment boundaries.
- Replication edge cases: sync/async replication quorum behavior, timeline switches, cascading replicas, delayed standby, and failover fencing.
- Vacuum and bloat control in anger: autovacuum interactions at high write rates, HOT updates, visibility map accuracy over weeks of load.
- Upgrade paths: in-place upgrades via pg_upgrade semantics, catalog migrations across major versions, logical migration wagons.
- Observability invariants: stability and accuracy of
pg_stat_*views at high concurrency,auto_explainhooks, stats reset semantics.
Passing the suite tells you the engine can pretend to be Postgres. It doesn’t tell you what happens at 02:13 on a Sunday during a rolling network partition when your primary hits an fsync stall and your failover logic gets itchy.
Compatibility extends far beyond SQL
If you’ve been on-call for Postgres, you know the “database” is an ecosystem, not just a server binary. Before you even lab a Rust rewrite, inventory the features you actually use:
- Extensions:
pg_stat_statements,pgcrypto,pg_trgm,hstore, PostGIS, Timescale, HypoPG. Does the rewrite support Postgres’s C-ABI for extensions? If not, which equivalents exist and how mature are they? - Logical decoding: decoding plugins (wal2json, pgoutput variants), Debezium integration, replication slots semantics, vacuum interactions.
- Foreign Data Wrappers (FDWs): are you using S3, Kafka, or cross-DB FDWs? How does the rewrite handle them (if at all)?
- Protocol and auth quirks: wire-level behavior with PgBouncer (Tx vs Session pooling), SCRAM-SHA-256 auth, GSSAPI/Kerberos, SSL renegotiation policies.
- DDL and migration tools: Flyway, Liquibase, Django, Rails, Prisma, TypeORM. Does anything assume behaviors around transactional DDL, advisory locks, or
search_path?
Make a simple two-column table for your stack: “used feature” vs “status in Rust rewrite.” Any red cells on extensions, logical decoding, or HA should halt your production ambitions.
Operational maturity is 80% of Postgres’s value
Ten years ago, running Postgres in production meant you earned your stripes. Today, the ecosystem makes it almost boring—if you stick to well-beaten paths:
- HA/Failover: Patroni, Stolon, repmgr. Does the Rust engine integrate with these? If not, what orchestrates leader election, fencing, and replica bootstrap?
- Backup/PITR: base backups + WAL archiving with pgBackRest or WAL-G. Are archive formats and restore mechanics compatible? Can you prove Point-in-Time Recovery to an arbitrary timestamp?
- Upgrades: does it have a
pg_upgrade-equivalent or will you be doing logical migrations for every major bump? - Observability:
pg_stat_activity,pg_locks,pg_stat_replication,pg_stat_wal,pg_stat_io,auto_explain, query sampling, log_line_prefix hygiene. Can your current dashboards and alerts plug in unchanged? - Vacuum, bloat, and storage: visibility map correctness, freeze strategy, relation bloat detection and mitigation. Are you getting
pg_stat_all_tablesparity?
If your answer is “we’ll build our own operators and backup flows,” budget accordingly. You’re swapping a decade of community-hardened muscle memory for a new, bespoke runbook.
Performance claims need apples-to-apples yardsticks
Rust’s memory safety is a win. It tells you what won’t happen (use-after-free), not what will (latency). Be skeptical of cherry-picked graphs. Define your yardsticks:
- Workload shape: OLTP (small rows, hot indexes), read-heavy dashboard queries, analytical scans? Use representative mixes, not
pgbench -Sfantasies. - Latency SLOs: p50 and p99 under realistic concurrency. Track tail behavior during checkpoints, autovacuum runs, and failover events.
- Feature parity under load: JIT, parallel query, bitmap heap scans, work_mem pressure, sort spill behavior, group commit, synchronous_commit tradeoffs.
- Hardware and FS: same NVMe, same filesystem, same mount options (barriers, noatime), same kernel. Pin CPU, isolate IRQs, disable turbo if you’re serious.
Set hard gates. For example: “Rust engine must hold p99 within 2x of our tuned Postgres baseline at 2x our current QPS, with zero correctness deltas observed over 7 days, and failover RTO ≤ 90 seconds.” If it can’t clear that with headroom, you’re buying risk, not performance.
Data correctness under duress: the only benchmark that matters
Most disasters don’t look like crashes; they look like partial progress. That’s where database engines earn their keep. Vet the rewrite with jepsen-style thinking:
- Crash loops at write hotspots: kill -9 the process during WAL flush, yank power during checkpoints, repeat under load. On restart, do you see torn page detection and consistent replay?
- WAL integrity: corrupted segments, partial segment archiving, archive/restore idempotence, timeline branching. Can you restore to N-1 transactions reliably?
- Replication truth: synchronous replica stalling the primary, async replica falling behind > 1 hour, network partitions. Are write acknowledgements aligned with your
synchronous_standby_namescontract? - Checksums and verification: page checksums on, background scrubbing,
pg_checksums-equivalents, proactive detection paths in logs and metrics.
It’s easy to be fast on happy paths. It’s hard to be right when the kernel lies about durability, your SAN is moody, and someone trips over a PDU.
A pragmatic adoption path if you still want in
If you’re determined to test-drive a Rust Postgres, don’t start by swapping your primary. Run a structured program:
- Segment by blast radius: pick a low-risk, low-feature-footprint workload—read-mostly internal analytics or a new microservice with a simple schema. Avoid PostGIS, time-series compression, or heavy triggers.
- Build a compatibility matrix: list every feature you use (extensions, auth, CDC, FDWs) against the rewrite’s documented support. Color-code: green/yellow/red. Stop if you see red on CDC or HA.
- Replicate your real traffic: dual-write a small % of production requests to both databases (or replay production traces). Store result hashes and compare. Gate promotion on zero correctness deltas for 7–14 days.
- CDC parity test: if your stack uses Debezium or downstream consumers, wire them to the Rust engine. Verify no missed or re-ordered events, especially under DDL churn.
- Backup/PITR drills: take a base backup, archive WAL, and perform a timed restore to T-5 minutes. Verify row counts, logical checksums, and application-level invariants.
- HA game day: force a primary crash during peak load, validate failover RTO/RPO against your SLOs, and confirm clients (via PgBouncer) reconnect without mass error storms.
- Rollout gates: promote to a single low-risk production tenant or environment after 2 consecutive clean weeks. Keep an immediate rollback path via
pg_dumpor logical migration back to stock Postgres. - Exit plan documented: decide now how you’ll bail out (dump/restore, logical copy). Validate it on a nontrivial dataset before you ship anything customer-facing.
Time-box this. Expect 6–8 weeks of dual-running and 2–3 engineer-months to reach a go/no-go decision with confidence. Anything less is theater.
Where a Rust rewrite could shine today
- Edge or embedded deployments: if the engine offers a smaller footprint or better cold-start, it could be ideal for local inference caches or offline-first apps.
- Tight supply-chain and memory-safety constraints: regulated environments that prize memory safety and a modern Rust codebase may accept operational tradeoffs for a narrower, safer surface.
- Greenfield feature slices: one internal service with simple schemas and modest traffic can serve as a proving ground without entangling your core revenue paths.
None of these should be your billing ledger or customer identity store.
When to say no for now
- Extension-heavy deployments (PostGIS, Timescale, custom C extensions). Without ABI parity, you’ll spend quarters replacing mature, supported components with lookalikes.
- Multi-region HA with tight RPOs. Cross-region replication, promotion fencing, and split-brain avoidance are battle scars you don’t want to earn twice.
- High-volume CDC pipelines feeding analytics or search. Logical decoding edge cases are where correctness bugs hide.
- Regulated workloads requiring documented controls, FIPS-validated crypto, deterministic backup/restore playbooks, and auditor-friendly observability.
Budget what this actually costs
New engines don’t replace line items; they create them. Rough order of magnitude for a real evaluation (not a blog-fueled weekend):
- Engineering: 2–3 engineer-months for infra, tests, and observability. Add 1 SRE familiar with your current Postgres to avoid false equivalence.
- Tooling: writing or adapting operators, backup scripts, dashboards. Expect 1–2 weeks to hit parity with your current alerts.
- Chaos time: at least two game days (crash/failover and restore/PITR). Budget 6–8 hours each with stakeholders present.
- On-call risk: first quarter in production usually carries 2–3 extra pages until runbooks stabilize. Price that stress into your decision.
If a vendor is pitching this, ask them to co-fund or staff the tests. If they won’t, that’s signal.
The nearshore angle: rent testing horsepower, not heroics
This kind of evaluation is perfect for a nearshore pod: repetitive, disciplined, time-boxed, and measurable. With 6–8 hours of overlap from Brazil to the US and a 20–30% cost advantage over US contractors, you can spin up a small squad to:
- Build the compatibility matrix and test harnesses.
- Standing up HA, backup, and observability stacks in parallel.
- Run dual-write comparisons and correctness diffs across real traffic replays.
- Automate game days: power cuts, kill -9 loops, and PITR drills on schedule.
The output you want is boring: green dashboards, clean diffs, and runbooks that a sleepy on-caller can follow at 2 a.m.
Bottom line
Rust is not the point. Data correctness and operational drudgery are. A Postgres rewrite that nails both will earn its place over time. Until then, treat “100% of regression tests” as the invite to evaluate—not the permission to migrate. Run a structured program, stage your risks, and insist on boring outcomes before you move anything with a customer’s name on it.
Key Takeaways
- Passing the Postgres regression suite is necessary but nowhere near sufficient for production readiness.
- Inventory extensions, CDC, FDWs, auth, and tooling; any red cell in your matrix is a stop sign.
- Operational maturity (HA, PITR, upgrades, observability, vacuum hygiene) is 80% of the real work.
- Define performance gates on p50/p99 under realistic load and chaos, not happy-path microbenchmarks.
- Jepsen-style crash, WAL, and replication tests are where correctness bugs show up—run them.
- Adopt via low-blast-radius pilots, dual-writing, and time-boxed rollout gates; keep a documented exit plan.
- Say “not yet” for extension-heavy, multi-region, CDC-centric, or regulated workloads.
- Expect 2–3 engineer-months to evaluate seriously; consider a nearshore pod to contain cost and schedule.