Replica sets, failover and read preferences
Run a set with a real majority, understand what an election costs and what a write concern protects, and route reads to a member that can actually serve the answer.
A replica set
mongod --replSet rs0 --dbpath /data/rs0 --port 27017 --bind_ip localhost
mongod --replSet rs0 --dbpath /data/rs1 --port 27018 --bind_ip localhost
mongod --replSet rs0 --dbpath /data/rs2 --port 27019 --bind_ip localhost
mongosh --port 27017rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "127.0.0.1:27017", priority: 2 },
{ _id: 1, host: "127.0.0.1:27018" },
{ _id: 2, host: "127.0.0.1:27019" }
]
});
rs.status();
rs.conf();
db.hello();- One member is primary and accepts writes; the rest are secondaries that apply the oplog. A member with the highest priority tends to win an election, but only a majority can elect anyone.
- Use an odd number of members in production. An even number adds a member without adding fault tolerance, and can deadlock an election.
- An arbiter votes but stores no data. It restores a majority in an even-sized set, at the cost of having no data to fail over to.
Elections and write concern
| Setting | Effect on failover |
|---|---|
w: 1 | Acknowledged before replication; a failover can lose it |
w: "majority" | Replicated to a majority; survives an election |
w: "majority", j: true | Durable on a majority before acknowledgement |
priority | Higher-priority members are preferred as primary |
catchUpTakeoverDelayMillis | How long the best candidate waits before calling an election |
db.adminCommand({ replSetGetStatus: 1 })
.members.forEach(m => print(m.name, m.stateStr));
db.getSiblingDB("local").oplog.rs.stats().maxSize;
rs.printReplicationInfo(); // oldest oplog entry, and the window it covers
rs.stepDown(60); // deliberately hand over the primary role- A primary steps down when it cannot reach a majority of the set. This is why an even number of members is a bad idea: a single loss removes the majority.
- During an election the set accepts no writes for a few seconds. The driver retries what it can, and the application should treat that window as normal rather than exceptional.
- A secondary that has fallen behind is not an eligible candidate until it catches up, which is what prevents a stale member from being promoted.
- The oplog is a capped collection. Size its window to cover your longest maintenance window and your slowest consumer, then alert when the window shrinks.
Read preferences and consistency
| Preference | Use it for |
|---|---|
primary | Read-your-own-writes; the default and the safest choice |
primaryPreferred | Traffic that should keep working during a primary outage |
secondary | Deliberately isolating reporting load; fails if no secondary is available |
secondaryPreferred | Best-effort offload with a fallback to the primary |
nearest | Latency-driven routing, not freshness-driven routing |
db.orders.find({ status: "paid" }).readPref("secondaryPreferred");
db.orders.find({ region: "eu" }).readPref("nearest", [{ dc: "eu-west" }]);
await orders.insertOne(doc, { writeConcern: { w: "majority" } });
await orders.find({ _id: doc._id }).readConcern("majority");💡
Reading from a secondary is eventual consistency by another name. A request that writes and then reads the same document can observe the old value if it lands on a lagging member. Keep read-your-own-write paths on the primary and send analytics elsewhere.
FAQ
How many members should a replica set have?
Three in production, spread across failure domains. Two offers no majority after a single loss, and one is not a set at all. A fifth member tolerates a second simultaneous failure, at the cost of more write latency and more election traffic.
Does a replica set replace backups?
No. Replication copies corruption, a bad migration and an accidental delete to every member within milliseconds. A delayed member or a real backup, stored somewhere else and restore-tested, is still required.
Related
Sharding and horizontal scaling Backup, restore and operational tooling
Last refreshed 2026-09-18.