postgres.git / summary / log / commit / refs

commit    1f95181b44c843729caaa688f74babe9403b5850
Author:   Michael Paquier <michael@paquier.xyz>
Date:     Tue Jul 05 01:16:12 2022 +0000

    Replace durable_rename_excl() by durable_rename(), take two
    
    durable_rename_excl() attempts to avoid overwriting any existing files
    by using link() and unlink(), and it falls back to rename() on some
    platforms (aka WIN32), which offers no such overwrite protection.  Most
    callers use durable_rename_excl() just in case there is an existing
    file, but in practice there shouldn't be one (see below for more
    details).
    
    Furthermore, failures during durable_rename_excl() can result in
    multiple hard links to the same file.  As per Nathan's tests, it is
    possible to end up with two links to the same file in pg_wal after a
    crash just before unlink() during WAL recycling.  Specifically, the test
    produced links to the same file for the current WAL file and the next
    one because the half-recycled WAL file was re-recycled upon restarting,
    leading to WAL corruption.
    
    This change replaces all the calls of durable_rename_excl() to
    durable_rename().  This removes the protection against accidentally
    overwriting an existing file, but some platforms are already living
    without it and ordinarily there shouldn't be one.  The function itself
    is left around in case any extensions are using it.  It will be removed
    on HEAD via a follow-up commit.
    
    Here is a summary of the existing callers of durable_rename_excl() (see
    second discussion link at the bottom), replaced by this commit.  First,
    basic_archive used it to avoid overwriting an archive concurrently
    created by another server, but as mentioned above, it will still
    overwrite files on some platforms.  Second, xlog.c uses it to recycle
    past WAL segments, where an overwrite should not happen (origin of the
    change at f0e37a8) because there are protections about the WAL segment
    to select when recycling an entry.  The third and last area is related
    to the write of timeline history files.  writeTimeLineHistory() will
    write a new timeline history file at the end of recovery on promotion,
    so there should be no such files for the same timeline.
    What remains is writeTimeLineHistoryFile(), that can be used in parallel
    by a WAL receiver and the startup process, and some digging of the
    buildfarm shows that EEXIST from a WAL receiver can happen with an error
    of "could not link file \"pg_wal/xlogtemp.NN\" to \"pg_wal/MM.history\",
    which would cause an automatic restart of the WAL receiver as it is
    promoted to FATAL, hence this should improve the stability of the WAL
    receiver as rename() would overwrite an existing TLI history file
    already fetched by the startup process at recovery.
    
    This is the second time this change is attempted, ccfbd92 being the
    first one, but this time no assertions are added for the case of a TLI
    history file written concurrently by the WAL receiver or the startup
    process because we can expect one to exist (some of the TAP tests are
    able to trigger with a proper timing).
    
    This commit has been originally applied on v16~ as of dac1ff30906b, and
    we have received more reports of this issue, where clusters can become
    corrupted at replay in older stable branches with multiple links
    pointing to the same physical WAL segment file.  This backpatch
    addresses the problem for the v13~v15 range.
    
    Author: Nathan Bossart
    Reviewed-by: Robert Haas, Kyotaro Horiguchi, Michael Paquier
    Discussion: https://postgr.es/m/20220407182954.GA1231544@nathanxps13
    Discussion: https://postgr.es/m/Ym6GZbqQdlalSKSG@paquier.xyz
    Discussion: https://postgr.es/m/CAJhEC04tBkYPF4q2uS_rCytauvNEVqdBAzasBEokfceFhF=KDQ@mail.gmail.com


src/backend/access/transam/timeline.c | 18 +++++------------- src/backend/access/transam/xlog.c | 9 +++------ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/backend/access/transam/timeline.c b/src/backend/access/transam/timeline.c index 8d0903c1756..eeb505fb6ad 100644 --- a/src/backend/access/transam/timeline.c +++ b/src/backend/access/transam/timeline.c @@ -441,12 +441,8 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, * Now move the completed history file into place with its final name. */ TLHistoryFilePath(path, newTLI); - - /* - * Perform the rename using link if available, paranoidly trying to avoid - * overwriting an existing file (there shouldn't be one). - */ - durable_rename_excl(tmppath, path, ERROR); + Assert(access(path, F_OK) != 0 && errno == ENOENT); + durable_rename(tmppath, path, ERROR); /* The history file can be archived immediately. */ if (XLogArchivingActive()) @@ -516,15 +512,11 @@ writeTimeLineHistoryFile(TimeLineID tli, char *content, int size) errmsg("could not close file \"%s\": %m", tmppath))); /* - * Now move the completed history file into place with its final name. + * Now move the completed history file into place with its final name, + * replacing any existing file with the same name. */ TLHistoryFilePath(path, tli); - - /* - * Perform the rename using link if available, paranoidly trying to avoid - * overwriting an existing file (there shouldn't be one). - */ - durable_rename_excl(tmppath, path, ERROR); + durable_rename(tmppath, path, ERROR); } /* diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index f1a795bba9f..a6e2cb88f34 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -3693,15 +3693,12 @@ InstallXLogFileSegment(XLogSegNo *segno, char *tmppath, } } - /* - * Perform the rename using link if available, paranoidly trying to avoid - * overwriting an existing file (there shouldn't be one). - */ - if (durable_rename_excl(tmppath, path, LOG) != 0) + Assert(access(path, F_OK) != 0 && errno == ENOENT); + if (durable_rename(tmppath, path, LOG) != 0) { if (use_lock) LWLockRelease(ControlFileLock); - /* durable_rename_excl already emitted log message */ + /* durable_rename already emitted log message */ return false; } [parent: bdb07d24113b]