diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml index 0f09d82..a8daa07 100644 --- a/doc/src/sgml/backup.sgml +++ b/doc/src/sgml/backup.sgml @@ -1090,6 +1090,22 @@ SELECT pg_stop_backup(); + The contents of the pg_dynshmem/, pg_stat_tmp/, + pg_notify/, pg_serial/, + pg_snapshots/, and pg_subtrans/ directories can + be omitted from the backup as they will be initialized on postmaster + startup. If the is set and is + under the database cluster directory then the contents of the directory + specified by can also be omitted. + + + + Any file or directory beginning with pgsql_tmp can be + omitted from the backup. These files are removed on postmaster start and + the directories will be recreated as needed. + + + The backup label file includes the label string you gave to pg_start_backup, as well as the time at which pg_start_backup was run, and diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index da9b7a6..a441ae2 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -30,6 +30,7 @@ #include "replication/basebackup.h" #include "replication/walsender.h" #include "replication/walsender_private.h" +#include "storage/dsm_impl.h" #include "storage/fd.h" #include "storage/ipc.h" #include "utils/builtins.h" @@ -57,6 +58,8 @@ static bool sendFile(char *readfilename, char *tarfilename, static void sendFileWithContent(const char *filename, const char *content); static void _tarWriteHeader(const char *filename, const char *linktarget, struct stat * statbuf); +static int64 _tarWriteDir(char *pathbuf, int basepathlen, struct stat *statbuf, + bool sizeonly); static void send_int8_string(StringInfoData *buf, int64 intval); static void SendBackupHeader(List *tablespaces); static void base_backup_cleanup(int code, Datum arg); @@ -95,6 +98,67 @@ static int64 elapsed_min_unit; static int64 throttled_last; /* + * The contents of these directories are removed or recreated during server + * start so they will not be included in the backup. The directory entry + * will be included to preserve permissions. + */ +const char *excludeDirContents[] = +{ + /* + * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped even + * when stats_temp_directory is set because PGSS_TEXT_FILE is always created + * there. + */ + PG_STAT_TMP_DIR, + + /* Recreated on startup, see StartupReplicationSlots(). */ + "pg_replslot", + + /* Removed on startup, see dsm_cleanup_for_mmap(). */ + PG_DYNSHMEM_DIR, + + /* Recreated/zeroed on startup, see AsyncShmemInit(). */ + "pg_notify", + + /* Recreated on startup, see OldSerXidInit(). */ + "pg_serial", + + /* Removed on startup, see DeleteAllExportedSnapshotFiles(). */ + "pg_snapshots", + + /* Recreated/zeroed on startup, see StartupSUBTRANS(). */ + "pg_subtrans", + + /* Terminate list. */ + NULL +}; + +/* + * Files that should not be included in the backup. + */ +const char *excludeFile[] = +{ + /* Skip auto conf temporary file. */ + PG_AUTOCONF_FILENAME ".tmp", + + /* + * If there's a backup_label or tablespace_map file, it belongs to a backup + * started by the user with pg_start_backup(). It is *not* correct for this + * backup, our backup_label/tablespace_map is injected into the tar + * separately. + */ + BACKUP_LABEL_FILE, + TABLESPACE_MAP, + + /* Skip postmaster.pid and postmaster.opts. */ + "postmaster.pid", + "postmaster.opts", + + /* Terminate list. */ + NULL +}; + +/* * Called when ERROR or FATAL happens in perform_base_backup() after * we have started the backup - make sure we end it! */ @@ -889,6 +953,8 @@ sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces, char pathbuf[MAXPGPATH]; struct stat statbuf; int64 size = 0; + int excludeIdx; + bool excludeFound; dir = AllocateDir(path); while ((de = ReadDir(dir, path)) != NULL) @@ -903,22 +969,88 @@ sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces, strlen(PG_TEMP_FILE_PREFIX)) == 0) continue; - /* skip auto conf temporary file */ - if (strncmp(de->d_name, - PG_AUTOCONF_FILENAME ".tmp", - sizeof(PG_AUTOCONF_FILENAME) + 4) == 0) + /* Stat the file */ + snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name); + + if (lstat(pathbuf, &statbuf) != 0) + { + if (errno != ENOENT) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat file or directory \"%s\": %m", + pathbuf))); + + /* If the file went away while scanning, it's not an error. */ continue; + } /* - * If there's a backup_label or tablespace_map file, it belongs to a - * backup started by the user with pg_start_backup(). It is *not* - * correct for this backup, our backup_label/tablespace_map is - * injected into the tar separately. + * Scan for files that should be excluded. See excludeFile[] for info + * on exclusions. */ - if (strcmp(de->d_name, BACKUP_LABEL_FILE) == 0) - continue; + excludeFound = false; + excludeIdx = 0; + + while (excludeFile[excludeIdx] != NULL) + { + if (strcmp(pathbuf + 2, excludeFile[excludeIdx]) == 0) + { + ereport(DEBUG1, + (errmsg("file excluded from backup: %s", + excludeFile[excludeIdx]))); - if (strcmp(de->d_name, TABLESPACE_MAP) == 0) + excludeFound = true; + break; + } + + excludeIdx++; + } + + /* + * Scan for directories whose contents should be excluded. See + * excludeDirContents[] for info on exclusions. + */ + if (!excludeFound) + { + excludeIdx = 0; + + while (excludeDirContents[excludeIdx] != NULL) + { + if (strcmp(pathbuf + 2, excludeDirContents[excludeIdx]) == 0) + { + ereport(DEBUG1, + (errmsg("directory contents excluded from backup: %s", + excludeDirContents[excludeIdx]))); + + size += _tarWriteDir(pathbuf, basepathlen, &statbuf, + sizeonly); + + excludeFound = true; + break; + } + + excludeIdx++; + } + } + + /* + * Exclude contents of directory specified by statrelpath if not set + * to the default (pg_stat_tmp) which is caught in the loop above. + */ + if (!excludeFound && statrelpath != NULL && + strcmp(pathbuf, statrelpath) == 0) + { + ereport(DEBUG1, + (errmsg("stats_temp_directory contents excluded from backup: %s", + statrelpath))); + + size += _tarWriteDir(pathbuf, basepathlen, &statbuf, sizeonly); + + excludeFound = true; + } + + /* If file matched exclusion, continue. */ + if (excludeFound) continue; /* @@ -938,55 +1070,10 @@ sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces, "and should not be used. " "Try taking another online backup."))); - snprintf(pathbuf, MAXPGPATH, "%s/%s", path, de->d_name); - - /* Skip postmaster.pid and postmaster.opts in the data directory */ - if (strcmp(pathbuf, "./postmaster.pid") == 0 || - strcmp(pathbuf, "./postmaster.opts") == 0) - continue; - /* Skip pg_control here to back up it last */ if (strcmp(pathbuf, "./global/pg_control") == 0) continue; - if (lstat(pathbuf, &statbuf) != 0) - { - if (errno != ENOENT) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not stat file or directory \"%s\": %m", - pathbuf))); - - /* If the file went away while scanning, it's no error. */ - continue; - } - - /* - * Skip temporary statistics files. PG_STAT_TMP_DIR must be skipped - * even when stats_temp_directory is set because PGSS_TEXT_FILE is - * always created there. - */ - if ((statrelpath != NULL && strcmp(pathbuf, statrelpath) == 0) || - strncmp(de->d_name, PG_STAT_TMP_DIR, strlen(PG_STAT_TMP_DIR)) == 0) - { - if (!sizeonly) - _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf); - size += 512; - continue; - } - - /* - * Skip pg_replslot, not useful to copy. But include it as an empty - * directory anyway, so we get permissions right. - */ - if (strcmp(de->d_name, "pg_replslot") == 0) - { - if (!sizeonly) - _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf); - size += 512; /* Size of the header just added */ - continue; - } - /* * We can skip pg_xlog, the WAL segments need to be fetched from the * WAL archive anyway. But include it as an empty directory anyway, so @@ -994,18 +1081,8 @@ sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces, */ if (strcmp(pathbuf, "./pg_xlog") == 0) { - if (!sizeonly) - { - /* If pg_xlog is a symlink, write it as a directory anyway */ -#ifndef WIN32 - if (S_ISLNK(statbuf.st_mode)) -#else - if (pgwin32_is_junction(pathbuf)) -#endif - statbuf.st_mode = S_IFDIR | S_IRWXU; - _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf); - } - size += 512; /* Size of the header just added */ + /* If pg_xlog is a symlink, write it as a directory anyway */ + size += _tarWriteDir(pathbuf, basepathlen, &statbuf, sizeonly); /* * Also send archive_status directory (by hackishly reusing @@ -1248,6 +1325,31 @@ _tarWriteHeader(const char *filename, const char *linktarget, } /* + * Write tar header for a directory. If the entry in statbuf is a link then + * write it as a directory anyway. + */ +static int64 +_tarWriteDir(char *pathbuf, int basepathlen, struct stat *statbuf, + bool sizeonly) +{ + /* Include the directory entry to preserve permissions. */ + if (!sizeonly) + { + /* If symlink, write it as a directory anyway */ +#ifndef WIN32 + if (S_ISLNK(statbuf->st_mode)) +#else + if (pgwin32_is_junction(pathbuf)) +#endif + statbuf->st_mode = S_IFDIR | S_IRWXU; + + _tarWriteHeader(pathbuf + basepathlen + 1, NULL, statbuf); + } + + return 512; +} + +/* * Increment the network transfer counter by the given number of bytes, * and sleep if necessary to comply with the requested network transfer * rate.