Replication and Replica Sets

Keep data safe and available with replica sets: understand primaries, secondaries and elections, configure a three-member set, choose write concerns for durability and read preferences for scaling reads, and handle failover in your application.

Lesson 22 of 43Advanced18 min read

A single database server is a single point of failure. If its disk dies, data is lost; if it restarts for an update, the application goes down. MongoDB solves this with replication: a replica set is a group of servers, usually three, that hold copies of the same data.

One member is the primary, which receives all writes. The others are secondaries, which continuously copy the primary's changes from its oplog (operations log). If the primary becomes unavailable, the remaining members hold an election and promote a secondary to primary, usually within seconds. Applications using the official drivers reconnect to the new primary automatically, and retryable writes repeat an interrupted write once.

Replica sets also let you choose trade-offs. A write concern decides how many members must confirm a write before it's acknowledged, which controls durability. A read preference decides which members can serve reads. In this lesson you'll set up and inspect a replica set, see what happens during failover, and choose write concerns and read preferences sensibly. Every Atlas cluster is a replica set, and every production deployment should be one.

How a replica set works

  • The primary accepts writes and records each change in its oplog, a special capped collection.
  • Secondaries replay the oplog to stay in sync. The delay between the primary and a secondary is replication lag.
  • Members send heartbeats every two seconds. If the primary is unreachable for about 10 seconds, an election chooses a new one.
  • An election needs a majority of voting members. With three members, the set survives the loss of one; with five, it survives two. That's why replica sets have an odd number of voting members.
  • Hidden and delayed members hold copies for backups or analytics without serving application reads.

Setting up and inspecting

Terminal
mongod --replSet rs0 --port 27017 --dbpath /data/a
mongod --replSet rs0 --port 27018 --dbpath /data/b
mongod --replSet rs0 --port 27019 --dbpath /data/c
MongoDB Shell
rs.initiate({
  _id: 'rs0',
  members: [
    { _id: 0, host: 'db1.example.internal:27017' },
    { _id: 1, host: 'db2.example.internal:27018' },
    { _id: 2, host: 'db3.example.internal:27019' }
  ]
})
rs.status()            // members, their states and how far behind they are
rs.conf()              // the configuration
rs.stepDown()          // ask the primary to step down, for maintenance

Applications connect with all members and the set name: mongodb://db1:27017,db2:27018,db3:27019/?replicaSet=rs0. Atlas gives you an mongodb+srv:// string that does the same.

Write concern, read preference and read concern

SettingOptionsMeaning
Write concern w1, 'majority', a numberHow many members must confirm a write
Journal jtrueWait until the write is on disk
Read preferenceprimary, primaryPreferred, secondary, secondaryPreferred, nearestWhich members may serve reads
Read concernlocal, majority, snapshotWhether reads can see data that might be rolled back

The default write concern is majority: a write is acknowledged only when most members have it, so it survives a failover. Reading from secondaries spreads load but may return slightly stale data, so use it for reports and analytics, not for reading back something a user just saved.

MongoDB Operations Syntax

Replica set commands

rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'a:27017' }, { _id: 1, host: 'b:27017' }, { _id: 2, host: 'c:27017' }] })
rs.status().members.map(m => `${m.name} ${m.stateStr}`)
rs.add('d:27017')
rs.stepDown()
db.orders.insertOne(doc, { writeConcern: { w: 'majority', wtimeout: 5000 } })
db.orders.find().readPref('secondaryPreferred')
db.getMongo().setReadPref('nearest')
// mongodb://a:27017,b:27017,c:27017/?replicaSet=rs0&readPreference=secondaryPreferred

MongoDB Operations Summary

  • A replica set is a group of servers holding copies of the same data: one primary, several secondaries.
  • Secondaries replay the primary's oplog; elections pick a new primary automatically if it fails.
  • Use an odd number of voting members, usually three, so a majority can always be reached.
  • Write concern majority makes writes survive failover; read preferences can send reads to secondaries.
  • Drivers reconnect after failover and retry writes once; every production deployment should be a replica set.

MongoDB Operations Examples

Each example shows the code, the output it produces and what each part does.

Example 1

Inspecting a replica set

MongoDB Shell
rs.status().set
rs.status().members.map(m => `${m.name}  ${m.stateStr}  health ${m.health}`)
rs.conf().members.length
db.hello().isWritablePrimary
db.getSiblingDB('local').oplog.rs.find({ op: 'i' }, { op: 1, ns: 1, _id: 0 }).sort({ $natural: -1 }).limit(1)
Output
rs0
[
  'db1.example.internal:27017  PRIMARY  health 1',
  'db2.example.internal:27018  SECONDARY  health 1',
  'db3.example.internal:27019  SECONDARY  health 1'
]
3
true
[ { op: 'i', ns: 'shop.orders' } ]
Line-by-line explanation
  1. Line 1The replica set's name, which must match the replicaSet option in connection strings.
  2. Line 2One primary and two healthy secondaries.
  3. Line 4hello reports whether the member you're connected to accepts writes.
  4. Line 5The oplog records every change; here the latest insert went into shop.orders.
Example 2

Write concerns and read preferences

MongoDB Shell
db.orders.insertOne({ _id: 9001, total: 59.9 }, { writeConcern: { w: 'majority', wtimeout: 5000 } })
db.orders.insertOne({ _id: 9002, total: 12.5 }, { writeConcern: { w: 1 } })

db.orders.find({ _id: 9001 }).readPref('primary').toArray().length
db.reports.find().readPref('secondaryPreferred').limit(1).toArray().length
db.getMongo().getReadPrefMode()
Output
{ acknowledged: true, insertedId: 9001 }
{ acknowledged: true, insertedId: 9002 }
1
1
primary
Line-by-line explanation
  1. Line 1majority waits until most members have the write, so it survives a failover; wtimeout stops it waiting forever.
  2. Line 2w: 1 only waits for the primary: faster, but the write could be lost if the primary fails before replicating.
  3. Line 4Reading from the primary always sees the latest acknowledged writes.
  4. Line 5Reports can read from a secondary to spread the load, accepting slightly stale data.
  5. Line 6The default read preference is primary.
Example 3

What happens during failover

MongoDB Shell
// before maintenance
rs.status().members.filter(m => m.stateStr === 'PRIMARY').map(m => m.name)
rs.stepDown(60).ok
// about ten seconds later, after the election
rs.status().members.filter(m => m.stateStr === 'PRIMARY').map(m => m.name)
rs.status().members.find(m => m.name === 'db1.example.internal:27017').stateStr
Output
[ 'db1.example.internal:27017' ]
1
[ 'db2.example.internal:27018' ]
SECONDARY
Line-by-line explanation
  1. Line 2db1 is the current primary.
  2. Line 3stepDown asks it to become a secondary for 60 seconds, as you'd do before patching a server.
  3. Line 5An election has made db2 the primary; applications using drivers reconnect automatically.
  4. Line 6The old primary keeps running as a secondary and catches up from the oplog.

Common MongoDB Operations Mistakes

Running production on a single server

Don't
mongod --dbpath /data/db                       # one disk failure loses everything
Do
mongod --replSet rs0 --dbpath /data/db         # on three servers, then rs.initiate(...)

An even number of voting members

Don't
// two data centres with two members each: if the link between them fails, neither side has a majority
Do
// three voting members (or five), ideally spread over three locations

Reading your own writes from a secondary

Don't
db.profiles.updateOne({ _id: me }, { $set: { name: 'Ada' } })
db.profiles.find({ _id: me }).readPref('secondary')   // may still show the old name
Do
db.profiles.find({ _id: me })                         // read from the primary after writing

MongoDB Operations in Real Projects

Replica sets in production

Atlas clusters are three-member replica sets by default, spread across availability zones, and Atlas handles elections, patching and monitoring. Self-managed teams run replica sets with members in different racks or zones, monitor replication lag, and use hidden or delayed members for backups and analytics. Applications rely on the drivers' automatic failover and retryable writes, so routine maintenance causes no visible downtime.

MongoDB Operations FAQ

How many members does a MongoDB replica set need?

Three voting members is the usual minimum for production, because a majority, two of three, can still elect a primary when one member fails. A single-member replica set is fine for development and enables features such as transactions and change streams. Avoid an even number of voting members, since it adds cost without surviving more failures.

Can I write to a secondary in MongoDB?

No. Only the primary accepts writes; a write sent directly to a secondary fails with a "not primary" error. Drivers connected with a replica set connection string always send writes to the current primary and find the new one after an election, so applications don't have to track it themselves.

What is the difference between replication and sharding in MongoDB?

Replication keeps full copies of the same data on several servers for high availability and data safety. Sharding splits data across several replica sets so that each one holds only part of it, which increases total storage and write capacity. Every production deployment uses replication; only very large or write-heavy ones need sharding as well.

How big is the MongoDB oplog?

By default, the WiredTiger oplog uses 5% of free disk space, with a minimum of 990 MB and a maximum of 50 GB. What really matters is the oplog window: how many hours of changes it holds. A secondary that falls further behind than that window can't catch up and must be resynced, so check it with rs.printReplicationInfo().

What is an arbiter in a MongoDB replica set?

An arbiter is a member that votes in elections but holds no data, so it's cheap to run. It can turn two data-bearing members into an odd-numbered set, but it can't help acknowledge majority writes, so writes can stall if a data-bearing member fails. Three data-bearing members are the safer choice where possible.

MongoDB Operations Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Fill in the blankseasy

Complete the write concern so the write survives a failover.

db.orders.insertOne(order, { writeConcern: { w: '' } })
Multiple choiceeasy

Which member of a replica set accepts writes?

Multiple choicemedium

How do secondaries stay in sync with the primary?

Multiple choicemedium

Why use an odd number of voting members?

Multiple choicehard

What's the risk of reading from secondaries?

MongoDB Operations Quiz

5 questions to check what you learned.

  1. What is a replica set?

    • A backup file
    • A group of MongoDB servers holding copies of the same data
    • A sharded collection
    • A set of indexes
  2. What happens when the primary fails?

    • The database is read-only forever
    • All data is lost
    • The remaining members elect a new primary, usually within seconds
    • An admin must manually copy data
  3. Which write concern is the default in modern MongoDB?

    • w: 0
    • w: 1
    • w: 'all'
    • w: 'majority'
  4. What does rs.stepDown() do?

    • Asks the primary to step down so another member is elected
    • Deletes the replica set
    • Removes a secondary
    • Stops replication
  5. What are retryable writes?

    • Driver behaviour that automatically retries a write once after a failover or network error
    • Writes that never fail
    • Writes saved to a queue
    • Transactions