A replica set is a group of mongod processes holding the same data. One is the primary and takes all writes; the rest are secondaries copying its operation log. A secondary hearing no heartbeat for electionTimeoutMillis (10 seconds by default) calls an election, and a candidate collecting a strict majority of configured voters becomes primary and increments the term — hence the odd voter count.

One machine is enough: start mongod --port 28110 --dbpath <dir> --replSet rsops, then run rs.initiate({_id: 'rsops', members: [{_id: 0, host: '127.0.0.1:28110'}]}). Since rs.status() returns past sixty lines, print the fields that matter.
const s = rs.status(), m = s.members[0], c = rs.conf().settings;
print(`set=${s.set} state=${m.stateStr} health=${m.health} term=${s.term}`);
print(`writeMajority=${s.writeMajorityCount} voters=${s.votingMembersCount}`);
print(`heartbeat=${s.heartbeatIntervalMillis}ms election=${c.electionTimeoutMillis}ms`);
print(`optime=${m.optime.ts.t}.${m.optime.ts.i} term=${m.optime.t} cfg=${m.configVersion}`);set=rsops state=PRIMARY health=1 term=2 writeMajority=1 voters=1 heartbeat=2000ms election=5000ms optime=1790050386.1 term=2 cfg=2
Term 2, not 1: a restart costs an election even when the node is alone. writeMajority is what w: "majority" is measured against — 2 in a three-member set, so one member may be down without blocking writes. The 5-second timeout and config version 2 came from rs.reconfig(), which also sets per-member options: priority: 0 bars a member from becoming primary, hidden: true hides it from drivers, and secondaryDelaySecs keeps one behind as an undo button.
db.getSiblingDB('local').oplog.rs.find({ ns: 'shop.orders' })
.sort({ $natural: -1 }).limit(3).toArray().reverse()
.forEach(e => print(e.ts.t + '.' + e.ts.i, e.op, JSON.stringify(e.o)));1790049038.1 i {"_id":"z1","sku":"kbd-01","qty":2}
1790049038.2 u {"$v":2,"diff":{"u":{"qty":3}}}
1790049038.3 d {"_id":"z1"}Every entry is idempotent: the update stored a $v: 2 diff carrying the new value, not $set: { qty: 3 }, so applying qty: 3 twice is harmless where replaying $inc: { qty: 1 } would not be. Oplog size is your recovery window — a secondary falling behind the span it holds must be resynced from scratch.