Commit 997b48ed authored by Heikki Linnakangas's avatar Heikki Linnakangas

Support multiple concurrent pg_basebackup backups.

With this patch, pg_basebackup doesn't write a backup_label file in the
data directory, so it doesn't interfere with a pg_start/stop_backup() based
backup anymore. backup_label is still included in the backup, but it is
injected directly into the tar stream.

Heikki Linnakangas, reviewed by Fujii Masao and Magnus Hagander.
parent 48c9de80
...@@ -814,12 +814,13 @@ SELECT pg_stop_backup(); ...@@ -814,12 +814,13 @@ SELECT pg_stop_backup();
<para> <para>
You can also use the <xref linkend="app-pgbasebackup"> tool to take You can also use the <xref linkend="app-pgbasebackup"> tool to take
the backup, instead of manually copying the files. This tool will take the backup, instead of manually copying the files. This tool will do
care of the <function>pg_start_backup()</>, copy and the equivalent of <function>pg_start_backup()</>, copy and
<function>pg_stop_backup()</> steps automatically, and transfers the <function>pg_stop_backup()</> steps automatically, and transfers the
backup over a regular <productname>PostgreSQL</productname> connection backup over a regular <productname>PostgreSQL</productname> connection
using the replication protocol, instead of requiring filesystem level using the replication protocol, instead of requiring filesystem level
access. access. pg_basebackup does not interfere with filesystem level backups
taken using <function>pg_start_backup()</>/<function>pg_stop_backup()</>.
</para> </para>
<para> <para>
......
...@@ -59,10 +59,9 @@ PostgreSQL documentation ...@@ -59,10 +59,9 @@ PostgreSQL documentation
</para> </para>
<para> <para>
Only one backup can be concurrently active in There can be multiple pg_basebackups running at the same time, but it is
<productname>PostgreSQL</productname>, meaning that only one instance of better from a performance point of view to take only one backup, and copy
<application>pg_basebackup</application> can run at the same time the the result.
against a single database cluster.
</para> </para>
</refsect1> </refsect1>
......
This diff is collapsed.
...@@ -42,8 +42,10 @@ typedef struct ...@@ -42,8 +42,10 @@ typedef struct
static int64 sendDir(char *path, int basepathlen, bool sizeonly); static int64 sendDir(char *path, int basepathlen, bool sizeonly);
static void sendFile(char *path, int basepathlen, struct stat * statbuf); static void sendFile(char *readfilename, char *tarfilename,
static void _tarWriteHeader(char *filename, char *linktarget, struct stat * statbuf);
static void sendFileWithContent(const char *filename, const char *content);
static void _tarWriteHeader(const char *filename, char *linktarget,
struct stat * statbuf); struct stat * statbuf);
static void send_int8_string(StringInfoData *buf, int64 intval); static void send_int8_string(StringInfoData *buf, int64 intval);
static void SendBackupHeader(List *tablespaces); static void SendBackupHeader(List *tablespaces);
...@@ -87,8 +89,9 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) ...@@ -87,8 +89,9 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
{ {
XLogRecPtr startptr; XLogRecPtr startptr;
XLogRecPtr endptr; XLogRecPtr endptr;
char *labelfile;
startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint); startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &labelfile);
PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
{ {
...@@ -144,6 +147,10 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) ...@@ -144,6 +147,10 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
pq_sendint(&buf, 0, 2); /* natts */ pq_sendint(&buf, 0, 2); /* natts */
pq_endmessage(&buf); pq_endmessage(&buf);
/* In the main tar, include the backup_label first. */
if (ti->path == NULL)
sendFileWithContent(BACKUP_LABEL_FILE, labelfile);
sendDir(ti->path == NULL ? "." : ti->path, sendDir(ti->path == NULL ? "." : ti->path,
ti->path == NULL ? 1 : strlen(ti->path), ti->path == NULL ? 1 : strlen(ti->path),
false); false);
...@@ -164,7 +171,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) ...@@ -164,7 +171,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
} }
PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
endptr = do_pg_stop_backup(); endptr = do_pg_stop_backup(labelfile);
if (opt->includewal) if (opt->includewal)
{ {
...@@ -299,8 +306,9 @@ parse_basebackup_options(List *options, basebackup_options *opt) ...@@ -299,8 +306,9 @@ parse_basebackup_options(List *options, basebackup_options *opt)
/* /*
* SendBaseBackup() - send a complete base backup. * SendBaseBackup() - send a complete base backup.
* *
* The function will take care of running pg_start_backup() and * The function will put the system into backup mode like pg_start_backup()
* pg_stop_backup() for the user. * does, so that the backup is consistent even though we read directly from
* the filesystem, bypassing the buffer cache.
*/ */
void void
SendBaseBackup(BaseBackupCmd *cmd) SendBaseBackup(BaseBackupCmd *cmd)
...@@ -423,7 +431,52 @@ SendBackupHeader(List *tablespaces) ...@@ -423,7 +431,52 @@ SendBackupHeader(List *tablespaces)
pq_puttextmessage('C', "SELECT"); pq_puttextmessage('C', "SELECT");
} }
/*
* Inject a file with given name and content in the output tar stream.
*/
static void
sendFileWithContent(const char *filename, const char *content)
{
struct stat statbuf;
int pad, len;
len = strlen(content);
/*
* Construct a stat struct for the backup_label file we're injecting
* in the tar.
*/
/* Windows doesn't have the concept of uid and gid */
#ifdef WIN32
statbuf.st_uid = 0;
statbuf.st_gid = 0;
#else
statbuf.st_uid = geteuid();
statbuf.st_gid = getegid();
#endif
statbuf.st_mtime = time(NULL);
statbuf.st_mode = S_IRUSR | S_IWUSR;
statbuf.st_size = len;
_tarWriteHeader(filename, NULL, &statbuf);
/* Send the contents as a CopyData message */
pq_putmessage('d', content, len);
/* Pad to 512 byte boundary, per tar format requirements */
pad = ((len + 511) & ~511) - len;
if (pad > 0)
{
char buf[512];
MemSet(buf, 0, pad);
pq_putmessage('d', buf, pad);
}
}
/*
* Include all files from the given directory in the output tar stream. If
* 'sizeonly' is true, we just calculate a total length and return ig, without
* actually sending anything.
*/
static int64 static int64
sendDir(char *path, int basepathlen, bool sizeonly) sendDir(char *path, int basepathlen, bool sizeonly)
{ {
...@@ -446,6 +499,14 @@ sendDir(char *path, int basepathlen, bool sizeonly) ...@@ -446,6 +499,14 @@ sendDir(char *path, int basepathlen, bool sizeonly)
strlen(PG_TEMP_FILE_PREFIX)) == 0) strlen(PG_TEMP_FILE_PREFIX)) == 0)
continue; continue;
/*
* If there's a backup_label file, it belongs to a backup started by
* the user with pg_start_backup(). It is *not* correct for this
* backup, our backup_label is injected into the tar separately.
*/
if (strcmp(de->d_name, BACKUP_LABEL_FILE) == 0)
continue;
/* /*
* Check if the postmaster has signaled us to exit, and abort * Check if the postmaster has signaled us to exit, and abort
* with an error in that case. The error handler further up * with an error in that case. The error handler further up
...@@ -532,7 +593,7 @@ sendDir(char *path, int basepathlen, bool sizeonly) ...@@ -532,7 +593,7 @@ sendDir(char *path, int basepathlen, bool sizeonly)
/* Add size, rounded up to 512byte block */ /* Add size, rounded up to 512byte block */
size += ((statbuf.st_size + 511) & ~511); size += ((statbuf.st_size + 511) & ~511);
if (!sizeonly) if (!sizeonly)
sendFile(pathbuf, basepathlen, &statbuf); sendFile(pathbuf, pathbuf + basepathlen + 1, &statbuf);
size += 512; /* Size of the header of the file */ size += 512; /* Size of the header of the file */
} }
else else
...@@ -590,7 +651,7 @@ _tarChecksum(char *header) ...@@ -590,7 +651,7 @@ _tarChecksum(char *header)
/* Given the member, write the TAR header & send the file */ /* Given the member, write the TAR header & send the file */
static void static void
sendFile(char *filename, int basepathlen, struct stat * statbuf) sendFile(char *readfilename, char *tarfilename, struct stat *statbuf)
{ {
FILE *fp; FILE *fp;
char buf[TAR_SEND_SIZE]; char buf[TAR_SEND_SIZE];
...@@ -598,11 +659,11 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf) ...@@ -598,11 +659,11 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
pgoff_t len = 0; pgoff_t len = 0;
size_t pad; size_t pad;
fp = AllocateFile(filename, "rb"); fp = AllocateFile(readfilename, "rb");
if (fp == NULL) if (fp == NULL)
ereport(ERROR, ereport(ERROR,
(errcode(errcode_for_file_access()), (errcode(errcode_for_file_access()),
errmsg("could not open file \"%s\": %m", filename))); errmsg("could not open file \"%s\": %m", readfilename)));
/* /*
* Some compilers will throw a warning knowing this test can never be true * Some compilers will throw a warning knowing this test can never be true
...@@ -611,9 +672,9 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf) ...@@ -611,9 +672,9 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
if (statbuf->st_size > MAX_TAR_MEMBER_FILELEN) if (statbuf->st_size > MAX_TAR_MEMBER_FILELEN)
ereport(ERROR, ereport(ERROR,
(errmsg("archive member \"%s\" too large for tar format", (errmsg("archive member \"%s\" too large for tar format",
filename))); tarfilename)));
_tarWriteHeader(filename + basepathlen + 1, NULL, statbuf); _tarWriteHeader(tarfilename, NULL, statbuf);
while ((cnt = fread(buf, 1, Min(sizeof(buf), statbuf->st_size - len), fp)) > 0) while ((cnt = fread(buf, 1, Min(sizeof(buf), statbuf->st_size - len), fp)) > 0)
{ {
...@@ -660,7 +721,7 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf) ...@@ -660,7 +721,7 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
static void static void
_tarWriteHeader(char *filename, char *linktarget, struct stat * statbuf) _tarWriteHeader(const char *filename, char *linktarget, struct stat * statbuf)
{ {
char h[512]; char h[512];
int lastSum = 0; int lastSum = 0;
......
...@@ -312,8 +312,15 @@ extern void HandleStartupProcInterrupts(void); ...@@ -312,8 +312,15 @@ extern void HandleStartupProcInterrupts(void);
extern void StartupProcessMain(void); extern void StartupProcessMain(void);
extern void WakeupRecovery(void); extern void WakeupRecovery(void);
extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast); /*
extern XLogRecPtr do_pg_stop_backup(void); * Starting/stopping a base backup
*/
extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile);
extern XLogRecPtr do_pg_stop_backup(char *labelfile);
extern void do_pg_abort_backup(void); extern void do_pg_abort_backup(void);
/* File path names (all relative to $PGDATA) */
#define BACKUP_LABEL_FILE "backup_label"
#define BACKUP_LABEL_OLD "backup_label.old"
#endif /* XLOG_H */ #endif /* XLOG_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment