The idea: a history with a name and a position
A Redis replica is a copy of the master that is kept up to date by a replication stream: every write command the master executes is also sent, as it is, to its replicas. To know whether a replica that reconnects can simply continue, the master names its history with a replication ID (master_replid, 40 random hex characters, shortened on the page) and counts the bytes of the stream with the replication offset (master_repl_offset). The pair (replid, offset) marks an exact point in the data set's history. The master also keeps the end of the stream in a replication backlog, a circular buffer, so a replica that missed a little can get just that part again.
The page runs one master and two replicas. Numbers are scaled so they fit:
| Thing | Real Redis | On the page |
|---|---|---|
| offset | bytes of the RESP stream | one step per command |
repl-backlog-size | 1mb | 8 commands, drawn as a ring of 8 slots |
| replid | 40 hex characters | the first 6, then … |
REPLCONF ACK | every second | after every Write (one second of clock) |
The handshake
A replica configured with replicaof <host> <port> (or the command REPLICAOF) opens a connection to the master and goes through a short handshake:
PING, to check that the master answers (+PONG), thenAUTHifmasterauthis set;REPLCONF listening-port 6379, so the master can list the replica inINFO replication, andREPLCONF capa eof capa psync2: the replica can read a diskless RDB (eof) and understands PSYNC2 (psync2);PSYNC <replid> <offset>: "I have history replid up to offset − 1; continue from offset." An empty server sendsPSYNC ? -1.
Full resync
When the master cannot continue that history it replies +FULLRESYNC <replid> <offset> and sends a snapshot instead (Demo: first sync (full resync)):
- With
repl-diskless-sync nothe master runs a BGSAVE: it forks, the child writesdump.rdb, and the file is sent when it is complete. - With
repl-diskless-sync yes(the default since Redis 7.0) the child writes the RDB straight into the replica's socket. The master first waitsrepl-diskless-sync-delay(5 seconds) so that other replicas that arrive can share the same transfer. - The snapshot holds the data at the moment of the fork. Writes that arrive meanwhile are executed by the master and also put in that replica's output buffer (limited by
client-output-buffer-limit replica 256mb 64mb 60). - The replica throws its old data away, loads the RDB and then receives the buffered writes followed by the live stream.
Partial resync and the backlog
The master answers +CONTINUE when two things hold: the replica's ID is the master's current replid (or its replid2, see below), and the requested offset is still inside the backlog. Then it sends only the missing part of the stream from the backlog, and the replica keeps all its data (Demo: short disconnect, partial resync).
The backlog is a ring: a new command overwrites the oldest one once the buffer is full. On the page, offset o lives in slot (o − 1) mod 8. If a replica is away while more than 8 commands are written, the offset it asks for has been overwritten and it needs a full resync again (Demo: backlog overflow forces full resync). In production, size repl-backlog-size as write rate × the longest disconnect you want to survive without a full copy; the backlog is freed after repl-backlog-ttl (3600 s) without any replica.
Replicas keep a backlog too, filled from the stream they receive. That is what lets a replica that becomes master serve partial resyncs.
The stream and REPLCONF ACK
Replication is asynchronous. The master executes a write, answers +OK and sends the command to the replicas without waiting for them. Each replica reports how far it got with REPLCONF ACK <offset> once per second; the master shows the difference as lag in INFO replication. The master also pings its replicas every repl-ping-replica-period (10 s), and either side drops the link after repl-timeout (60 s) of silence.
WAIT and min-replicas-to-write
WAIT numreplicas timeout blocks the client until at least numreplicas replicas have acknowledged all the client's previous writes, or until timeout milliseconds have passed. Instead of waiting up to a second for the next ack, the master sends REPLCONF GETACK * right away. WAIT returns the number of replicas that have the write. It does not undo anything: after a timeout the write is still there, on fewer copies (Demo: WAIT 1 for a replica ack).
min-replicas-to-write N together with min-replicas-max-lag S (10 s) makes the master refuse writes with -NOREPLICAS when fewer than N replicas are connected and acked within S seconds (Demo: min-replicas-to-write 1 refuses). It limits how long an isolated master keeps accepting writes that may later be lost, at the price of availability.
Failover and replid2 (PSYNC2)
When a replica is promoted with REPLICAOF NO ONE (by Sentinel, Cluster or an operator), it starts a new history: it picks a new replid. It does not forget the old one: it stores it as master_replid2 together with second_replid_offset = its offset + 1, the point where the two histories part. A replica of the old master that asks PSYNC <old id> <p> can continue partially if p ≤ second_replid_offset, because up to there both histories are the same. It receives +CONTINUE <new id> and moves the old ID to its own replid2 (Demo: failover keeps replid2).
Writes lost at failover
Because the master answers before the replicas have the write, a write can be acknowledged and still be lost: the master crashes after +OK but before the command left in the stream, and a replica without it is promoted (Demo: acked write lost at failover). When the old master comes back, it asks to continue from an offset beyond second_replid_offset; its history forked, so it gets a full resync, and the write disappears from it as well. WAIT reduces this window but does not close it: a failover can still promote a replica that did not ack.
See also how one Redis thread serves many connections, how Redis Sentinel fails over a master and Redis Cluster; the other Redis internals pages RDB and AOF persistence, the dict, encodings, sorted sets, expiry and eviction, streams and transactions; and, for the general picture, database replication, Kafka replication and copy-on-write fork (what BGSAVE relies on).
What the page leaves out
Offsets in bytes and real backlog sizes; AUTH/masterauth and TLS; the master's periodic PINGs and repl-timeout; replicas of replicas (sub-replicas share the master's stream and replid); repl-diskless-load on the replica; the rdb-channel replication of newer versions; client-output-buffer-limit killing a slow full sync; the SELECT commands and expiry handling inside the stream; replica-read-only and replica-serve-stale-data; and how Sentinel chooses the replica (priority, offset, run ID) and tells clients.