HomeResourcesCase study
Case study

Notion Was Copying 100% to Catch 1%

By The SDL team·4 min read·Updated Sep 16, 2026

The analytics pipeline didn't need a bigger warehouse. It needed to stop re-copying data that never changed.

Notion's analytics pipeline was choking, and the instinct was "buy a bigger warehouse." The actual problem was that they were re-copying 100% of their data to capture the 1% that changed.

Notion is a database of blocks — every paragraph, toggle, and checkbox is a row. Across 480 shards, the workload has a peculiar shape: on any given day, only about 1% of blocks get updated. But they get updated constantly. It's an update-heavy, not insert-heavy, world. And their off-the-shelf pipeline — Fivetran into Snowflake — was built on an assumption that quietly didn't hold.

Plain English

To analyse your data, you copy it from the live product database into a separate "warehouse" built for big queries. The simple way to keep the warehouse fresh is to periodically re-copy the tables. That works beautifully when data mostly gets added.

Notion's data mostly gets edited. So a re-copy meant hauling enormous tables across the network to capture a sliver of genuine change — like photocopying an entire book every night because someone fixed three typos. Worse, a Notion-specific query (walking the permission tree up through nested pages) was so heavy it timed out in the warehouse entirely.

Stop copying tables. Start shipping changes.

The fix is a technique called Change Data Capture (CDC). Instead of asking the database "what does this table look like now?", you tap into its write-ahead log — the running journal every database keeps of every change — and stream only the deltas: this row inserted, that row updated, this one deleted. You move 1% of the data because only 1% changed.

CDC pipeline: read the changelog, not the table 480 Postgres shards only ~1% of rows change, but heavily WAL Debezium CDC connector Kafka 1 topic / table Apache Hudi → S3 UPSERT, copy-on-write + Spark on top The old way (Fivetran → Snowflake): re-snapshot whole tables, even though 99% didn't change. Slow, expensive, and Snowflake timed out traversing the block permission tree. The CDC way: ship only the deltas. Hudi merges upserts efficiently. Ingestion went from >1 day to minutes–hours, saving $1M+ in 2022.
The architecture, and the contrast. Debezium reads each shard's changelog; Kafka carries the deltas; Apache Hudi efficiently merges upserts into an S3 data lake. Move the change, not the table.
Now the engineering

Notion built an S3 data lake fed by this pipeline: Debezium connectors read the Postgres write-ahead log (one connector per Postgres host), publishing to Kafka with one topic per table across all 480 shards. From Kafka, the changes land in Apache Hudi — a storage format designed for exactly this update-heavy case, using a COPY_ON_WRITE / UPSERT model to merge incoming changes into existing files without rewriting everything. Spark runs analytics on top.

Why Hudi and not plain files on S3? Data lakes are historically append-only — great for logs, terrible for updates. Hudi adds upsert and delete semantics to the lake, which is the precise capability Notion's 1%-churn workload demanded. The database's shape dictated the storage engine, not the other way around.

~1%
of blocks change daily
>1 day → mins
ingestion latency
$1M+
saved in 2022

Worth knowing

The dollar figure is the part executives hear, but the durable lesson is architectural: ingestion dropped from over a day to minutes-to-hours because the work scaled with change, not with size. When your pipeline's cost is proportional to total data volume instead of to how much actually changed, you don't have a capacity problem — you have a design mismatch.

The gap it reveals

Plenty of engineers can name CDC. Fewer can articulate when it's the right call: an update-heavy workload where re-snapshotting wastes effort proportional to the data you didn't touch. And fewer still connect that to needing a lake format like Hudi because vanilla data lakes can't handle updates. Recognising that the access pattern (1% churn) dictates the entire pipeline is the gap.

In the interview room

In a "design an analytics pipeline" round, the differentiator is asking one question: "is the source data insert-heavy or update-heavy?" If it's update-heavy, batch re-snapshots are the wrong tool, and CDC into an upsert-capable store is the move. Saying that unprompted tells the interviewer you reason from the workload, not from a menu of buzzwords.

The reframe

The seductive answer to a slow pipeline is always "more horsepower." Notion's win came from the opposite instinct: looking at the shape of their data and noticing the pipeline was doing 100x the necessary work. The breakthrough wasn't a faster warehouse. It was matching the architecture to the fact that almost nothing changes — but what changes, changes a lot.

Don't scale the work. Question whether the work needs doing at all.

Primary source →
notion.com — Building and Scaling Notion's Data Lake

Want feedback on your design?

The weekly teardown

One real-world architecture, every week.

How real companies actually built it: the design, the trade-offs, and what to say about it in an interview. Free, and one click to unsubscribe.

Related articles