]> granicus.if.org Git - postgresql/commitdiff
Fix WAL file replacement during cascading replication on Windows.
authorHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 6 Sep 2012 01:10:15 +0000 (18:10 -0700)
committerHeikki Linnakangas <heikki.linnakangas@iki.fi>
Thu, 6 Sep 2012 01:52:12 +0000 (18:52 -0700)
When the startup process restores a WAL file from the archive, it deletes
any old file with the same name and renames the new file in its place. On
Windows, however, when a file is deleted, it still lingers as long as a
process holds a file handle open on it. With cascading replication, a
walsender process can hold the old file open, so the rename() in the startup
process would fail. To fix that, rename the old file to a temporary name, to
make the original file name available for reuse, before deleting the old
file.

src/backend/access/transam/xlog.c

index 787eda2a61ec793b947469b1303d476c27d30430..ff56c26ab4c0edd26bf0a494715efabfa86976b2 100644 (file)
@@ -2787,7 +2787,33 @@ XLogFileRead(XLogSegNo segno, int emode, TimeLineID tli,
                XLogFilePath(xlogfpath, tli, segno);
                if (stat(xlogfpath, &statbuf) == 0)
                {
-                       if (unlink(xlogfpath) != 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",