Sharding a database is easy if you can take it offline. Stripe cannot. Jimmy Morzaria's talk is about the machinery that lets a payments company redistribute petabytes of financial data across more than 2,000 database shards while applications keep reading and writing, and his central thesis is that the capability is worth far more than the problem that motivated it. Stripe built online data movement to scale horizontally, then discovered the same platform solves database version upgrades, tenancy migrations, and seasonal capacity planning. The engineering lesson is about foundations, not sharding.
Morzaria opens with an analogy worth keeping in mind throughout. Hartsfield-Jackson Atlanta International Airport is the busiest airport in the world by passenger count, handling more than 250,000 passenger arrivals and departures and 1,700 flights on an average day — a takeoff or landing every 60 seconds. Its nearly 50-year-old Concourse D was built for smaller planes and had grown cramped, so the airport undertook a $1.3 billion project to widen it from 60 feet to 99 feet. The constraint that makes the story interesting is that the airport and the airlines mandated the concourse stay operational, with only a limited number of gates out of service at any time, to reduce the revenue impact. The modules were built at a remote location on airport grounds while foundation and utility work proceeded at the concourse itself, then self-propelled modular transporters crab-walked 700-ton modules a mile across active runways and set them within inches of the existing structure. Build the new thing elsewhere, verify it, then swap it in during a window short enough that nobody notices. That is precisely the shape of the data movement protocol described below.
Morzaria is a Staff Software Engineer at Stripe working on database infrastructure, with ten years in cloud infrastructure including more than five years at AWS on Amazon Quantum Ledger Database and Amazon Managed Streaming for Kafka. The talk was recorded at QCon San Francisco 2025 and published by InfoQ, which dates the recording page April 30, 2026; it runs 43:50 including Q&A. These notes report what Morzaria presented; supplementary explanation is labeled as such.
What You Will Learn
- Why a payments company chose to build a database-as-a-service in-house on open-source MongoDB rather than buy a managed offering, and the specific criteria that decision rested on.
- How Stripe models sharded data as logical databases, collections, shard keys, and a chunk map, and where that map physically lives.
- The six steps of a zero-downtime chunk migration, and why each one exists.
- Why sorting rows by index attributes before bulk insert produced a 10x write throughput improvement when tuning engine parameters did not.
- How version gating fences a source shard so that a routing update can propagate eventually-consistently across hundreds of stateless proxies without ever serving a query from the wrong shard.
- Why replication runs bidirectionally during a migration, how that avoids being multi-master, and how the write-ahead log tagging prevents a replication loop.
- The non-obvious uses the same platform unlocks: n-way splits and merges, seasonal capacity, major-version upgrades that skip versions, and tenancy migrations in both directions.
- A concrete build-versus-buy checklist for infrastructure capabilities.
The Reliability Bar That Sets Everything Else
Every design choice in this talk is downstream of one number, and "reliability is non-negotiable" is the first of the three takeaways Morzaria closes with. Stripe is a financial technology company whose stated mission is to grow the GDP of the internet, and whose customers include Amazon, Google, Shopify, and OpenAI. It facilitated $1.4 trillion of payments in 2024, roughly 1.3% of global GDP, and targets 5.5 nines of reliability. Morzaria justifies that target commercially rather than technically: research he cites shows that 40% of customers whose payment is denied abandon that business entirely, and the damage extends past the lost transaction into lasting reputational harm.
Added context for readers unfamiliar with the notation: 5.5 nines means 99.9995% availability, which allows roughly 2.6 minutes of downtime per year. That budget is why "take a maintenance window" is not an available answer to any of the problems in this talk, and why the migration protocol is designed around a failure mode applications already tolerate rather than around avoiding failures entirely.
The scale underneath that target: more than 5 million database queries per second against petabytes of financial data spread over more than 2,000 database shards. Morzaria places databases alongside networking and compute as the tier 0 infrastructure on which the reliability figure rests, and frames the whole talk as answering how you serve that volume at that reliability, saying the answer lies in the evolution rather than in any single design.
Fifteen Years of Database Infrastructure
Stripe launched in 2011 with MongoDB as its online datastore. Morzaria is matter-of-fact about why: at the time MongoDB offered better developer productivity and automated failover than standard relational databases, which let a young company move quickly. Product applications connected directly to a handful of MongoDB shards.
By 2017 the fleet had grown to tens of shards holding many different use cases, some data sharded and much of it still unsharded. The operational reality was the problem. Spinning up shards, building indexes, replacing nodes, and resharding data were all handled by engineers running ad hoc scripts, which Morzaria describes as becoming a scaling and reliability bottleneck. The response was the database proxy service, placed between applications and the MongoDB shards. Connection pooling was the immediate motivation, but he is explicit that the team always intended the proxy to be more than that: a single point of interface at which to enforce reliability, scalability, admission control, and access control. That framing matters, because the proxy layer is what makes the later migration protocol possible at all.
2020 was the inflection. Online commerce grew exponentially, illustrated in the
talk with a chart from Stripe's annual shareholder letter showing an exponential
rise starting in 2019 and 2020 in new businesses signing up with .ai as their
top-level domain — a proxy for a similar increase Stripe saw across sectors. Query
volume and storage footprint rose correspondingly. The first response was
vertical scaling, pushing some shards to tens of terabytes, and Morzaria notes
this worked for a long time before the team confronted the physical limits of
scaling up. Adding shards became unavoidable.
That required three foundational investments, all still present in the architecture today:
| Component | Responsibility |
|---|---|
| Control plane | Provision and deprovision databases and shards, create and drop indexes, run maintenance ops |
| Routing metadata service | Map database partitions to physical shards, replacing a static map |
| Data movement platform | Move data between shards online, so horizontal scaling does not require downtime |
Alongside these sit the CDC systems, which transport MongoDB's write-ahead log to Kafka and eventually to S3, powering Stripe's offline and analytical systems. MongoDB calls its write-ahead log the oplog, and that naming matters for the migration design, because the CDC pipeline turns out to be the right place to read replication input from.
The shards themselves are deployed as MongoDB replica sets with a primary and several secondaries distributed across availability zones and regions. In 2023 Stripe put the whole stack to the test by performing online data movement at scale to prove its robustness.
Morzaria summarizes the resulting cultural shift as moving from running MongoDB shards "like a few pets that needed constant manual care to running them like a large fleet of herd that can be automated and scaled easily." The pets-versus- cattle framing is a familiar one in infrastructure, and the point is that the automation is what makes the fleet size irrelevant.
Why Build DocDB Instead of Buying
DocDB is the composition of the proxy layer, control plane, routing metadata service, data movement platform, CDC systems, and several components not covered in the talk, offered to Stripe's product engineering organization as a database-as-a-service. Morzaria anticipates the obvious objection — why build this over fifteen years instead of buying it — and says Stripe has explored off-the-shelf offerings at various points. Three reasons drove the in-house decision.
Security. For a financial platform, Morzaria says security "isn't just a feature, it's everything." Building DocDB let the team bake in authorization policy enforcement at the data layer itself rather than relying on every calling service to get it right.
Reliability and performance. This is the most interesting of the three, because it is an argument for removing capability. MongoDB has a large surface area of querying capabilities, and Morzaria notes that used incorrectly these lead to unintended performance and reliability problems. By exposing only a minimal, battle-tested set of functions to Stripe engineers, DocDB prevents those problems from arising. The same restriction is what enables true multi-tenancy with enforced quotas, so one tenant's database activity cannot unintentionally degrade another's. A concrete consequence surfaced in Q&A: Stripe does not use MongoDB's aggregation pipeline at all.
Scale. DocDB was designed around seamless horizontal scaling with sharding, so growth would not force a trade against reliability or performance.
The Logical Model a Product Engineer Sees
Two constructs are exposed to product engineers. A logical database is a
container housing one or more related collections. A collection holds
documents. When an engineer needs a datastore, they create both through an
internal database management console, which calls the DocDB control plane to
provision the logical constructs and the backing infrastructure. At creation time
the engineer also specifies a shard key. Morzaria's running example is a
document in the core_payments database, payment_intents collection, sharded
on merchant.
Underneath, data is sharded across many database servers by dividing the shard
key's keyspace into chunks, each holding a contiguous range. In his example
core_payments.payment_intents is spread over two shards:
| Key range | Shard |
|---|---|
| 1 – 50 | shard_a |
| 50 – 100 | shard_b |
This table is the chunk map, each row a chunk, and it lives in the routing metadata service. Everything in the migration protocol is ultimately an exercise in changing one row of this table safely.
Worth noting for readers who know MongoDB: this is not MongoDB's own sharding.
Morzaria confirmed in Q&A that Stripe does not use sharded Mongo — no mongos
routers and no config servers. The database proxy server and the routing metadata
service are both built in-house. What Stripe forks MongoDB for is a small set of
targeted patches, described below.
Architecture And Data Flow
graph TD
App[Product Applications] --> Proxy[Database Proxy Servers]
Proxy -->|fetch routes| RMS[Routing Metadata Service
chunk map]
Proxy -->|query + routing version| Shards[(MongoDB Replica Set Shards
primary + secondaries
across AZs and regions)]
CP[Control Plane] -->|provision, index, maintain| Shards
CP --> RMS
Shards -->|oplog| CDC[CDC Systems]
CDC --> Kafka[Kafka]
Kafka --> S3[S3 / analytics]
CDC --> Repl[Replication Service]
Repl -->|replay writes| Shards
Coord[Migration Coordinator] -->|check lag| Repl
Coord -->|bump version / fence| Shards
Coord -->|update route + version| RMS
Bulk[Bulk Import Service] -->|load from snapshot| ShardsReading the diagram in terms of the request path: an application query enters the proxy layer, the proxy consults the chunk map it has cached from the routing metadata service, and it forwards the query to the owning shard annotated with the routing metadata version it used. The migration machinery — coordinator, replication service, CDC — sits off to the side and only intervenes when a chunk is moving.
The Zero-Downtime Data Movement Platform
Before the mechanics, Morzaria states the design principles that constrained the solution — he groups them as three, pairing consistency with availability — and they are worth reading as requirements rather than aspirations.
Consistency. Data being migrated must remain consistent and complete across both source and target throughout the process.
Availability. Millions of businesses process payments through Stripe around the clock, so downtime was unacceptable. The specific goal is precise and practical: keep the critical phase of the migration shorter than the duration of a planned database primary failover, and within the retry budget of the calling applications. The insight is that applications already survive a primary failover, so if the migration's disruptive window is no worse than one, no new resilience work is needed in application code.
Performance. Migrating data must preserve the throughput and performance of the shards involved, otherwise product queries hitting those shards suffer. This principle explains two design choices later: reading the oplog from CDC rather than the shard, and comparing point-in-time snapshots rather than live data.
Granularity and adaptability. The platform must support migrating an arbitrary number of chunks from any number of sources to any number of targets, with no cap on in-flight migrations across the fleet and no cap on how many migrations a single shard can participate in. It also had to handle chunks of widely varying sizes at high throughput, given that several shards were already tens of terabytes.
The Six Steps
The protocol, in Morzaria's ordering, migrating chunks 50–75 and 75–100 off
shard_b onto two new shards shard_c and shard_d:
- Register the migration intent for the chunks in the routing metadata service.
- Bulk import the data onto the target shards from a point-in-time snapshot of the chunks.
- Replicate writes that occurred from the snapshot time onward to the targets, and wait for replication to catch up.
- Verify correctness exhaustively, confirming the data is complete and consistent.
- Switch traffic in the database proxy servers from source to target.
- Complete the migration in the routing metadata service, then deprovision the source.
Steps 2 through 4 are the Concourse D modules being assembled off to the side. Step 5 is the crab-walk across the runway.
Step 2: The 10x Bulk Import Breakthrough
This step "appeared simple at first" and was not. The team hit throughput limitations bulk-loading data into a MongoDB shard, and Morzaria says the obvious remedies — batching writes, adjusting MongoDB engine parameters for optimal bulk ingestion — had little success.
The breakthrough came from optimizing insertion order. The bulk import service is the component responsible for loading data onto the target shards. MongoDB's storage engine is based on a B-tree. By sorting the data on the most common index attributes in the collection and inserting in that sorted order, the team significantly increased the proximity of writes and boosted write throughput by 10x.
Added context for why this works: a B-tree index insert must locate and modify a leaf page. Random-order inserts scatter those modifications across the whole tree, so each insert is likely to touch a page not currently in memory, causing repeated page reads, dirty pages, and eviction churn. Sorted inserts concentrate successive writes on the same leaf page, which stays hot in cache and is flushed once. This is the same reason bulk loaders in most B-tree databases recommend presorted input, and it is a transferable technique well beyond MongoDB.
Q&A supplied the practical throughput figure. Asked how long a migration takes, Morzaria said it depends on the data size and the number of indexes on the collections involved, and offered what he explicitly called a hand-wavy average: roughly 1.5 to 2 terabytes of data per target shard per day for the bulk ingest phase. He was careful to add that this covers bulk ingest only — if the backfill takes a day, you then have a day's worth of writes to replicate, and how long that takes depends on the write throughput of the source shard.
Step 3: Replication, and Why It Runs Both Ways
The async replication service reads the oplog from the source shards through the CDC systems, not directly from the MongoDB shard. Morzaria gives two reasons. First, reading from CDC avoids consuming throughput on the source shard during the migration, which is the performance principle in action. Second, it avoids being constrained by the oplog's size on the shard. Added context on that second point: MongoDB's oplog is a fixed-size capped collection, so a slow consumer reading directly from a shard can fall off the end of the window and be forced to restart from a fresh snapshot; the CDC pipeline into Kafka and S3 has no such bound.
The replication service is designed to be resilient to target shard unavailability, to support starting, pausing, and resuming synchronization from any point in time, and to expose an RPC returning the current replication lag. That RPC is what the coordinator polls during the traffic switch.
Mutations replicate bidirectionally — source to target, and target back to source — for the duration of the migration only. Morzaria's stated reason is rollback: keeping the source current means traffic can be reverted to it instantly if anything goes wrong after the switch, which makes rollback trivial rather than a recovery project. A second questioner asked how this avoids accumulating duplicate copies across successive generations of sharding, and the answer is that the replication is torn down at the end of the migration; the cleanup is covered under step 6 below.
An audience member pushed back on this directly, reading bidirectional replication as multi-master writes and calling it hand-waving over a non-trivial problem. Morzaria's answer clarifies the design, and it is worth stating plainly because it is the difference between a sound design and a dangerous one: at any single point in time only one shard is the leader. The target, while not yet canonical and not taking active traffic, is still a follower. It is not concurrent multi-master writing; it is two replica sets kept in sync by an external service with a single writer at a time.
The loop-prevention mechanism is a custom patch to Stripe's MongoDB fork. Every write issued by the replication service is tagged, the patch appends that tag to the write-ahead log entry, and the replication service filters out tagged writes when replicating in the other direction. Without this, a write replicated source-to-target would be picked up from the target's oplog and replicated back, cycling indefinitely.
A third questioner asked about idempotency. Morzaria's answer is that the oplog itself provides it: every write applied from the write-ahead log is an idempotent write, so the entire log can be replayed and still arrive at the same end state. Added context: this is a deliberate property of MongoDB's oplog, which rewrites non-deterministic operations into deterministic ones before recording them, and it is what makes at-least-once replication delivery safe here.
Step 4: Verification
After replication syncs, the platform runs a comprehensive check that the data is complete and consistent by comparing point-in-time snapshots of source and target. Morzaria again flags that snapshots are used specifically to avoid consuming throughput available on the source shard.
Step 5: Version Gating, the Heart of the Design
The hard question is how to redirect reads and writes without ever serving a query from a shard that no longer owns the data. Conceptually three things must happen: stop traffic on the source briefly, update the routes, and have proxies redirect based on the new routes. The mechanism that makes this safe is version gating.
The idea has two halves. Database proxy servers annotate every request to a shard with a version number reflecting the routing metadata version they are currently using. The MongoDB shard, via the custom fork patch, checks the version on an incoming request against the version it knows about and serves only requests that pass. Morzaria phrases the check as the shard ensuring the received version "is newer than the version number that it knows of"; read literally that would reject steady-state traffic at the current version, so — this author's reconciliation, not the speaker's wording — the operative predicate is presumably "not older than". The distinction only matters at equality, and the shard's own version number is stored in a document in a special collection on the shard itself.
The coordinator drives the switch:
sequenceDiagram
participant C as Coordinator
participant R as Replication Service
participant S as Source Shard
participant M as Routing Metadata Service
participant P as Proxy Servers
participant T as Target Shard
C->>R: is replication caught up?
R-->>C: in sync
C->>S: bump version number (fence)
Note over S,P: proxies still on v1 -> stale version errors returned to client
C->>R: are post-fence writes replicated?
R-->>C: yes
C->>M: update chunk route, set version 2
P->>M: poll for new routes
M-->>P: chunk now on target, v2
P->>T: query annotated with v2
T-->>P: resultOnce the source is fenced, queries from applications routed there are rejected with a stale version error, which propagates back through the proxy to the client; an audience member characterised what the client sees as a 500, and Morzaria confirmed it. Once the routes update and proxies pick them up, requests flow to the target.
The critical numbers: the entire traffic switch protocol takes on the order of milliseconds up to a maximum of 2 seconds, and all failed reads and writes succeed on retries. This is where the availability design principle pays off — the window is comparable to a planned failover, and applications already retry through those.
Version gating also answers a question one audience member raised about consistency. Route propagation across Stripe's hundreds of stateless proxy routers is eventually consistent, so at any moment some proxies hold the old map and some the new. Morzaria's answer is that the fencing happens on a single leader — the primary of the MongoDB shard — so even if a stale proxy sends a request to the old shard, the shard rejects it, and stale proxies cannot serve any requests until they update their routing metadata version. Reading that as a general principle, which is this author's framing rather than the speaker's: correctness does not depend on all proxies agreeing at the same instant, it depends on one authoritative point refusing stale work, so eventual consistency in the control plane is made safe by a single serialization point in the data plane.
The same questioner then asked why the error propagates to the client rather than the proxy silently re-resolving and retrying. Morzaria confirmed that optimization exists: on receiving a stale-version response, the proxy will go to the routing metadata service, read the updated config, and resend the request. There is also retry logic in the SDK that sits on top of MongoDB and talks to the proxy servers. His stated reason for not making this universal is that the team "didn't want to do aggressive retries at multiple layers in the system," so retries are placed deliberately rather than everywhere. Added context on why that caution is warranted: retries at several layers multiply, so uncoordinated retry at client, SDK, and proxy can turn a two-second blip into a self-inflicted load spike.
Step 6: Completing the Migration and Reclaiming the Source
The chunk map is updated to its final state, with key ranges 50–75 and 75–100 now
pointing at shard_c and shard_d. Morzaria described the cleanup in Q&A rather
than in the main flow: once the migration is declared done, the team spins down
the bidirectional replication, deletes the databases on the source, and fully
deprovisions the source shard, so the data ends up in exactly one place rather
than accumulating copies across successive generations of sharding.
What the Capability Unlocks
Morzaria's second key takeaway is the one he clearly cares most about. Reduced to fundamentals, the platform does something generic: snapshot data at a point in time, backfill it elsewhere, replicate the writes that arrive during the backfill, verify nothing was dropped or corrupted, then switch reads and writes over. Once you have that primitive, several unrelated-looking problems become the same problem.
| Use case | How it works | Why it matters |
|---|---|---|
| Horizontal scaling | Split any shard n ways | n-fold throughput and storage |
| Consolidation | Merge n shards back into one | Reclaim capacity when the n shards are underutilized |
| Seasonal capacity | Split before peak, merge after | Stripe relies on this heavily for Black Friday and Cyber Monday |
| Major version upgrade | Migrate data from a shard on version X to a shard on version Y | Skips the usual constraint that you cannot skip major versions; used across 2,000+ shards |
| Tenancy migration | Move a tenant between multi-tenant and single-tenant infrastructure | Teams start multi-tenant and graduate to single-tenant when scale warrants; works both ways |
The version-upgrade case deserves emphasis. In-place upgrades typically only go to the immediate next major version by following the vendor's upgrade protocol, and skipping major versions is usually not permitted. Data movement sidesteps that entirely: you provision a shard on the version you want and move data to it. Morzaria says Stripe has upgraded its entire fleet of more than 2,000 shards this way, which substantially reduced upgrade effort. His reasoning about why that matters is candid — every new major version arrives with its own bugs and issues, and Stripe will not necessarily be able to satisfy its performance requirements on every version, so a fast rollback path is essential. Bidirectional replication provides it.
Trade-offs And Limitations
- This is a forked MongoDB. Version gating and the write-ahead-log tagging that prevents replication loops both require custom patches to Stripe's fork. Added context: maintaining a database fork means carrying patches forward through every upstream release and owning any interaction between your patches and upstream changes. Teams without a dedicated database infrastructure group should weigh this seriously.
- Capability is deliberately restricted. DocDB exposes a minimal set of functions and does not offer the aggregation pipeline. This is presented as a feature — it prevents misuse and enables enforced multi-tenant quotas — but it is a real constraint on product engineers, and it works because Stripe controls both the platform and its consumers.
- The switch is not truly zero-error, it is zero-downtime. During the fence, requests to the source are rejected and surface to the client as errors. The design depends on those calls succeeding on retry within the application's retry budget, so an application without sane retry behaviour would see user-visible failures — that inference is this author's, not a caveat Morzaria stated.
- Retry placement is a tuning problem, not a solved one. Morzaria says the team avoided aggressive retries at multiple layers and chose where retries happen; proxy-level re-resolution and SDK-level retry both exist but are applied selectively.
- Migration throughput is modest relative to shard size. At the bulk-ingest rate quoted above, against shards already at tens of terabytes, a large reshard is a multi-day operation plus replication catch-up.
- Bidirectional replication should not be read as a multi-master pattern. See step 3 — it does not demonstrate a general solution to concurrent multi-region writes.
- It rests on a large pre-existing platform. The migration protocol only works because the proxy layer, routing metadata service, control plane, and CDC pipeline already exist. Stripe built these across roughly a decade. The protocol is not something you can adopt in isolation.
- Build-in-house was justified by Stripe's specific position. Morzaria frames the decision around security, reliability, and scale requirements particular to a payments platform, not as general advice, and his closing takeaway offers criteria for both directions.
Practical Takeaways
- Put a proxy layer between applications and your database shards early, and treat it as the enforcement point for reliability, admission control, and access control rather than just connection pooling. Everything else in this architecture depends on it.
- Externalize routing into a service instead of a static map. A queryable chunk map is what makes a route change an operation rather than a deployment.
- Size your migration's disruptive window against something applications already survive — a planned primary failover, or the app's retry budget — rather than against zero. It converts an impossible requirement into a measurable one.
- Fence at a single authoritative point when your control plane is eventually consistent. One primary rejecting stale-version requests is far simpler and safer than trying to update hundreds of stateless routers atomically.
- Sort your data by common index attributes before bulk loading into a B-tree store. Stripe got 10x after batching and engine parameter tuning failed.
- Read replication input from your CDC pipeline, not the live database, when you need to protect source throughput or escape a capped oplog window.
- Keep replication running back to the source during a migration so rollback is a route flip rather than a restore, and tag replicated writes so they do not loop.
- Compare point-in-time snapshots for verification rather than reading live data, for the same throughput-protection reason.
- Decide deliberately where retries live. Retry at the client, the SDK, the proxy, and the coordinator will compound; pick the layers and leave the others passive.
- Look for the second, third, and fourth use of any hard-won primitive. Stripe built data movement for sharding and got version upgrades, seasonal capacity, and tenancy migration for free.
- Use the build-versus-buy criteria explicitly. This is Morzaria's third and final key takeaway, addressed to infrastructure engineers, managers, and executives facing unmet product engineering needs: build when the capability drives long-term strategic advantage, requires unique reliability, scalability, performance, security, or compliance controls, offers a better three-to-five-year return on investment, or de-risks vendor lock-in and regional footprint. Buy for undifferentiated capabilities so resources go to high-leverage work, provided the vendor meets your security standards, offers a viable cost structure, and aligns with your regional and cloud strategy.
Key Terms
- DocDB — Stripe's internal document database service, built on open-source MongoDB and offered as a database-as-a-service to product engineering. Not related to any similarly named commercial product.
- Logical database — The container construct a product engineer creates, housing one or more related collections.
- Collection — A grouping of documents within a logical database, the unit on which a shard key is defined.
- Shard key — The document attribute whose keyspace is partitioned to
distribute data across shards;
merchantin the talk's example. - Chunk — A contiguous range of the shard keyspace, the unit of migration.
- Chunk map — The table in the routing metadata service mapping key ranges to physical shards; each row is a chunk.
- Routing metadata service — The in-house service holding the chunk map and its version number, polled by proxy servers.
- Database proxy server — The in-house stateless layer between applications and shards that pools connections, enforces access and admission control, and annotates requests with the routing metadata version.
- Control plane — The service that provisions and deprovisions databases and shards, manages indexes, and runs maintenance operations.
- Oplog — MongoDB's name for its write-ahead log. Idempotent on replay, which is what makes the replication service's at-least-once delivery safe.
- CDC (change data capture) — The systems transporting the oplog to Kafka and S3, feeding both analytics and the migration replication service.
- Version gating — Stamping requests with a routing metadata version so a shard can reject any request carrying a stale one.
- Fencing — Bumping the version on the source shard so that it stops serving requests routed by the old chunk map.
- Stale version error — The response a shard returns to a request carrying an outdated routing version; triggers a route refresh or a client retry.
- Bidirectional replication — Replication running in both directions during a migration, with exactly one leader at any time.
- Coordinator — The component orchestrating the traffic switch: verifying replication lag, fencing the source, and publishing the new route.
- Replica set — A MongoDB shard's deployment unit, one primary plus several secondaries spread across availability zones and regions.
- 5.5 nines — 99.9995% availability, roughly 2.6 minutes of downtime per year.
The transferable idea here is not the migration protocol itself, most of which depends on infrastructure very few teams have. It is the reframing in the second takeaway: Stripe did not build a sharding tool, it built the ability to move data between two places while both stay online, and then found that database upgrades, tenancy changes, and peak capacity planning were all that same operation wearing different clothes. When a capability is expensive to build, the question worth asking before you start is which other problems it will quietly dissolve.
Reference: Jimmy Morzaria, Stripe's Docdb: How Zero-Downtime Data Movement Powers Trillion-Dollar Payment Processing, QCon San Francisco 2025, InfoQ recording page dated April 30, 2026.