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 27017
rs.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

SettingEffect on failover
w: 1Acknowledged before replication; a failover can lose it
w: "majority"Replicated to a majority; survives an election
w: "majority", j: trueDurable on a majority before acknowledgement
priorityHigher-priority members are preferred as primary
catchUpTakeoverDelayMillisHow 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

PreferenceUse it for
primaryRead-your-own-writes; the default and the safest choice
primaryPreferredTraffic that should keep working during a primary outage
secondaryDeliberately isolating reporting load; fails if no secondary is available
secondaryPreferredBest-effort offload with a fallback to the primary
nearestLatency-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.

Sharding and horizontal scaling Backup, restore and operational tooling

Last refreshed 2026-09-18.