From b168c5ef2730d0ecaa7462f0b90345b0a3798c16 Mon Sep 17 00:00:00 2001 From: Magnus Hagander Date: Tue, 7 Jan 2014 17:04:40 +0100 Subject: [PATCH] Avoid including tablespaces inside PGDATA twice in base backups If a tablespace was crated inside PGDATA it was backed up both as part of the PGDATA backup and as the backup of the tablespace. Avoid this by skipping any directory inside PGDATA that contains one of the active tablespaces. Dimitri Fontaine and Magnus Hagander --- src/backend/replication/basebackup.c | 59 ++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 244e3b0ab3..5689e556e2 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -45,7 +45,7 @@ typedef struct } basebackup_options; -static int64 sendDir(char *path, int basepathlen, bool sizeonly); +static int64 sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces); static int64 sendTablespace(char *path, bool sizeonly); static bool sendFile(char *readfilename, char *tarfilename, struct stat * statbuf, bool missing_ok); @@ -72,6 +72,7 @@ typedef struct { char *oid; char *path; + char *rpath; /* relative path within PGDATA, or NULL */ int64 size; } tablespaceinfo; @@ -100,6 +101,9 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) XLogRecPtr endptr; TimeLineID endtli; char *labelfile; + int datadirpathlen; + + datadirpathlen = strlen(DataDir); backup_started_in_recovery = RecoveryInProgress(); @@ -119,6 +123,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) { char fullpath[MAXPGPATH]; char linkpath[MAXPGPATH]; + char *relpath = NULL; int rllen; /* Skip special stuff */ @@ -145,9 +150,20 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) } linkpath[rllen] = '\0'; + /* + * Relpath holds the relative path of the tablespace directory + * when it's located within PGDATA, or NULL if it's located + * elsewhere. + */ + if (rllen > datadirpathlen && + strncmp(linkpath, DataDir, datadirpathlen) == 0 && + IS_DIR_SEP(linkpath[datadirpathlen])) + relpath = linkpath + datadirpathlen + 1; + ti = palloc(sizeof(tablespaceinfo)); ti->oid = pstrdup(de->d_name); ti->path = pstrdup(linkpath); + ti->rpath = relpath ? pstrdup(relpath) : NULL; ti->size = opt->progress ? sendTablespace(fullpath, true) : -1; tablespaces = lappend(tablespaces, ti); #else @@ -165,7 +181,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) /* Add a node for the base directory at the end */ ti = palloc0(sizeof(tablespaceinfo)); - ti->size = opt->progress ? sendDir(".", 1, true) : -1; + ti->size = opt->progress ? sendDir(".", 1, true, tablespaces) : -1; tablespaces = lappend(tablespaces, ti); /* Send tablespace header */ @@ -191,7 +207,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) sendFileWithContent(BACKUP_LABEL_FILE, labelfile); /* ... then the bulk of the files ... */ - sendDir(".", 1, false); + sendDir(".", 1, false, tablespaces); /* ... and pg_control after everything else. */ if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0) @@ -744,6 +760,8 @@ sendFileWithContent(const char *filename, const char *content) * Include the tablespace directory pointed to by 'path' in the output tar * stream. If 'sizeonly' is true, we just calculate a total length and return * it, without actually sending anything. + * + * Only used to send auxiliary tablespaces, not PGDATA. */ static int64 sendTablespace(char *path, bool sizeonly) @@ -779,7 +797,7 @@ sendTablespace(char *path, bool sizeonly) size = 512; /* Size of the header just added */ /* Send all the files in the tablespace version directory */ - size += sendDir(pathbuf, strlen(path), sizeonly); + size += sendDir(pathbuf, strlen(path), sizeonly, NIL); return size; } @@ -788,9 +806,12 @@ sendTablespace(char *path, bool sizeonly) * Include all files from the given directory in the output tar stream. If * 'sizeonly' is true, we just calculate a total length and return it, without * actually sending anything. + * + * Omit any directory in the tablespaces list, to avoid backing up + * tablespaces twice when they were created inside PGDATA. */ static int64 -sendDir(char *path, int basepathlen, bool sizeonly) +sendDir(char *path, int basepathlen, bool sizeonly, List *tablespaces) { DIR *dir; struct dirent *de; @@ -931,6 +952,9 @@ sendDir(char *path, int basepathlen, bool sizeonly) } else if (S_ISDIR(statbuf.st_mode)) { + bool skip_this_dir = false; + ListCell *lc; + /* * Store a directory entry in the tar file so we can get the * permissions right. @@ -939,8 +963,29 @@ sendDir(char *path, int basepathlen, bool sizeonly) _tarWriteHeader(pathbuf + basepathlen + 1, NULL, &statbuf); size += 512; /* Size of the header just added */ - /* call ourselves recursively for a directory */ - size += sendDir(pathbuf, basepathlen, sizeonly); + /* + * Call ourselves recursively for a directory, unless it happens + * to be a separate tablespace located within PGDATA. + */ + foreach(lc, tablespaces) + { + tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc); + + /* + * ti->rpath is the tablespace relative path within PGDATA, or + * NULL if the tablespace has been properly located somewhere + * else. + * + * Skip past the leading "./" in pathbuf when comparing. + */ + if (ti->rpath && strcmp(ti->rpath, pathbuf + 2) == 0) + { + skip_this_dir = true; + break; + } + } + if (!skip_this_dir) + size += sendDir(pathbuf, basepathlen, sizeonly, tablespaces); } else if (S_ISREG(statbuf.st_mode)) { -- 2.40.0