DEV Community

Cover image for Can a Mac Mini Handle 100 Million Rows?
Pratik Singh
Pratik Singh

Posted on

Can a Mac Mini Handle 100 Million Rows?

Row-store vs columnar performance on M4

I made ClickHouse and Postgres Fight to Find Out πŸ₯Š

One Mac Mini. Two databases. 100 million rows of LLM traces. Let's see who blinks first.

🧐 What is this, and why should you care?

I wanted to answer one silly question: how far can a plain Mac Mini M4 push a database before it falls over?

Not a giant cloud box. Not a 64-core server. A Mac Mini. The little thing that sits on your desk.

So I took a real problem β€” storing LLM traces (more on that in a sec) β€” and threw a growing pile of data at two very different databases:

  • Postgres, the row store we all know and love.
  • ClickHouse, the columnar database built for analytics.

I started at 1 million rows and climbed all the way to 100 million. Same data on both. Same machine. Same questions.

If you build stuff with LLMs, run dashboards on a database, or you've ever wondered "should I really move off Postgres?" β€” this one is for you. By the end you'll know where each database wins, where it hurts, and how much a humble Mac Mini can actually take.

Spoiler: the result surprised me. Nobody died. The story is still brutal. πŸ˜…


Prerequisites βœ…

Nothing scary. You're good if you have:

  • A basic idea of SQL (you know what a row and a query are).
  • Heard of Postgres.
  • Columnar Databases. You can learn from my friend's article: Here [Must Read]
  • A rough idea of "LLM observability". If not, the next section fixes that.
  • A machine to try it on. Mine was a Mac Mini M4 Pro.

That's it. Let's dig in.


πŸ€” The Problem (and why it pokes everyone)

Okay, quick story.

When you build an app on top of an LLM, you're kind of flying blind. Which model got called? How many tokens did it burn? What did it cost? Why was it slow?

So people log everything. Every LLM call, every tool call, every step becomes a "trace". One row each. This is what tools like Langfuse do (it's a popular open-source LLM observability platform).

Here's the catch. Traces pile up fast. One chat can be 5-8 rows. Multiply by thousands of users. You hit millions of rows before you know it.

And the dashboards on top? They aren't simple lookups. They're big questions. "Cost per model for the last 90 days." "p95 latency across 40 million calls." That's a lot of number-crunching on every single refresh.

Langfuse started on Postgres. As traces grew, those dashboard queries got slow. So in their v3 rewrite, they moved trace storage to ClickHouse. And in early 2026, ClickHouse acquired Langfuse.

Actual Story which I tried to mimic in my experiment: Here

That made me curious. Was the move actually worth it? Or just hype?

I didn't want to read about it. I wanted to see it. On the most normal machine I had lying around, a Mac Mini. Two birds, one stone: test the migration story, and find the limits of the little box. 🐦🐦


πŸ§ͺ The Experiment

No code here, just the plan.
I wrote four small scripts. Here's what each one did.

1. The data faker 🎲

Goal: make realistic fake LLM traces on the fly, without saving one giant file.
It generated a mix of models (GPT, Claude, embeddings), token counts, costs (cost = tokens Γ— price), latencies, and a few errors here and there. Same seed every run, so both databases got the exact same rows. That part is key. It's the only way the fight stays fair.

Result: I could summon 1 row or 100 million, identical on both sides.

2. The ClickHouse runner 🟑

Goal: load the data into ClickHouse and time everything.
It built the table, then loaded data in steps β€” 1M, 5M, 10M, 25M, 50M, 100M. At each step it measured four things: ingest speed, disk used, RAM used, and how long three real queries took. All the records are share in the last part of this article.

Result: one clean row of numbers per step, saved to a CSV.

3. The Postgres runner 🐘

Goal: do the exact same thing for Postgres.
Same data, same steps, same three queries. And I gave Postgres its best shot β€” proper indexes, its fastest bulk-load path (COPY), one query at a time. No handicap.

Result: matching numbers, so I could line them up side by side.

Those three queries, by the way, are the ones that actually matter for observability:

  • Fetch one full trace (a point lookup).
  • A dashboard aggregation (cost + latency per model, last 90 days).
  • A metrics query (crunch stats across all of history).

4. The report maker πŸ“Š

Goal: turn the boring CSV into charts I could actually read.
It spat out a PDF with everything β€” ingestion, disk, memory, query latency.
Result: the pretty pictures you're about to see.

Then I hit go and let the Mac Mini grind for a few hours. Watched the temps of my Mac in a corner. Drank some Irani Tea β˜• (IFYKYK)


πŸ“Š The Results

Alright. The good stuff.

First, the headline I did not expect: both databases finished all 100 million rows. Nobody crashed. Nobody ran out of memory. On a Mac Mini! my itsy-bitsy aluminium box did surprise me.

So if your only question was "can a Mac Mini hold 100M rows of traces?" β€” yes. Easily. Both did.

But "finished" and "usable" are very different things. Look closer and the gap gets wild.

- Ingesting the data πŸ›ƒ

ClickHouse ingested at a flat ~78,000 rows/sec the whole way. 1 million or 100 million, it didn't care. Straight line 😘🀌🏻

Postgres started around 32,000 rows/sec... and slowly sank to ~14,000 rows/sec by 100M. The more data it held, the slower new inserts got. Those indexes aren't free.

At 100 million rows, ClickHouse was ingesting about 5.6Γ— faster.

Ingestions

Ingest speed, disk size, memory, and ClickHouse's compression.

- Disk space πŸ’Ώ

Same 100 million rows:

  • ClickHouse: 48 GB
  • Postgres: 116 GB

ClickHouse packed it down about 2.4Γ— smaller. Its columns compress really well (~5.5Γ— internally). On a 300 GB SSD, that's the difference between "loads of room" and "getting nervous".

Quoted above in the picture

- The queries 🌢️

This is where it gets spicy!
Here's the twist. It's not a clean sweep.

Fetching one trace? Postgres wins. Every. Single. Time.
Postgres did it in ~0.03 ms. ClickHouse took ~3 ms. That's Postgres being roughly 100Γ— faster, and it stayed fast even at 100M rows. Grabbing one row by its ID is a row store's home turf. ClickHouse just isn't built for that, and it shows.

Queires' metrics

Query latency as data grows. Lower is better. Mind the log scale.

But the dashboard query? Whole different movie.

At 1 million rows, both were fine (Postgres ~228 ms, ClickHouse ~15 ms).

At 100 million rows:

  • ClickHouse: 284 ms
  • Postgres: 49,085 ms

That's not a typo. Postgres took about 49 seconds for one dashboard query. Now imagine refreshing a dashboard that takes 49 seconds. 😬

The metrics query was even rougher. Postgres hit ~70 seconds at 100M. ClickHouse stayed around half a second.

- Memory 🧠

Both barely touched RAM. One query at a time, nobody needed much β€” Postgres sat around a couple hundred MB, ClickHouse under a gig. The 24 GB Mac Mini was honestly bored. (Throw heavy concurrent traffic at it and this changes. But that's a different test for another day!.)

The whole fight in one table

At 100 million rows, on one Mac Mini:

ClickHouse Postgres
Ingest speed ~78K rows/s ~14K rows/s
Disk used 48 GB 116 GB
Fetch one trace (p95) ~3 ms πŸ˜” ~0.03 ms βœ…
Dashboard query (p95) 284 ms βœ… ~49 sec 😡
Metrics query (p95) ~0.5 sec βœ… ~70 sec 😡
Peak RAM ~950 MB ~134 MB

Real numbers

For all of us who love to see actual numbers


🎯 So what did I actually learn?

A few things stuck with me.

Nobody "won" Postgres crushes point lookups. ClickHouse crushes analytics. This was never really ClickHouse vs Postgres. It's OLTP vs OLAP, and I got to watch it play out live.

This is exactly why Langfuse moved. An observability product lives on dashboards. Cost per model. Latency percentiles. Usage over time. Those are analytical queries over millions of rows. On Postgres at scale, that's a 49-second dashboard. On ClickHouse, it's a blink. The migration wasn't hype β€” for that use case, it's the whole ballgame.

A Mac Mini is a beast. 100 million rows, two databases, a few hours, zero cloud bill. It didn't even sweat on memory. If you're prototyping something data-heavy, don't sleep on the little box on your desk.

Compression is a quiet flex. 48 GB vs 116 GB for the same data adds up fast when you're the one paying for the disk.


πŸ™Œ Wrapping up

That was a fun one. One Mac Mini, 100 million LLM traces, and a very clear picture of who's good at what.

If you're storing LLM traces: keep Postgres for the transactional bits, and reach for a columnar DB like ClickHouse when the dashboards start to crawl. I'm still wondering if the idea is helpful to someone. I might open source the scripts if you appreciate this expriment.

If you liked this content you can follow me or on Twitter at kitarp29 for more!

Thanks for reading my article :)

Top comments (9)

Collapse
 
mads_hansen_27b33ebfee4c9 profile image
Mads Hansen

Nice experiment. The most useful part is that it separates β€œcan store 100M rows” from β€œcan answer the dashboard question fast enough.” Those are very different claims.

For LLM traces I would split the decision into at least three workloads: point lookup, ingest, dashboard aggregation. Postgres can remain perfectly fine for trace detail lookup and transactional metadata, while ClickHouse wins hard once the question becomes p95 latency, cost by model, or long-window aggregates.

The other interesting comparison would be Postgres with time partitioning, BRIN indexes, and pre-aggregated rollups. Not because it will beat ClickHouse for analytics, but because it shows where the migration line actually is.

Collapse
 
kitarp29 profile image
Pratik Singh

These are insightful metrics, next time I am comparing databases will definitely include these numbers. Thanks for the input and reading my article :)

Collapse
 
motedb profile image
mote

The honest answer to "should I move off Postgres" is almost always "not yet, but watch for the day you need columnar."IҀ™ve shipped two products on Postgres that were supposed to be analytics-y and ended up being mostly point lookups. Postgres handled both. The day I actually needed ClickHouse, I knew it within a week of running the first query that scinned 40M rows and took 11 seconds.

What I appreciate about your setup is that you kept the workload real Ҁ” actual LLM traces with the messy schema, not synthetic rows. Most "Postgres vs X" benchmarks cheat by writing perfectly uniform timestamps. Real trace data has gaps, retries, and weird tool-call nesting, and that's where columnar stores actually earn their storage cost.

The Mac Mini result is the part I'd push you to write a follow-up about. Most people assume the floor is a 32-core box. If a $1,500 machine with unified memory can scan 100M rows of traces in single-digit seconds, the cloud-database-everything pitch gets weaker. Did you measure p99 vs p50? That's where the columnar advantage usually shows up or disappears.

Collapse
 
kitarp29 profile image
Pratik Singh

Hi
Thanks for the adding up to the context of the article. Yes the decision of when to move is very important. I did measure p99 vs p50 but not for the same scenario. I intended to check the bottleneck of the system and was pushing the hardware limits of the Mac too. For Data aggregation I checked p50 since it tells a story that's not only about bottleneck but actual someplace Clickhouse wins by flying colors.

Collapse
 
valentynkit profile image
Valentyn Kit

That Postgres ingest curve, 32k down to 14k rows/sec, is b-tree index maintenance cost showing up, not a ceiling on the engine. Load via COPY with the indexes dropped, then build them after, and that curve mostly flattens.

Collapse
 
tsabrandon profile image
Brandon

Can you tell us whats the spec of your mac mini. Processor, ram and storage?

Collapse
 
kitarp29 profile image
Pratik Singh

I've this exact Mac mini link.amazon/B05BguWaA

Collapse
 
spiro_fea0497b367356f9e1d profile image
Spiro

Hey everyone,

I'm exploring an idea and want honest feedback before building anything.

The idea: a tool that connects to your GitHub repo (or a single file), and uses AI to automatically generate documentation - explaining what each function/module does, its parameters, and how to use it. The goal is to save the time devs spend writing docs manually.

A few questions:

Do you currently write docs for your code regularly, or is it something you skip/put off?
Would a tool like this actually save you time, or would you not trust AI-generated docs enough to use them as-is?
What tools (if any) do you already use for this?

Not selling anything, genuinely trying to figure out if this is worth building. Appreciate any thoughts, even harsh ones.

Collapse
 
kitarp29 profile image
Pratik Singh

I'm pretty sure Claude code can do it already!