diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c index 26b023e754..037da0aa3d 100644 --- a/src/backend/access/transam/xlogarchive.c +++ b/src/backend/access/transam/xlogarchive.c @@ -382,6 +382,7 @@ KeepFileRestoredFromArchive(const char *path, const char *xlogfname) { char xlogfpath[MAXPGPATH]; bool reload = false; + bool skip_archive = false; struct stat statbuf; snprintf(xlogfpath, MAXPGPATH, XLOGDIR "/%s", xlogfname); @@ -416,6 +417,56 @@ KeepFileRestoredFromArchive(const char *path, const char *xlogfname) /* same-size buffers, so this never truncates */ strlcpy(oldpath, xlogfpath, MAXPGPATH); #endif + /* + * On a standby with archive_mode=always, there's the case where the + * same file is archived more than once. If the archive_command rejects + * overwriting, WAL-archiving won't go further than the file forever. + * Avoid duplicate archiving attempts when the file is known to have + * been archived and the content doesn't change. + */ + if (XLogArchiveMode == ARCHIVE_MODE_ALWAYS && + XLogArchiveCheckDone(xlogfname)) + { + unsigned char srcbuf[XLOG_BLCKSZ]; + unsigned char dstbuf[XLOG_BLCKSZ]; + int fd1 = BasicOpenFile(path, O_RDONLY | PG_BINARY); + int fd2 = BasicOpenFile(oldpath, O_RDONLY | PG_BINARY); + uint32 i; + uint32 off = 0; + + /* + * Compare the two files' contents. We don't bother completing if + * something's wrong meanwhile. + */ + for (i = 0 ; i < wal_segment_size / XLOG_BLCKSZ ; i++) + { + if (pg_pread(fd1, srcbuf, XLOG_BLCKSZ, (off_t) off) + != XLOG_BLCKSZ) + break; + + if (pg_pread(fd2, dstbuf, XLOG_BLCKSZ, (off_t) off) + != XLOG_BLCKSZ) + break; + + if (memcmp(srcbuf, dstbuf, XLOG_BLCKSZ) != 0) + break; + + off += XLOG_BLCKSZ; + } + + close(fd1); + close(fd2); + + if (i == wal_segment_size / XLOG_BLCKSZ) + { + skip_archive = true; + + ereport(LOG, + (errmsg ("log file \"%s\" have been already archived, skip archiving", + xlogfname))); + } + } + if (unlink(oldpath) != 0) ereport(FATAL, (errcode_for_file_access(), @@ -430,7 +481,7 @@ KeepFileRestoredFromArchive(const char *path, const char *xlogfname) * Create .done file forcibly to prevent the restored segment from being * archived again later. */ - if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS) + if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS || skip_archive) XLogArchiveForceDone(xlogfname); else XLogArchiveNotify(xlogfname);