Engineering Atlas › System Design Interviews

MongoDB for system design interviews

12 min read · last reviewed 2026-10-17

Open in the interactive app: practice, quiz, mock interview →

In one minute

How it works

The document model

A document holds what is read together: an order with its line items and address. One read fetches the aggregate, and a single-document write is atomic without a transaction. Unbounded arrays (every comment on a viral post) fight back: they grow towards 16 MiB, rewrite large pages on each update and bloat indexes, so anything that grows without bound gets its own collection.

Replica sets

A replica set has up to 50 members, at most 7 voting (MongoDB docs). The primary applies a write and records it in the oplog (local.oplog.rs); secondaries copy and replay it. Drivers discover the topology and send reads where the read preference allows (primary, secondary, nearest).

Sharded clusters

Stateless mongos routers consult a config server replica set that maps key ranges to shards. A query with the shard key hits one shard; without it, every shard. Hashed keys spread inserts but scatter range queries; ranged keys keep ranges local but funnel monotonic keys (timestamps, ObjectIds) onto one hot shard. Since 8.0 the topology is less fixed than it was: moveCollection relocates an unsharded collection to another shard, unshardCollection gathers a sharded one back onto a single shard, a config server can also hold data as a config shard, and resharding runs substantially faster, including resharding on the same key to redistribute data (MongoDB 8.0 release notes).

Down the stack

Layer 1The contract

A single-document write is atomic and, on the primary, linearizable. With w: "majority" (the implicit default for most deployments since 5.0), an acknowledged write is journaled on a majority of voting members and survives any failover the majority survives. The exception catches people out: with an arbiter, if data-bearing voters do not outnumber the voting majority (the common primary, secondary, arbiter layout), the default silently drops to w: 1 (MongoDB docs). With w: 1, only the primary had it, and if it dies first the write is rolled back when it rejoins. readConcern: "majority" never returns data that can be rolled back; local may. Causally consistent sessions add read-your-writes across members by carrying afterClusterTime, but only with majority reads and writes; with w: 1 and local reads, Jepsen saw acknowledged writes vanish and reads return values later rolled back (Jepsen). Multi-document transactions give snapshot isolation, not serializability, and set their own concerns: Jepsen found 4.2.6 transactions ignored collection-level concerns and fell back to local and w: 1, so set snapshot and majority explicitly on each transaction (Jepsen). Expect single-digit millisecond indexed reads within a zone and about 10 to 15 seconds without writes during an unplanned failover; the docs put the median election, including detection, at no more than about 12 seconds with default settings (MongoDB docs).

Layer 2The mechanism

WiredTiger. Each collection and index is a .wt file holding a B-tree. In cache, records carry update chains stamped with transaction timestamps, which gives MVCC snapshot reads without read locks; versions still needed by old readers move to the history store. On disk, pages are compressed (snappy for collections, zstd for time-series collections, prefix compression for indexes) and never overwritten: a reconciled page goes to a new block, and a checkpoint every 60 seconds publishes a consistent root (MongoDB docs). Between checkpoints the journal makes writes recoverable; it is flushed every 100 ms by default (storage.journal.commitIntervalMs), or at once for j: true and majority writes.

Oplog. A capped collection of idempotent operations keyed by optime (timestamp plus term); an $inc is logged as a $set of the result so replay is safe. Secondaries pull with a tailable cursor from a sync source (possibly another secondary), apply batches with parallel writer threads, and report applied and durable optimes with replSetUpdatePosition. The primary computes the majority commit point and gossips it in heartbeats.

Elections. Members heartbeat every two seconds. After electionTimeoutMillis (10 seconds) without the primary, a secondary bumps the term and sends replSetRequestVotes with its last optime. Voters grant one vote per term, only to candidates at least as up to date, Raft's rule. A deposed primary rolls back entries the new primary lacks into a rollback directory.

Balancer. Routers cache the routing table with a collection version; a shard holding a newer version returns a stale-config error and the router refreshes. Since 6.0 the balancer, running on the config server primary, balances by data size: it acts when shards differ by three times the range size (default 128 MB, so 384 MB) (MongoDB docs). A migration clones documents, streams concurrent changes, holds a short critical section that blocks writes to the range, commits ownership on the config servers, and deletes the donor copy later.

Layer 3The machine

The WiredTiger cache defaults to the larger of 50 percent of (RAM minus 1 GB) or 256 MB, so a 64 GB host gets 31.5 GB; the OS page cache holds compressed blocks with the rest. Eviction threads aim to keep the cache near 80 percent full and dirty data near 5 percent; past the hard triggers (roughly 95 percent full or 20 percent dirty), application threads must evict pages themselves and p99 jumps from milliseconds to hundreds of milliseconds. Checkpoints flush all dirty pages each minute, a periodic I/O burst. The journal is append-only with grouped fsyncs: one flush of a few hundred microseconds on NVMe covers every write in the window, so majority writes batch well. Each connection gets its own thread, commonly estimated at around 1 MB of memory, so 20,000 pooled connections hurt before any query runs.

Why it's built this way

  1. Joins across tables cost random I/O, so the aggregate lives in one document behind one B-tree lookup.
  2. Single-document writes need no distributed transaction, so data can be sharded by key with each write on one replica set.
  3. Random in-place updates are slow and torn writes corrupt pages, so WiredTiger writes new blocks, journals sequentially and checkpoints periodically.
  4. Machines fail, so shards replicate an ordered log, and safe failover needs majorities and terms so two primaries can never both commit.
  5. A majority costs a round trip, so latency versus durability is chosen per operation.

Diagram

sequenceDiagram
    participant C as App (driver)
    participant P as Primary
    participant S1 as Secondary 1
    participant S2 as Secondary 2
    C->>P: insert, w majority
    P->>P: apply + journal + oplog entry (term 7, ts 105)
    S1->>P: getMore on oplog (tailable cursor)
    P-->>S1: batch up to ts 105
    S1->>S1: apply + journal
    S1->>P: replSetUpdatePosition durable 105
    P->>P: commit point = 105 (2 of 3)
    P-->>C: ok
    S2->>P: getMore (lagging)

Worked example

An order history service holds 600 million orders of about 2 KB (1.2 TB plus about 300 GB of indexes), with 8,000 inserts and 40,000 reads per second at peak. Nearly every read is "orders for customer X, newest first".

Shard key {customerId: 1, createdAt: -1} makes each read a single-shard index range scan, and inserts spread because customers arrive at random. A hashed _id would spread writes too but scatter every customer read.

Six shards hold about 250 GB each. On 128 GB hosts the cache is about 63 GB, enough for all indexes and the recent orders that dominate reads. Each primary takes about 1,300 inserts per second.

Oplog: at roughly 2.2 KB per entry a shard logs about 2.9 MB per second, 10 GB per hour. With 1.5 TB free disk the default oplog (5 percent of free disk, at least 990 MB and capped at 50 GB) hits its cap: about five hours of window (MongoDB docs). A secondary down longer needs a full initial sync, so before an eight-hour maintenance raise oplogSizeMB or set oplogMinRetentionHours.

Orders use w: "majority" across three zones, adding about 1 to 2 ms to p50 inserts. History pages read from secondaries with maxStalenessSeconds: 90, the minimum allowed.

In the wild

Trade-offs

OptionStrengthsWeaknessesChoose when
w: 1Lowest write latencyAcknowledged writes can roll back on failoverLogs, metrics, rebuildable data
w: "majority"Survives failover of a minorityOne extra intra-cluster round tripDefault for business data
Hashed shard keyEven writes, no hot shard on monotonic keysRange queries scatter to all shardsPoint lookups by ID, high insert rate
Ranged compound shard keyTargeted range queriesHot spots if the leading field is skewed or monotonicQueries scoped by tenant or customer
Embed related dataOne read, atomic updateDocument growth, duplicationBounded, read-together data
Reference in another collectionUnbounded growth, independent updatesExtra query or $lookupComments, events, history

Pitfalls in production

Leadership lens

Check yourself

  1. A client receives "ok" for a w: 1 insert, the primary crashes, and after failover the document is missing. Where did it go?

    Show answer

    The write never reached a secondary, so the new primary lacked it. When the old primary rejoined it rolled the write back into its rollback directory.

  2. Why does a voter refuse a candidate with an older last optime?

    Show answer

    A committed write lives on a majority, and any winning majority overlaps it; refusing staler candidates guarantees the new primary holds every committed write.

  3. Why might p99 latency spike while CPU and disk look moderate?

    Show answer

    The cache crossed its eviction trigger, so application threads are evicting pages before serving their own operations.

  4. Why is {createdAt: 1} a poor shard key for an insert-heavy collection?

    Show answer

    Every new document has the largest key, so all inserts land on the shard owning the top range until the balancer moves it, creating a permanent hot shard.

Sources and further study

Primary

Systems docs

Books

Other curricula

Watch

Free resources

Open in the interactive app: practice, quiz, mock interview →