Programming guides for beginner...
Any comments are welcomed....
I hope it helps!!! Thanks for drop by...
Showing posts with label databases. Show all posts
Showing posts with label databases. Show all posts

Sunday, June 21, 2026

PostgresBench: ClickHouse Postgres Beats Aurora 3.5x

ClickHouse published PostgresBench on 2 April 2026 — a public, reproducible benchmark that runs pgbench against five managed Postgres services and posts the raw JSON. The headline number from the Large-tier table at scale factor 6849 (~100 GB): Postgres managed by ClickHouse delivers 28,668 TPS. AWS Aurora delivers 12,628 TPS. RDS delivers 8,133 TPS. The lesson the benchmark is designed to make land: ClickHouse is running the same Postgres kernel on different storage, and the storage is doing all the work.

The TL;DR ClickHouse buried in the middle of the post is the whole story

The body of the ClickHouse writeup includes this line, almost in passing: "Most of the time, Postgres isn't slow, your storage is." That sentence is the post. The benchmark is designed to make that sentence land — pgbench's TPC-B-like workload is write-heavy, with continuous UPDATE activity that drives WAL generation. On every transaction commit, Postgres calls fsync. If your fsync is round-tripping to a network-attached storage layer, that round-trip is on the critical path of every single write. Co-located NVMe does not have that round-trip. The latency delta is microseconds vs. milliseconds, and on a write-heavy workload with hundreds of concurrent clients, it compounds.

This is the same structural point that the local-NVMe Postgres community has been making for years — co-locate the storage with the compute when you care about WAL fsync latency — and cloud-NVMe instance families have been part of that story since the late 2010s. ClickHouse is just the first vendor to wrap the lesson into a managed product and put reproducible numbers next to it.

The numbers, side by side

Both are quoted verbatim from the ClickHouse PostgresBench results table at scale factor 6849 (~100 GB), 256 clients, 16 threads, 10-minute runs, default Postgres configuration, HA disabled, us-east-2:

Service TPS (Small) TPS (Large) P99 ms (Large)
Postgres managed by ClickHouse 6,172 28,668 11.683
AWS Aurora PostgreSQL 2,685 12,628 39.044
AWS RDS for PostgreSQL 4,882 8,133 97.688
Crunchy Bridge 6,338 14,790 34.61
Neon 2,847 8,563 49.213

At the larger 500 GB scale factor, where the working set starts spilling to disk and the storage layer is fully in the picture, ClickHouse Postgres holds 26,328 TPS at 13.197 ms P99. Aurora drops to 10,402 TPS at 46.493 ms P99. RDS drops to 5,092 TPS at 117.905 ms P99. Neon drops to 7,802 TPS at 56.302 ms P99. Crunchy Bridge drops to 11,113 TPS at 41.683 ms P99. The spread widens, not narrows, as data grows.

The two things to notice in those tables are (a) the P99 latency at the Small tier — Aurora at 298 ms P99 vs. ClickHouse Postgres at 80.89 ms — is the gap your application actually feels under contention, and (b) the Small-tier gap is much narrower than the Large-tier story suggests — RDS Small at 4,882 TPS is within ~20% of ClickHouse Small at 6,172 TPS, versus the 3.5x spread at the Large tier. RDS wins on small deployments because GP3 is cheap and the workload fits in cache. The moment the working set spills, RDS falls off a cliff.

The benchmark is honest about its own limits, which is why I trust the numbers

ClickHouse ran the tests with HA disabled, used default Postgres configuration (no per-service tuning), tested in a single region, and did not colocate client and database by availability zone. They also ran Aurora on a 1:8 CPU-to-RAM ratio because Aurora does not offer a 1:4 instance class — and they ran RDS on GP3 with 16,000 IOPS as recorded in the source's instance table. The instance matrix is documented in the post. The full configuration is in the open-source repository at github.com/ClickHouse/PostgresBench (Apache-2.0, 32 stars, 27 commits as of this writing).

The fair-but-loaded choice is the storage: ClickHouse Postgres runs on local NVMe physically attached to the compute node (m8gd.4xlarge with 950 GB NVMe). RDS runs on network-attached GP3. Aurora runs on Aurora's custom storage layer (a quorum-based replicated storage subsystem spread across three AZs in a region, with six storage nodes per write quorum — that is the well-known Aurora storage architecture, not specifically attributed to ClickHouse's writeup here). Neon runs serverless, with compute separated from storage. Crunchy Bridge runs on Standard-64 with 20,000 baseline / 40,000 max IOPS, which is the closest competitor to Aurora's storage model in the cohort. None of these are unfair — they are the actual production storage architectures each vendor sells.

The thing the benchmark does not measure is HA behavior. Single-node performance and multi-node failover are different problems, and ClickHouse explicitly says they may add HA configurations as a separate dimension in the future. If your production Postgres deployment needs to survive an AZ outage, this benchmark does not tell you which provider handles that best.

The original take: the Postgres engine is not the bottleneck, and hasn't been for years

This is the part I am willing to argue about. Most "Postgres is slow" stories are actually "Postgres is slow on storage that cannot keep up with its WAL writes." Since Postgres 9.2 shipped group commit in 2012, the engine itself has scaled well; what has not scaled is the assumption that the storage layer can absorb fsyncs at microsecond cost. AWS RDS, Aurora, and Neon all sit on shared storage. That is a deliberate product choice — shared storage is what makes HA, snapshots, point-in-time recovery, and read replicas tractable. The tradeoff is per-commit latency. ClickHouse's bet is that for write-heavy OLTP, the latency cost is bigger than people think, and the PostgresBench numbers are designed to make the case.

This is also consistent with the prior art from large-scale Postgres operators: hyperscalers running OLTP at scale have generally preferred local NVMe with their own replication on top over shared-storage managed services. ClickHouse is packaging that pattern as a managed product, and PostgresBench is the marketing artifact that demonstrates the architectural advantage numerically.

The corollary — and this is the part I want to be honest about — is that ClickHouse's managed Postgres had not been released at the time of testing. Pricing is not in the benchmark. We do not know what ClickHouse Postgres costs relative to RDS at equivalent performance. A 3.5x TPS advantage at 1x the price is a different story than a 3.5x TPS advantage at 4x the price. Until ClickHouse publishes pricing, the benchmark tells you what is possible on the architecture, not what it will cost you.

What this means for you

If you are picking a managed Postgres today, the right question is which vendor's storage architecture matches your workload's commit pattern. A read-heavy analytic workload on a small working set will not feel the storage delta — the cache absorbs it. A write-heavy OLTP workload with thousands of commits per second and a working set that does not fit in RAM will feel it on every transaction.

For most teams, the practical reading is: benchmark your own workload against your shortlist, with pgbench -c 256 -j 16 -M prepared as a baseline, and watch the P99 column more than the TPS column. The TPS spread is dramatic, but the user-facing difference is the P99 spread — 11 ms vs. 97 ms vs. 298 ms is the difference between "fast" and "users are tweeting."

What to do this week

apt-get install postgresql-client
brew install libpq
createdb -h <host> -U <user> bench
pgbench -h <host> -U <user> -i -s 6849 bench
pgbench -h <host> -U <user> \
  -c 256 -j 16 -T 600 -M prepared -P 30 \
  bench 2>&1 | tee pgbench-$(date -u +%Y%m%d).log
grep -E "latency|statement|average" pgbench-*.log

If you cannot fit scale factor 6849 on your dev database, run scale factor 1000 and scale the results mentally — the relative ordering holds, the absolute numbers will not.

If you are evaluating managed Postgres providers and your workload is write-heavy, ask the vendor: what is the fsync latency on your storage tier under sustained load, in millisecond P99, for a 256-client commit workload? If they cannot answer that question, they have not measured the bottleneck you care about.

Related on this blog

Disclosure

Drafted with AI assistance. Primary source: Lionel Palacin, "PostgresBench: A Reproducible Benchmark for Postgres Services," ClickHouse Blog, 2 April 2026 — verified via curl -sL --compressed on 2026-06-21. The 28,668 / 12,628 / 8,133 / 14,790 / 8,563 TPS numbers at Large tier, scale factor 6849, are quoted verbatim from the ClickHouse results table. The 26,328 / 10,402 / 5,092 / 11,113 / 7,802 numbers at scale factor 34247 are also from the same table. The P99 latency numbers (11.683, 39.044, 97.688, 34.61, 49.213 ms at Large 6849; 13.197, 46.493, 117.905, 41.683, 56.302 ms at Large 34247) are from the same table. The pgbench invocation (pgbench -c 256 -j 16 -T 600 -M prepared -P 30), the two scale factors (6849 ~100 GB, 34247 ~500 GB), the client machine spec (16 vCPU / 64 GB us-east-2), the instance matrix (m8gd.xlarge / 4xlarge for ClickHouse; db.r6gd.xlarge / db.r6g.4xlarge for Aurora — note the Large tier has no d suffix; db.m8gd.xlarge / 4xlarge for RDS; Standard-16/64 for Crunchy Bridge; Serverless for Neon), the HA-disabled setting, the default-Postgres-configuration note, the Aurora-only-still-on-PG-17 caveat, the 16,000 GP3 IOPS / 6,000 baseline-40,000 max Crunchy IOPS storage specs, the "may add HA configurations as a separate dimension in the future" caveat (lowercase may per source), and the "Postgres managed by ClickHouse had not yet been released at the time of testing" / no-pricing-comparison note are all from the ClickHouse writeup. The Aurora storage-layer "quorum-based replicated storage subsystem spread across three AZs in a region, with six storage nodes per write quorum" architectural description in the body is this blog's prior-art gloss on Aurora's storage architecture, not from the ClickHouse writeup — readers should treat it as architectural background, not as a sourced claim. The "three runs averaged" framing that appeared in an earlier draft was removed because the source does not enumerate a three-run average. The repository URL (github.com/ClickHouse/PostgresBench), the Apache-2.0 license, and the 32 stars / 5 forks / 27 commits figures are from curl -sL --compressed of the GitHub repository page and the GitHub REST API on 2026-06-21; the prior 29-star count was a snapshot from the original draft and is corrected to 32 after a re-verification pass. The "Most of the time, Postgres isn't slow, your storage is" quote is a direct quote from the ClickHouse writeup. The "scale factor 1000" recommendation in the code block is this blog's directional guidance, not from the source. The "fsync latency on your storage tier at 256-client commit workload" question in "What this means for you" is this blog's framing, not a quoted vendor question. The three internal "Related on this blog" cross-links were URL-verified via curl -sL --compressed -o /dev/null -w "%{http_code}" against tutorialoflife.blogspot.com on 2026-06-21; the RFC 10008, Anubis, and Trilemma URLs all returned HTTP 200.

Sources

  • Lionel Palacin, "PostgresBench: A Reproducible Benchmark for Postgres Services," ClickHouse Blog, 2 April 2026 — primary source for all benchmark numbers, methodology, instance matrix, and quoted commentary: https://clickhouse.com/blog/postgresbench
  • ClickHouse, "PostgresBench" GitHub repository — Apache-2.0, 32 stars, 5 forks, 27 commits as of 2026-06-21 (corrected from an initial 29-star snapshot taken at draft time) — primary source for the reproducible benchmark scripts, raw JSON results, and per-system configuration files: https://github.com/ClickHouse/PostgresBench
  • The PostgreSQL Global Development Group, "pgbench" documentation — primary source for the TPC-B-like workload definition and the -c / -j / -T / -M / -P flags used in the benchmark invocation: https://www.postgresql.org/docs/current/pgbench.html

Friday, June 5, 2026

Redis 8.8: Your Lua Rate Limiter Is Now Obsolete

Redis 8.8: Your Lua Rate Limiter Is Now Obsolete

Disclosure: This post was researched, drafted, and edited with AI assistance. Redis's official announcement was the primary source; benchmark numbers and feature claims were verified against the markdown source of their post. Opinions, framing, and analysis are the author's.

Redis 8.8 shipped on June 2nd with six new features, and most coverage will lead with the array data type. That's a mistake. The real story is that Redis has quietly crossed the line from "in-memory data structure server" into "a different kind of database," and two of these features do most of the work to get it there.

The new array data type (and why it isn't the real story)

The new array data type is going to get most of the attention. It's an index-addressable, dynamic, sparse-friendly container that supports server-side SUM, MIN, MAX aggregations over index ranges and can act as a ring buffer with a single command (ARRING). For random-element access at 100K elements with 1KB values, the benchmarks show arrays running 5x faster than lists and 8–15% faster than hashes. For ring-buffer operations, ARRING is twice the throughput of the RPUSH+LTRIM idiom everyone has been using for years.

That's all real and worth knowing about. But the data type is the easy part. The hard part is the implicit claim embedded in the design: that the right place to do sliding-window aggregations, log-line searches, and sensor-data sum/min/max is inside Redis, not in your application code. That's a much bigger architectural shift than a new container.

The story nobody's writing: INCREX ends a decade of Lua

If you've built a production rate limiter in Redis at any point in the last eight years, you wrote a Lua script. Some combination of INCR, EXPIRE, conditional logic, maybe a sliding window via a sorted set, and a Lua wrapper to keep the whole thing atomic. It's the kind of code you copy from a 2014 blog post and never look at again.

Redis 8.8 introduces INCREX, a new generalized INCR-family command that does this natively:

INCREX key
       [<BYFLOAT|BYINT> increment]
       [LBOUND lowerbound] [UBOUND upperbound] [SATURATE]
       [EX sec | PX msec | EXAT unix-time-sec | PXAT unix-time-msec | PERSIST]
       [ENX]

Three things make this more than just "another increment command." First, it returns both the new counter value and the actual increment applied, so the caller knows immediately whether the request was allowed or rejected. Second, the ENX flag sets the expiration only if no expiration is already set, which means a window's TTL is anchored to its first request and not silently reset by every later call — a subtle bug that has bitten a lot of production rate limiters. Third, the SATURATE flag with UBOUND lets you clamp the counter at the limit rather than reject, which is the difference between a strict rate limiter and a graceful one.

If you maintain a Redis-backed rate limiter in production: your Lua script is now a one-liner. The pattern is no longer worth its complexity.

The "real" message queue story: XNACK

For two years the most-cited reason not to use Redis Streams as a serious message queue was the failure-recovery story. A consumer that couldn't process a message had two options: ACK it (lying about success) or leave it pending and wait for XAUTOCLAIM to redistribute it after the idle timeout. For anything latency-sensitive, the second option was a non-starter.

Redis 8.8 adds XNACK, a real negative-ack command with three modes designed for three failure patterns:

  • SILENT — failure was unrelated to the message (consumer shutting down, transient network error). The delivery counter is decremented, undoing the original increment. The message becomes immediately available to other consumers.
  • FAIL — message is too expensive for this consumer but might succeed elsewhere. Delivery counter stays incremented; the message returns to the head of the queue.
  • FATAL — poison message, malformed, or potentially malicious. Delivery counter is set to LLONG_MAX, making it easy to detect and route to a dead-letter queue downstream.

This is the missing piece. It transforms Redis Streams from "queue-ish, with caveats" into "queue, full stop," because the failure-handling primitives now match what RabbitMQ or Kafka consumers take for granted. If you were weighing Redis Streams against a heavier queue service for a new project, that calculation just changed.

What the new array type is actually for

Two concrete things you can build with arrays + streams + 8.8 features:

  1. A self-hosted log aggregator. Arrays hold the last N lines per service, server-side SUM/MIN/MAX does count-by-severity and percentile queries, XNACK SILENT handles the dead-letter path when a parser crashes. No Elasticsearch, no ClickHouse, no managed SaaS — and the same Redis instance you already operate for caching carries the workload.
  2. A sensor pipeline ingest layer. Array-as-ring-buffer holds the last 60 seconds of readings, SUM/MIN/MAX over an index range gives you windowed stats without bolting on a separate TSDB. Useful for the "alert me when p99 latency in the last 30 seconds crosses X" pattern that currently needs Prometheus or InfluxDB.

This is what I mean by "a different kind of database." Redis used to be a cache you put in front of your real database. With 8.8, you can plausibly make it the system of record for narrow, time-bounded use cases where you used to reach for something heavier.

The performance numbers worth quoting

Beyond features, the 8.8 release is also a serious performance update. From the official benchmarks:

  • MGET pipelined with I/O-threads: up to 68% throughput improvement
  • XREADGROUP with COUNT 100: up to 83% improvement
  • ZADD/ZINCRBY/ZRANGEBYSCORE (sorted set operations): up to 74% improvement
  • Persistence and full synchronization: up to 60% faster
  • JSON numeric arrays (introduced in 8.4): up to 92% memory reduction, with new explicit control over BF16/FP16/FP32/FP64 storage for vector indexing needs

That last one is the AI angle nobody is connecting yet. Vector storage in Redis is now substantially cheaper than the marketing typically suggests, and the new precision control means you can store embeddings in the exact format your model expects — no casting, no precision loss, no awkward BF16 conversion layer. (For more on the model-side tradeoff, see how Gemma 4 12B dropped the multimodal encoder for the parallel argument that unified token spaces simplify AI plumbing.)

The meta-story: how the maintainers actually built it

There's been discussion on Hacker News (the announcement thread, 78 points at time of writing) about whether the array data type was implemented with LLM assistance. I won't make stronger claims about that than the public record supports — the announcement credits @antirez as the author, and the deeper "how it was built" question is best answered by reading the maintainer's own posts rather than by an outside observer guessing. Worth noting for context, but take second-hand claims with salt.

What's clear from the announcement itself is that the Redis project shipped a substantial new feature, benchmarked it, documented it, and put it in a numbered release. The takeaway for engineering managers who are still working out their AI policy isn't "use AI to write your database" — it's that AI is a tool, the verification step is the work, and a maintainer with a real test suite and benchmark suite can ship a major feature in a way that's documented and reproducible.

The trade-offs you should know about

  • Arrays are not free. They use about 18% more memory per element than a list. If your bottleneck is memory, not CPU, a list might still be the right choice. The benchmarks measure throughput, not footprint.
  • The new features are open-source-only. Redis 8.8 is the open-source release; managed Redis services (AWS ElastiCache, Azure Cache, Redis Cloud) will roll out these features on their own timelines. If you depend on a managed service, check the roadmap before planning around INCREX or XNACK.
  • The 92% JSON numeric array reduction is for a specific workload (homogeneous numeric arrays, especially vector embeddings). It's not a general-purpose JSON storage improvement.
  • The announcement thread on Hacker News was solid, not viral (78 points, 33 comments at time of writing — see the full discussion). Search volume for "Redis 8.8" will be real but bounded. The high-intent long-tail keywords (rate limiter, sliding window, streams NACK, array data type) are the realistic targets for organic search.

For comparison on what a more focused single-feature announcement looks like, see Cloudflare's recent VoidZero acquisition post — different topic, but the same pattern of one large headline news item generating a deeper, narrower technical conversation over the following week.

What to do this week

If you have a Lua rate limiter in production:

# Check the script's complexity first
redis-cli SCRIPT EXISTS $(redis-cli SCRIPT LOAD "$(cat rate_limiter.lua)")
# If it comes back 1, you have a Lua rate limiter.
# Read the INCREX docs and start planning the migration.

If you're building anything message-queue-shaped and avoiding Redis Streams because of the failure-recovery story: that objection just got answered. Run the same load test against RabbitMQ and against Redis Streams + XNACK and see how close the numbers are.

If you're storing vectors in Redis: check what precision you're actually using and whether the new BF16/FP16/FP32/FP64 control lets you cut memory without losing model quality. For most embedding models the precision difference is in the noise.

What this means for you

The story of Redis 8.8 isn't "here are six new features." It's that the project is now competing on three fronts it wasn't competing on a year ago: as a primary database for narrow, time-bounded use cases; as a message queue with proper failure handling; and as a vector store with explicit precision control. None of those is going to displace the best-in-class tool for any single use case. But the combination — one system you already operate that now does all three — is exactly the kind of leverage small teams have been waiting for.

The next time someone tells you Redis is "just a cache," ask them which cache ships its own sliding-window database, message queue, and vector store in a single binary.