Redis closes replica transfer files after full synchronization
Redis 7.2.3 fixes a missing close that kept deleted replica files consuming disk space. A separate change makes forgotten-node blacklist updates tolerate an existing entry.
Deleting a filename does not release its storage while a process still holds the underlying file open. Redis 7.2's full replica synchronization path could lose track of a descriptor without closing it, leaving deleted files consuming disk space. Repeated full synchronizations could therefore retain resources that ordinary directory inspection no longer explained.
The 7.2 release notes date Redis 7.2.3 to November 1 and assign high upgrade urgency. They list the replica file-descriptor correction and a separate possible crash after cluster-node removal. Both repairs are small in code size, but they address state that persists beyond the operation that originally created it.
I have used Redis since its early releases, and I value predictable replication behavior as much as its command interface. A replica that completes synchronization can still accumulate an operating-system resource leak. The protocol reaching its connected state is only one part of completing the operation.
Full synchronization prepares a local database file
The 7.2.3 replication source separates diskless loading from the path that receives a temporary database file. In the file-based path, Redis synchronizes that file, renames it to the configured RDB filename, synchronizes the directory, and loads the RDB data.
The code also handles an existing RDB file. It opens that earlier file before replacement and arranges an asynchronous close afterward. That is a different descriptor from the transfer descriptor that owns the newly received file. Closing one does not automatically close the other.
After successful loading, a replica configured to remove synchronization files when persistence is disabled can unlink the received RDB. The process then needs to release its transfer descriptor as part of cleanup. Otherwise, removing the path leaves the descriptor's reference alive.
The fix in pull request 12693 adds the missing close(server.repl_transfer_fd) immediately before setting that field to -1. Previously, resetting the field discarded the program's recorded handle without releasing the operating-system resource it identified.
Resetting a handle is not resource cleanup
Setting a descriptor variable to an invalid marker can prevent later code from attempting to use it. It does not ask the kernel to close the open file. That distinction explains how the cleanup path could appear complete in application state while retaining storage outside it.
The pull request description says the defect was introduced in 7.2 and occurs on replicas after full synchronization unless diskless loading is used. It also explains that deleted files could remain open until process restart. Those conditions identify the affected lifecycle more precisely than treating every Redis file as a possible source.
The leak involves the full synchronization path. The report does not say that each ordinary replicated command opens and loses another database file. Identifying the event that creates the resource and the event that should release it provides the relevant boundaries for understanding accumulation. A process can report a healthy replication connection while still holding references from earlier transfers, so connection health and resource ownership need different evidence.
Likewise, the problem should not be described as failure to issue the deletion request. The source contains the unlink path. The missing step is releasing the open reference after the file is no longer needed. Both the directory entry and descriptor ownership matter to the final storage outcome.
The connection state follows cleanup
The corrected close appears before the code creates the primary connection client and marks replication connected. That gives the resource-release step a clear place in successful synchronization. The file has served its role in loading the received state before the replica proceeds with its ordinary connection setup.
The earlier fsync() and directory synchronization steps serve a different purpose. They establish persistence of file contents and the rename. They do not release a descriptor. A successful durability operation cannot substitute for an ownership operation merely because both happen during cleanup.
The patch's placement also distinguishes the transfer handle from the temporary filename string. Redis frees that string and clears its pointer around the same point. Releasing heap memory that names a file and closing the open file itself are independent obligations.
I prefer this explanation because it accounts for the apparent contradiction an operator sees. The file can disappear from a directory while its storage remains in use. The corrected code closes the remaining reference before discarding its identity, so successful synchronization no longer leaves that particular resource behind.
Cluster removal has a different retained-state problem
Pull request 12702 fixes the separate crash listed in the release. Its cluster code previously added a forgotten node's identifier to the node blacklist with an insertion operation that expected a new entry. It asserted that the insertion succeeded.
The patch replaces that operation with dictAddOrFind(). The caller can obtain an existing entry as well as create a new one, then update the stored expiration. That avoids asserting merely because the entry it wants to maintain already exists.
The same change clears the node blacklist during cluster reset. The reset path removes that retained state alongside the rest of its reset work. This is distinct from descriptor ownership, but it also concerns assumptions about state that remains after topology operations.
The source does not justify recommending a cluster reset as a routine remedy. The command definition warns that resetting a node may execute FLUSHALL as a side effect. What matters here is how the repaired reset implementation treats its blacklist, not an instruction to perform that administrative operation casually.
Both fixes make lifecycle completion more precise. The replica path closes the descriptor before invalidating its field. The blacklist path tolerates an existing entry and clears retained entries when the node resets. Redis 7.2.3 addresses these specific failure mechanisms while preserving the ordinary replication and cluster operations that depend on them.