Replication

MongoDB Intermediate/Advanced — Replication
MongoDB Intermediate/Advanced
Chapter 5 · Replication

🔁 Replication

Chapter 4's closing gotcha mentioned transactions requiring a replica set — a standalone mongod can't run them. This chapter explains what a replica set actually is, why it's the foundation transactions (and much of MongoDB's real-world resilience) depend on, and how to control where an application's reads actually come from.

What Is a Replica Set?

A replica set is a group of mongod instances holding copies of the same data. Exactly one member is the primary — the only one that accepts writes — while the rest are secondaries, continuously replicating the primary's changes so they stay (nearly) in sync. If the primary becomes unavailable, the replica set can elect a new primary from among the secondaries automatically, without a human intervening.

// Connecting to a replica set names every member — the driver figures out who's currently primary mongodb://host1:27017,host2:27017,host3:27017/mydb?replicaSet=rs0

The Oplog: How Replication Actually Works

The primary records every write operation into a special capped collection called the oplog (operations log). Each secondary continuously reads ("tails") the primary's oplog and re-applies the same operations against its own copy of the data, in the same order they happened on the primary. This is why secondaries are described as "nearly" in sync rather than instantly — there's always some small amount of replication lag between a write landing on the primary and that same write showing up on a given secondary.

Elections & Automatic Failover

When the current primary becomes unreachable — a crash, a network partition, planned maintenance — the remaining members hold an election to choose a new primary, without any write ever being manually redirected by a person. A candidate needs a majority of the replica set's voting members to agree before it becomes primary, which is exactly why replica sets are normally deployed with an odd number of voting members (3, 5, 7...) — an even number risks a tie that no majority can break.

// Checking replica set health and who's currently primary rs.status() // The current primary's own view of the set — includes each member's role and health rs.isMaster()

This is the mechanism Chapter 4's transactions actually rely on for durability: a transaction committed with a majority write concern isn't considered safely committed until a majority of replica set members have acknowledged it — meaning even if the primary immediately crashed afterward, an election would promote a secondary that already has that committed data.

Read Preference: Where Reads Actually Go

By default, every read goes to the primary — the same place every write goes, guaranteeing a read always sees the absolute latest data. Read preference lets an application redirect reads elsewhere instead, trading some staleness risk for benefits like spreading read load across more machines.

Read PreferenceBehavior
primary (default)Always reads from the primary; always up to date; no read scaling benefit
primaryPreferredReads from the primary normally, falls back to a secondary if the primary is unreachable
secondaryAlways reads from a secondary; never touches the primary; may be slightly stale
secondaryPreferredPrefers a secondary, falls back to the primary if none are available
nearestReads from whichever member has the lowest network latency, primary or secondary

Primary Reads vs. Secondary Reads

Reading From the Primary

Always current, since it's the same place every write lands. The right choice whenever a stale read would actually be wrong — a bank balance, an inventory count right before checkout.

Reading From a Secondary

Offloads read traffic away from the primary — useful for reporting/analytics queries that can tolerate being a few seconds behind, at the cost of that unavoidable replication lag.

Route to a Secondary When

The read is for analytics, reporting, or a dashboard that's acceptable to be a little behind real-time — offloading it keeps that load off the primary, which is busy serving every write.

Keep Reading From the Primary When

The application just wrote data and immediately needs to read it back (read-your-own-writes), or the read feeds a decision where staleness would cause a real, visible bug.

Setting Read Preference in the Node.js Driver

const reports = client.db("shop").collection("orders"); // This one query reads from a secondary; everything else on this client still defaults to primary const monthlyTotals = await reports .aggregate([ /* ...grouping pipeline... */ ], { readPreference: "secondaryPreferred" }) .toArray();

💻 Coding Challenges

Challenge 1: Explain the Odd-Number Rule

A team proposes a 4-member replica set for extra redundancy. Explain, in terms of how elections work, why an odd number of voting members (3 or 5) is the better choice.

Goal: Practice connecting the majority-vote election mechanism to a concrete deployment decision.

→ Solution

Challenge 2: Choose a Read Preference

For each, name the most appropriate read preference and justify it: (a) a nightly sales report query, (b) reading a user's profile immediately after they update it, (c) a customer-facing "check my order status" page.

Goal: Practice applying the primary-vs-secondary trade-off to realistic, differently-sensitive read scenarios.

→ Solution

Challenge 3: Trace a Failover

A 3-node replica set's primary crashes. Walk through, step by step, what happens next — up to and including the application's writes succeeding again.

Goal: Practice narrating the full election-and-failover sequence in the right order, including the brief window where writes aren't yet possible.

→ Solution

⚠️ Gotcha: Secondary Reads Are Eventually Consistent, Not Instantly Consistent

Reading from a secondary means accepting replication lag — under normal conditions it's milliseconds, but a slow network, heavy write load, or a struggling secondary can widen that gap noticeably. An application that writes data and then immediately reads it back from a secondary can genuinely fail to see its own write yet — a surprising, hard-to-reproduce bug if the read preference wasn't a deliberate choice. Default to primary unless there's a specific, understood reason (like this chapter's reporting example) to read from a secondary instead.

🎯 What's Next

The next chapter is Sharding — what happens when even a well-replicated dataset outgrows a single primary's capacity: shard keys, choosing a good one, chunks, the balancer, and when sharding is (and isn't) the right call.