]> granicus.if.org Git - postgresql/commitdiff
Keep timeline history files restored from archive in pg_xlog.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Sun, 30 Dec 2012 12:26:47 +0000 (14:26 +0200)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Sun, 30 Dec 2012 12:29:45 +0000 (14:29 +0200)
The cascading standby patch in 9.2 changed the way WAL files are treated
when restored from the archive. Before, they were restored under a temporary
filename, and not kept in pg_xlog, but after the patch, they were copied
under pg_xlog. This is necessary for a cascading standby to find them, but
it also means that if the archive goes offline and a standby is restarted,
it can recover back to where it was using the files in pg_xlog. It also
means that if you take an offline backup from a standby server, it includes
all the required WAL files in pg_xlog.

However, the same change was not made to timeline history files, so if the
WAL segment containing the checkpoint record contains a timeline switch, you
will still get an error if you try to restart recovery without the archive,
or recover from an offline backup taken from the standby.

With this patch, timeline history files restored from archive are copied
into pg_xlog like WAL files are, so that pg_xlog contains all the files
required to recover. This is a corner-case pre-existing issue in 9.2, but
even more important in master where it's possible for a standby to follow a
timeline switch through streaming replication. To make that possible, the
timeline history files must be present in pg_xlog.

src/backend/access/transam/timeline.c
src/backend/access/transam/xlog.c
src/backend/access/transam/xlogarchive.c
src/include/access/xlog_internal.h

index 432cc1463c352093a9d462fb7f934191a698ef59..d7b7b7721439dcf537e6964237bb21adc0ce9220 100644 (file)
@@ -59,6 +59,7 @@ readTimeLineHistory(TimeLineID targetTLI)
        TimeLineHistoryEntry *entry;
        TimeLineID      lasttli = 0;
        XLogRecPtr      prevend;
+       bool            fromArchive = false;
 
        /* Timeline 1 does not have a history file, so no need to check */
        if (targetTLI == 1)
@@ -72,7 +73,8 @@ readTimeLineHistory(TimeLineID targetTLI)
        if (InArchiveRecovery)
        {
                TLHistoryFileName(histfname, targetTLI);
-               RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
+               fromArchive =
+                       RestoreArchivedFile(path, histfname, "RECOVERYHISTORY", 0, false);
        }
        else
                TLHistoryFilePath(path, targetTLI);
@@ -165,6 +167,13 @@ readTimeLineHistory(TimeLineID targetTLI)
 
        result = lcons(entry, result);
 
+       /*
+        * If the history file was fetched from archive, save it in pg_xlog for
+        * future reference.
+        */
+       if (fromArchive)
+               KeepFileRestoredFromArchive(path, histfname);
+
        return result;
 }
 
index 2998f60c5f91770fcc6b956579dac5d5f0acb413..aebdf0b85ab71dd2581e4513ab047b3d9a7d852f 100644 (file)
@@ -2626,68 +2626,12 @@ XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli,
         */
        if (source == XLOG_FROM_ARCHIVE)
        {
-               char            xlogfpath[MAXPGPATH];
-               bool            reload = false;
-               struct stat statbuf;
-
-               XLogFilePath(xlogfpath, tli, segno);
-               if (stat(xlogfpath, &statbuf) == 0)
-               {
-                       char oldpath[MAXPGPATH];
-#ifdef WIN32
-                       static unsigned int deletedcounter = 1;
-                       /*
-                        * On Windows, if another process (e.g a walsender process) holds
-                        * the file open in FILE_SHARE_DELETE mode, unlink will succeed,
-                        * but the file will still show up in directory listing until the
-                        * last handle is closed, and we cannot rename the new file in its
-                        * place until that. To avoid that problem, rename the old file to
-                        * a temporary name first. Use a counter to create a unique
-                        * filename, because the same file might be restored from the
-                        * archive multiple times, and a walsender could still be holding
-                        * onto an old deleted version of it.
-                        */
-                       snprintf(oldpath, MAXPGPATH, "%s.deleted%u",
-                                        xlogfpath, deletedcounter++);
-                       if (rename(xlogfpath, oldpath) != 0)
-                       {
-                               ereport(ERROR,
-                                               (errcode_for_file_access(),
-                                                errmsg("could not rename file \"%s\" to \"%s\": %m",
-                                                               xlogfpath, oldpath)));
-                       }
-#else
-                       strncpy(oldpath, xlogfpath, MAXPGPATH);
-#endif
-                       if (unlink(oldpath) != 0)
-                               ereport(FATAL,
-                                               (errcode_for_file_access(),
-                                                errmsg("could not remove file \"%s\": %m",
-                                                               xlogfpath)));
-                       reload = true;
-               }
-
-               if (rename(path, xlogfpath) < 0)
-                       ereport(ERROR,
-                                       (errcode_for_file_access(),
-                                        errmsg("could not rename file \"%s\" to \"%s\": %m",
-                                                       path, xlogfpath)));
+               KeepFileRestoredFromArchive(path, xlogfname);
 
                /*
                 * Set path to point at the new file in pg_xlog.
                 */
-               strncpy(path, xlogfpath, MAXPGPATH);
-
-               /*
-                * If the existing segment was replaced, since walsenders might have
-                * it open, request them to reload a currently-open segment.
-                */
-               if (reload)
-                       WalSndRqstFileReload();
-
-               /* Signal walsender that new WAL has arrived */
-               if (AllowCascadeReplication())
-                       WalSndWakeup();
+               snprintf(path, MAXPGPATH, XLOGDIR "/%s", xlogfname);
        }
 
        fd = BasicOpenFile(path, O_RDONLY | PG_BINARY, 0);
index 0ef53e7cbf68f470fd6afdab5b757fca0f7a4fa7..95852a5c48f24a5ad02d9eaf8849a2c11de5c9ef 100644 (file)
@@ -24,6 +24,7 @@
 #include "access/xlog_internal.h"
 #include "miscadmin.h"
 #include "postmaster/startup.h"
+#include "replication/walsender.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/lwlock.h"
@@ -416,6 +417,80 @@ ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal)
 }
 
 
+/*
+ * A file was restored from the archive under a temporary filename (path),
+ * and now we want to keep it. Rename it under the permanent filename in
+ * in pg_xlog (xlogfname), replacing any existing file with the same name.
+ */
+void
+KeepFileRestoredFromArchive(char *path, char *xlogfname)
+{
+       char            xlogfpath[MAXPGPATH];
+       bool            reload = false;
+       struct stat statbuf;
+
+       snprintf(xlogfpath, MAXPGPATH, XLOGDIR "/%s", xlogfname);
+
+       if (stat(xlogfpath, &statbuf) == 0)
+       {
+               char oldpath[MAXPGPATH];
+#ifdef WIN32
+               static unsigned int deletedcounter = 1;
+               /*
+                * On Windows, if another process (e.g a walsender process) holds
+                * the file open in FILE_SHARE_DELETE mode, unlink will succeed,
+                * but the file will still show up in directory listing until the
+                * last handle is closed, and we cannot rename the new file in its
+                * place until that. To avoid that problem, rename the old file to
+                * a temporary name first. Use a counter to create a unique
+                * filename, because the same file might be restored from the
+                * archive multiple times, and a walsender could still be holding
+                * onto an old deleted version of it.
+                */
+               snprintf(oldpath, MAXPGPATH, "%s.deleted%u",
+                                xlogfpath, deletedcounter++);
+               if (rename(xlogfpath, oldpath) != 0)
+               {
+                       ereport(ERROR,
+                                       (errcode_for_file_access(),
+                                        errmsg("could not rename file \"%s\" to \"%s\": %m",
+                                                       xlogfpath, oldpath)));
+               }
+#else
+               strncpy(oldpath, xlogfpath, MAXPGPATH);
+#endif
+               if (unlink(oldpath) != 0)
+                       ereport(FATAL,
+                                       (errcode_for_file_access(),
+                                        errmsg("could not remove file \"%s\": %m",
+                                                       xlogfpath)));
+               reload = true;
+       }
+
+       if (rename(path, xlogfpath) < 0)
+               ereport(ERROR,
+                               (errcode_for_file_access(),
+                                errmsg("could not rename file \"%s\" to \"%s\": %m",
+                                               path, xlogfpath)));
+
+       /*
+        * If the existing file was replaced, since walsenders might have it
+        * open, request them to reload a currently-open segment. This is only
+        * required for WAL segments, walsenders don't hold other files open, but
+        * there's no harm in doing this too often, and we don't know what kind
+        * of a file we're dealing with here.
+        */
+       if (reload)
+               WalSndRqstFileReload();
+
+       /*
+        * Signal walsender that new WAL has arrived. Again, this isn't necessary
+        * if we restored something other than a WAL segment, but it does no harm
+        * either.
+        */
+       WalSndWakeup();
+}
+
 /*
  * XLogArchiveNotify
  *
index 1443f96998f4cc51da1d5511f7b8f66c637bc87b..01898c63be26cf66081a9e14bf3e52d3ffb3b927 100644 (file)
@@ -265,6 +265,7 @@ extern bool RestoreArchivedFile(char *path, const char *xlogfname,
                                        bool cleanupEnabled);
 extern void ExecuteRecoveryCommand(char *command, char *commandName,
                                           bool failOnerror);
+extern void KeepFileRestoredFromArchive(char  *path, char *xlogfname);
 extern void XLogArchiveNotify(const char *xlog);
 extern void XLogArchiveNotifySeg(XLogSegNo segno);
 extern bool XLogArchiveCheckDone(const char *xlog);