MongoDB replica sets and sharding, simulated
An animated MongoDB cluster in your browser: a replica set with its oplogs, elections in terms, write concern and the rollback of acknowledged w:1 writes, read concern and read preference with maxStalenessSeconds; a sharded cluster with mongos, config servers, ranges, hashed and ranged keys, zones, the balancer and jumbo ranges; a query-targeting checker and a shard-key evaluator.
Added
Works with
- MongoDB playground Databases and streamingrun the queries and indexes on one node there, then see here how a cluster replicates and routes them
- Raft and Paxos, simulated Distributed systemsMongoDB's elections are Raft-like — terms, one vote per term, the up-to-date check — shown in their general form there
- Partitioning and replication Distributed systemshash and range partitioning, quorums and rebalancing in general, beside MongoDB's own
- Elasticsearch, simulated Search and relevanceanother document store's primaries, replicas, routing and elections, animated the same way
- Distributed patterns on Redis Databases and streamingRedis Cluster's hash-slot sharding, replica failover and lost acknowledged writes, beside MongoDB's ranges and elections
How to use it
A MongoDB deployment in miniature that runs entirely in this page: a replica set — the members, each with its oplog, and a client driver — and a sharded cluster — the application, mongos, the config servers and the shards with the ranges of the shard key they own. Every write, oplog batch, heartbeat, vote, read, query and range migration is drawn as it happens, and the rules — when a write is acknowledged, who may become primary, what a read can see, where a query goes, when the balancer moves data — follow the MongoDB manual for 6.0 and later. Nothing here talks to a real MongoDB; for a console on a single (emulated) node, see the MongoDB playground.
How to use it
The Replica set and Sharded cluster tabs are simulators. Pick a scenario and press Start.
Each step does something — writes, reads, kills a member, cuts the network, inserts, queries — then
plays until it has happened and pauses so you can read what happened and why, with links to the
manual. Next step moves on, Back rewinds (the simulator is deterministic, so it replays the
earlier steps), ⏭ jumps to the end of the step and Step ⇥ advances to the next single event.
Space plays and pauses; → is the next step. Each tab has a playground with no script. Speed
scales everything; Hop time is how long a labelled message takes to cross a wire at 1× — while one
is on its way the simulated clock slows down — and Sim clock turns that off. ⛶ Full screen (or
F) gives the tool the whole window.
Replica set. The client (a driver) at the top and one card per member: its state (PRIMARY,
SECONDARY, ARBITER, ROLLBACK, DOWN) and term, its priority and flags (hidden, delayed, slow, stalled),
the election timer as a ring, and its oplog — one cell per entry, coloured by the term it was
written in, labelled with the key it set (· for a no-op). A faded cell has been fetched but not yet
applied; a dashed amber outline is not yet in the journal; a red outline differs from the primary’s
entry at that position; the black bar underneath is the majority commit point. Below the oplog:
applied and committed positions, the replication lag, and the member’s data as a local read and as
a majority read would see it. Heartbeats are drawn as small dots; writes, oplog batches, positions,
votes and reads as labelled packets. Tap a member to kill or restart it, cut it off, slow or stall
it, step it down or make it stand for election; tap the client to write and read. Do something
writes with any write concern (w, j, wtimeout) and reads with any read concern, read
preference and maxStalenessSeconds. Panels: Members (like rs.status()), Client requests
(every write and read with its result — ROLLED BACK and STALE are flagged) and Server
selection (the driver’s view of the members, each secondary’s estimated staleness and which are
eligible for the current read preference).
Sharded cluster. The application, mongos with the version of its cached routing table, the config
servers with the real version and the balancer’s state, and one card per shard: its documents, the
inserts it took in the last five seconds (🔥 when it is the only one taking any) and its ranges as
blocks (width by documents; amber outline = just inserted into; hatched = jumbo; dashed = moving).
Underneath, the whole routing table as a strip in key order, from MinKey to MaxKey. Do something
inserts one document or twenty, starts steady traffic, runs a find you type (or pick) and stops or
starts the balancer. Panels: Last query (targeted or scatter-gather, which shards, why, the explain
stage) and Routing table (config.chunks).
Query targeting. Type a filter as you would in mongosh — { customerId: 42, createdAt: { $gte:
ISODate("2026-03-01") } } — and a shard key — { customerId: 1, createdAt: 1 } or { _id: "hashed"
}. The tool splits 3 000 sample orders into ranges over the shards and shows whether mongos can
target the query, which ranges and shards it reads, the bounds it computes on the shard key, the
explain stage and the rule behind it.
Shard key evaluator. Pick or type a candidate key. It reports what analyzeShardKey reports in
7.0 and later — cardinality, the most common values and their share, monotonicity as the correlation
between key order and insert order — and what the key would do: how many ranges and jumbo ranges,
documents per shard, where the next 300 inserts land, which of a typical workload’s queries are
targeted, and a verdict, with a table comparing ten candidate keys.
Scenarios
Replica set:
-
Writes, the oplog and write concern — w:1 answered at once, secondaries pulling the oplog, w:"majority" and the commit point, a slow secondary and replication lag, w:3 with
wtimeoutand why a write-concern error does not undo the write. -
Failover: an election — heartbeats, the primary dies mid-write, a dry-run election then the real one in term 2, the driver’s retryable write, the old primary rejoining.
-
Rollback: acknowledged, then lost — the primary cut off with the client keeps acknowledging w:1 writes, the majority elects a new primary, the old one steps down and, when the network heals, rolls the acknowledged writes back; why w:"majority" prevents it.
-
Read concern and stale reads —
localon a stalled secondary,localandmajorityon the primary right after a w:1 write,linearizableand its no-op,snapshot. -
Read preference and maxStalenessSeconds — the 90-second minimum, a secondary falling minutes behind, the driver’s staleness estimate leaving it out,
nearest. -
Priorities and priority takeover — a priority-2 member fails, returns, catches up and takes the primary role back.
-
An arbiter: primary–secondary–arbiter — elections still work with a data member down, but w:"majority" cannot be acknowledged.
-
Hidden and delayed members — a member that applies the oplog 30 s late, and a mistake it still does not have.
-
Replica set playground.
Sharded cluster:
-
Routing through mongos — the components, an insert routed by its hashed key, a targeted query, a scatter-gather query, and why a hashed key cannot target a range.
-
An increasing shard key: one hot shard — every insert to the range ending at MaxKey; the balancer moves cold ranges but the inserts never spread.
-
The balancer and stale routing — a collection on its primary shard, a migration (clone, catch-up, commit, version bump), mongos answered with StaleConfig, refreshing and retrying.
-
A low-cardinality key: jumbo ranges — sharded on a three-valued field, each value one unsplittable range, marked jumbo and never moved.
-
Zones: data where it belongs — a zone per region pinned to a shard, inserts landing in their zone, a targeted query for one region.
-
Sharded cluster playground.
What it models
-
Replication. The primary applies a write, appends it to its oplog with an optime (term, position) and its wall-clock time, and journals it (at once with
j: true, otherwise at the next group commit, 100 ms). Each secondary keeps a getMore open on its sync source’s oplog (a tailable, awaiting cursor), applies the batches it receives and reports its applied and journaled positions back (replica set data synchronisation). The primary writes a no-op every 10 s when idle, asperiodicNoopIntervalSecsdoes, so that staleness can be measured. -
Write concern (manual).
w: 0is not acknowledged;w: 1when the primary has applied it (and journaled it withj: true);w: nwhen n data-bearing members have applied it;w: "majority"when a majority of voting data-bearing members have journaled it — the count is the smaller of a majority of all voting members and the number of voting data-bearing members, so an arbiter votes but never acknowledges.wtimeoutreturns an error but leaves the write in place. A primary that steps down answers its waiting writes with an error; retryable writes (on by default) retry once on the new primary. -
The commit point. The highest oplog entry of the primary’s own term that a write-majority has journaled; everything up to it is majority-committed and can never be rolled back. Members learn it from oplog batches and heartbeats.
-
Elections (protocol version 1; replica set elections). Heartbeats every 2 s. A secondary that is electable (data-bearing, priority above 0, not hidden or delayed) starts an election when it has not heard from a primary for
electionTimeoutMillis(10 s) plus a random offset of up to 15 %. It first runs a dry run (asking whether voters would vote for it in the next term, without incrementing its own), then increments its term, votes for itself and asks for votes. A voter refuses a candidate whose term is older than its own, one it when it has already voted for another candidate in that term, or one whose last applied optime is older than its own. A majority of votes makes it primary; it catches up from any member it can see that is further ahead, then writes a no-op in the new term and accepts writes. A primary steps down when it sees a higher term or cannot see a majority of voting members for an election timeout. A caught-up secondary with a higher priority than the primary calls a priority takeover election. -
Rollback (rollbacks during failover). A member whose oplog diverges from its sync source’s finds the last common entry, undoes everything after it (saving the documents to a rollback file) and fetches from there. Writes acknowledged with w:1 that the majority never had are counted as acked but lost. A crash loses what was applied but not yet journaled.
-
Read concern.
local(andavailable) reads the member’s latest applied data;majorityandsnapshotread the member’s majority-committed data;linearizableruns only on the primary, which writes a no-op and waits for a majority to acknowledge it before answering (read concern). -
Read preference and staleness. The driver checks every member it can see every 10 s (
heartbeatFrequencyMS; hidden members and arbiters are never in its list). Server selection follows the drivers' Server Selection specification:primary,primaryPreferred,secondary,secondaryPreferred,nearest; withmaxStalenessSecondseach secondary’s staleness is estimated as (S.lastUpdateTime − S.lastWriteDate) − (P.lastUpdateTime − P.lastWriteDate) + heartbeatFrequencyMS (or from the freshest secondary when there is no primary), and values below 90 s or below heartbeatFrequencyMS + idleWritePeriodMS are refused (Max Staleness specification). Among eligible members, those withinlocalThresholdMS(15 ms) of the fastest round trip are chosen from at random. -
Sharding (manual). Documents belong to ranges
[min, max)of the shard key, compared in BSON order (MinKey, numbers, strings, ObjectIds, dates, MaxKey); compound keys compare field by field; a hashed field is replaced by its hash. A new collection with a ranged key is one range on the primary shard; with a hashed key it is pre-split over every shard (two ranges each here). mongos routes with a cached copy of the routing table; a shard that sees an older shard version answers StaleConfig and mongos refreshes from the config servers and retries. Zones split ranges at their boundaries and keep them on their shards. -
The balancer (sharded cluster balancer), as in 6.1 and later: ranges are not split on insert; each round, a range in the wrong zone moves first, then if the fullest and emptiest shards differ by at least three times the range size, a range of about the range size is split off the fullest shard and moved to the emptiest — clone, catch-up, commit on the config servers (the collection’s major version goes up), then the donor deletes its copy in the background. A range above the range size whose documents all share one key value cannot be split, is marked jumbo and is never moved.
-
Query targeting (query routing). The filter’s bounds on the shard key: equality or
$inon a prefix narrows to points, the first range ends the narrowing (later fields are MinKey…MaxKey), a filter without the first field is sent to every shard, a hashed field is narrowed only by equality,$oris targeted clause by clause. Range operators bracket by type, as MongoDB’s do. The shards whose ranges overlap the bounds are asked; explain on mongos showsSINGLE_SHARDorSHARD_MERGE. -
The shard-key evaluator (choose a shard key, analyzeShardKey). Cardinality is the number of distinct key values; frequency the share of the most common ones; monotonicity the correlation between the order of documents by key and by insertion, called monotonic at |r| ≥ 0.7. The dataset is split into ranges of the chosen size (never between equal keys) over the shards, the next 300 generated orders are placed to see where inserts go, and five typical queries are checked for targeting.
Limits
-
Real timings (heartbeats, election timeout, the driver’s 10 s checks) but played faster; the network latency is fixed and small. A few members, three keys (
x,y,z) and a handful of values; documents are not stored in the replica set, only the oplog that sets them. -
Not modelled in the replica set: initial sync, chained replication (every secondary syncs from the primary),
votes: 0members, reconfiguration, catch-up takeover, read concernavailable’s difference on sharded clusters, `afterClusterTimeand causal consistency, transactions, the oplog’s size and window, flow control, and the WiredTiger cache pressure in a PSA set (described, not simulated).snapshotis shown as the member’s majority-committed state. -
In the sharded cluster each shard is one box, not a replica set; there is one mongos and one config server box. Range size is counted in documents (standing in for 128 MB). Not modelled: auto-merging,
moveChunkthrottling and_waitForDelete, orphaned documents, resharding,refineCollectionShardKey(described), sharded transactions, aggregation pipelines across shards, sorts (SHARD_MERGE_SORT) and updates or deletes without the shard key. -
The hash is a 32-bit stand-in for MongoDB’s 64-bit hash of the value (an MD5-based hash), so the ranges a hashed value falls into are not the ones a real cluster would use; the spread is the same.
-
The evaluator’s thresholds (when cardinality is "low", a value "hot", inserts "concentrated") are this tool’s judgment, not MongoDB’s.
Linking to an example
Links can be written by hand. Add a tab after #: replset (the default), sharded, query or
evaluator; a sharded-cluster example or a query or evaluator parameter opens its own tab. A link’s
settings replace this page’s saved settings (a button restores yours).
| Parameter | Meaning | Example |
|---|---|---|
|
A scenario from either list below, with its own settings; parameters given with it override them |
|
|
The replica-set scenario, without its settings |
|
|
Open the replica-set scenario at this step |
|
|
The sharded-cluster scenario (with |
|
|
Open the sharded-cluster scenario at this step |
|
|
Data-bearing members, 1–7 |
|
|
Add an arbiter ( |
|
|
Add a hidden member |
|
|
Add a delayed member with this |
|
|
Priorities of M1, M2, … |
|
|
Default write concern: |
|
|
|
|
|
Default |
|
|
Default read concern: |
|
|
Default read preference: |
|
|
Default |
|
|
|
|
|
One-way network latency between members, ms |
|
|
Shards in the sharded cluster, 2–6 |
|
|
Shard key: |
|
|
Hash the key’s last field |
|
|
A zone per region (uses |
|
|
Range size in documents |
|
|
Documents in the collection at the start |
|
|
Balancer on |
|
|
Query targeting: the filter |
|
|
Query targeting: the shard key |
|
|
Query targeting: shards |
|
|
Evaluator: the candidate key |
|
|
Evaluator: shards, range size, documents |
|
|
Random seed |
|
|
Playback speed: |
|
|
Hop time in seconds ( |
|
Replica-set scenarios:
example= |
Shows |
|---|---|
|
Writes, the oplog, w:1, w:"majority", replication lag, wtimeout (open) |
|
Failover: dry run, vote, new term, retryable write (open) |
|
Acknowledged w:1 writes rolled back after a partition (open) |
|
local, majority, linearizable and snapshot reads; stale reads from a secondary (open) |
|
maxStalenessSeconds: the 90 s minimum and a lagging secondary left out (open) |
|
Priorities and priority takeover (open) |
|
A PSA set: elections with a member down, w:"majority" blocked (open) |
|
Hidden and delayed members (open) |
|
A three-member replica set to experiment with (open) |
Sharded-cluster scenarios:
example= |
Shows |
|---|---|
|
mongos, config servers, shards; targeted and scatter-gather queries (open) |
|
An increasing key sends every insert to one shard (open) |
|
Range migration and mongos’s stale routing table (open) |
|
A three-valued key and its jumbo ranges (open) |
|
Zones by region (open) |
|
A sharded collection to experiment with (open) |
Examples: the rollback scenario with w:"majority", an increasing key, hashed, a five-member set reading from the nearest member, a range query on a hashed key, evaluating createdAt as a shard key.
Sources: the MongoDB manual — Replication, Replica Set Elections, Replica Set Oplog, Write Concern, Read Concern, Read Preference, maxStalenessSeconds, Rollbacks During Replica Set Failover, Replica Set Arbiter, Delayed Members, Retryable Writes, Sharding, Hashed Sharding, Zones, Sharded Cluster Balancer, Choose a Shard Key, analyzeShardKey and BSON comparison order; the MongoDB drivers' Server Selection and Max Staleness specifications; and the server’s replication internals README (protocol version 1, dry-run elections, catch-up, priority takeover).
Feedback is a gift. Something to improve, a feature to add, or a bug? Tell me.
Runs entirely in your browser; nothing you type leaves this page.