Walk through what happens on both the primary and the replica during a Redis full synchronization, when a replica connects for the first time.
answer
- PSYNC ? -1 then +FULLRESYNC replid offset
- Fork child, RDB to disk or diskless to socket
- Writes buffered in replica output buffer meanwhile
- Replica flushes, loads, then applies backlog
- replica-serve-stale-data during the window
basics
~20 sThe replica sends PSYNC with no known history; the primary replies FULLRESYNC with its replication ID and offset, produces an RDB snapshot from a forked child (to disk or streamed directly), and buffers all writes made meanwhile in that replica's output buffer. The replica flushes its data, loads the RDB, then applies the buffered and ongoing stream.
solid answer
~60 s1. The replica opens a connection, handshakes (PING, auth, REPLCONF listening-port and capabilities) and sends `PSYNC ? -1`, meaning it knows no replication history. 2. The primary answers `+FULLRESYNC <replid> <offset>` and starts producing an RDB snapshot: it forks a child process, which serializes the dataset either to a file (`repl-diskless-sync no`) or straight into the replica's socket (diskless, the default since Redis 6). 3. While that runs, the primary keeps serving clients and accumulates every new write into the replica's client output buffer, governed by `client-output-buffer-limit replica 256mb 64mb 60`. Overflowing it kills the sync, and a retry loop is a classic symptom. 4. The replica receives the snapshot, flushes its own dataset and loads it, which is CPU-blocking; `replica-serve-stale-data` decides whether it answers reads with old data or errors during this window. 5. It then applies the buffered backlog and stays attached to the live stream, acknowledging its offset with `REPLCONF ACK` about once a second. The main costs are the fork with its copy-on-write memory on the primary, plus disk and network for the transfer.
code
text · 16 lines# on the primary
> INFO stats
sync_full:3
sync_partial_ok:41
sync_partial_err:0
> CONFIG GET repl-diskless-sync repl-diskless-sync-delay
1) "repl-diskless-sync" 2) "yes"
3) "repl-diskless-sync-delay" 4) "5"
> CONFIG GET client-output-buffer-limit
... "slave 268435456 67108864 60" ...
# on the replica, during the transfer
> INFO replication
role:slave
master_link_status:down
master_sync_in_progress:1go deeper
Recall the shape: replica asks to sync, primary sends a snapshot, replica loads it and then follows the live write stream.
Give the sequence with names: PSYNC, FULLRESYNC with replication ID and offset, fork plus RDB, buffered writes, flush and load, then streaming.
Add operational detail: diskless sync and load options, replica output-buffer limits causing sync loops, replica-serve-stale-data, and the INFO counters you would check.
Frame full sync as the expensive fallback path and reason about avoiding it at scale: backlog sizing, staggered replica bring-up, memory headroom for fork copy-on-write, and whether replicas should be built from backups instead.
## The handshake A replica drives the process. After connecting it sends PING to check the link, authenticates if needed, announces itself with `REPLCONF listening-port` and `REPLCONF capa eof capa psync2`, and then asks to synchronize with `PSYNC <replid> <offset>`. On a first connection it has no history, so it sends `PSYNC ? -1`. Every replication stream is identified by a pair: a replication ID (a random 40-character string identifying a history) and a byte offset into that history. Those two values are what make later partial resynchronization possible. ## The primary's side Seeing an unknown history, the primary answers `+FULLRESYNC <replid> <offset>`, telling the replica which history it is joining and at which byte position the snapshot corresponds to. It then needs a consistent point-in-time image of the dataset, which it gets by forking a child process; the child serializes the data in RDB format while the parent keeps serving commands. There are two transfer modes. With `repl-diskless-sync no` the child writes an RDB file to disk and the parent then streams the file, which is useful when several replicas sync at once because one file can serve them all. With diskless replication, the default from Redis 6, the child writes the serialized data directly into the replica sockets, avoiding disk I/O entirely, which matters on slow disks or when the working set is large relative to the disk. `repl-diskless-sync-delay` makes the primary wait a moment so that multiple replicas arriving together can share a single child. Crucially, the primary does not stop accepting writes. Everything that happens after the fork point is appended to the new replica's client output buffer so it can be replayed once the snapshot is loaded. That buffer is bounded by `client-output-buffer-limit replica 256mb 64mb 60`. If the write rate multiplied by the transfer time exceeds the limit, the primary closes the connection, the replica retries, and you get an endless full-sync loop that shows up as a climbing `sync_full` counter and repeated forks. Raising the limit or reducing transfer time is the fix. ## The replica's side The replica receives the snapshot, then flushes its existing dataset and loads the new one. Loading is a foreground, CPU-bound operation that blocks that instance; for large datasets it takes real time. `replica-serve-stale-data yes` (default) means the replica answers reads with whatever it has during link loss and sync, at the risk of serving stale or, on a first sync, empty results; setting it to `no` makes it reply `-MASTERDOWN` to most commands, trading availability for the guarantee of not lying. Redis 6 added `repl-diskless-load` (`disabled`, `on-empty-db`, `swapdb`) so a replica can parse the stream directly from the socket instead of writing a temporary RDB file first, with `swapdb` keeping the old dataset in memory as a fallback until the new one is fully loaded. Once loaded, the replica applies the buffered write stream and then continues consuming it live. From that point it sends `REPLCONF ACK <offset>` roughly every second, which is how the primary knows each replica's progress; the primary pings replicas every `repl-ping-replica-period` seconds (default 10) so link failures are detected. ## What it costs, and how to avoid it A full sync is the expensive path: a fork (with copy-on-write page churn that can spike the primary's memory usage on a write-heavy workload), a full serialization of the dataset, the network transfer, and a blocking load on the replica. That is why the rest of Redis replication is designed to avoid it, using partial resynchronization for reconnections. Observability lives in `INFO`: `rdb_bgsave_in_progress`, `sync_full`, `sync_partial_ok`, `sync_partial_err`, and on the replica `master_sync_in_progress` and `master_link_status`. A production instance whose `sync_full` counter keeps rising has a problem worth investigating, because each occurrence loads the primary and briefly degrades the replica.
- A replica keeps starting a full sync, failing near the end, and retrying. What is the most likely cause?Its replication output buffer on the primary is overflowing: writes accumulated during the transfer exceed client-output-buffer-limit for the replica class, so the primary closes the connection and the replica starts over. Confirm with the rising sync_full counter and the primary's log line about the client being closed for output-buffer limits. Fix it by raising that limit, shortening the transfer (diskless sync, faster network), or syncing during a quieter write period.
- What does replica-serve-stale-data control, and when would you set it to no?It decides what a replica does while its link to the primary is down or an initial sync is in progress: yes (the default) serves reads from whatever data it holds, no makes it reject most commands with -MASTERDOWN. Set it to no when serving arbitrarily stale data is worse than serving an error, for example for entitlement or balance lookups, and leave it at yes for caches where stale reads are acceptable and availability matters more.
saying these in an interview costs you the question
- Claiming the primary blocks or stops accepting writes during a full sync
- Thinking the RDB snapshot must always be written to disk first
- Forgetting that writes during the transfer are buffered and replayed
- Assuming the replica keeps its old keys instead of flushing before load
- Ignoring fork and copy-on-write cost on the primary when discussing overhead