September 21, 2017·4 min read·engineeringvalkeyredisreplicationpersistence

Redis 4.0.2 repairs replication metadata and shutdown flushing

Resuming replication from an RDB file requires a coherent history and database context. Redis 4.0.2 corrects that bookkeeping and flushes process-local AOF buffers before shutdown.

A snapshot can contain the right values while carrying the wrong claim about their replication history. Redis 4.0.2 addresses that distinction in PSYNC2 restart handling. Replication identifiers and offsets are useful only when they describe the dataset the instance actually has, together with the context needed to interpret the next part of the stream.

The 4.0 release notes date 4.0.2 to 21 September and classify its urgency as high. The versioned notes identify several replication-metadata corrections, an AOF shutdown bug, and a race between slow-log values and asynchronous database flushing. These affect the relationship between stored data and the internal state used to manage it.

I value Redis's ability to resume useful work without rebuilding the entire dataset. That depends on metadata being as trustworthy as the values themselves. A partial-resynchronization optimization must preserve the conditions that make continuation valid, including after a restart or change of role.

Replication history must match the loaded dataset

The release notes describe a problematic case in which a primary starts from an RDB file generated by a replica. Previous replicas with the same recorded history can later connect to that primary. If it retains identifiers that no longer describe its actual sequence of writes, the history metadata can support an invalid continuation decision.

Other defects had the opposite effect. Redis sometimes performed a full synchronization when partial synchronization was possible. Both cases concern the interpretation of restart state, but their consequences differ. One accepts too much confidence in the stored history; the other discards a valid opportunity to continue.

The main metadata patch explains the intended invariants. It groups the replication identifier, offset, and currently selected database as information required for successful continuation. Saving only part of that information can leave a later instance unable to interpret the stream consistently.

The selected database is easy to overlook because it does not describe an individual value. It describes execution context in the replication stream. The patch restores that context on the cached upstream client when loading suitable replication information. A byte position alone does not communicate which database subsequent commands should affect.

The commit also limits loading replication information to an instance configured as a replica. Its explanation warns that a primary could otherwise start with identifiers belonging to another dataset history and retain them while receiving unrelated writes. Restoring a snapshot's values and adopting its replication identity are separate decisions.

The load condition also checks that the saved stream database is not -1. A source comment notes that older implementations may store that value. Rejecting incomplete context prevents the loader from treating the presence of an identifier alone as enough information to resume.

Snapshot callers need consistent save information

The same patch introduces a helper to populate RDB save information and updates callers in replication, scheduled saving, and shutdown. That centralization matters because snapshots can be produced through several paths. A rule applied only to one caller would leave another route capable of writing different replication metadata for equivalent state.

The release notes list additional refinements beyond this patch, including work on persisting replication information and creating a backlog after partial synchronization on a replica. Together these form a set of related corrections. The main patch gives the design rationale, while the release notes identify the broader restart-handling work shipped with it.

The practical outcome is a stricter relationship between replication bookkeeping and the dataset being resumed. A saved identifier should not become a general assertion that any instance loading the file can continue the same history indefinitely. The instance's role and restored stream context participate in that decision.

I find that relationship useful when evaluating recovery mechanisms. Avoiding a full synchronization saves work, but a conservative full synchronization is preferable to treating incompatible histories as identical. The release also corrects unnecessary full synchronizations where the required state can be established.

Shutdown must empty the process buffer before fsync

The AOF fix concerns another boundary between representations of state. The notes say SHUTDOWN could fail to write commands still held in the Redis process's AOF buffer. Calling shutdown while traffic continued could therefore leave some operations out of the persisted file.

The shutdown patch adds flushAppendOnlyFile(1) before the existing AOF fsync call. The order is the mechanism. Synchronizing the file descriptor cannot persist commands that Redis has not yet written out of its own memory buffer.

This distinction separates a process buffer from the operating system's file state. The old shutdown path already called fsync, but that call operated on the file's existing contents. The added flush moves the pending AOF data into the write path before the synchronization step.

The correction is relevant to an orderly shutdown, not only to abrupt crashes. A shutdown procedure can deliberately request persistence and still omit recent operations if it misses an earlier buffering stage. The patch addresses the missing stage without changing the fact that AOF persistence has multiple layers.

Slow-log entries need independent string objects

The release also fixes sharing between slow-log arguments and values stored in keys. Its clarifying patch comment explains that FLUSHALL ASYNC can release database objects from another thread. Sharing those objects with other Redis structures creates a race during reclamation.

The corrected slow-log path duplicates the relevant string objects. This gives the log's argument data a representation independent of the database values that asynchronous flushing can release. Recording a command for later inspection must not require those stored values to survive unchanged.

These fixes all concern internal state crossing a lifetime boundary. Restarted replication needs coherent history and stream context. Shutdown needs process-local AOF data written before file synchronization. Slow-log arguments need ownership independent of asynchronously reclaimed values. Redis 4.0.2 makes those boundaries explicit in the corresponding maintenance paths, with replication restart handling receiving several related corrections.