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();
<para>
You can also use the <xref linkend="app-pgbasebackup"> tool to take
the backup, instead of manually copying the files. This tool will take
care of the <function>pg_start_backup()</>, copy and
the backup, instead of manually copying the files. This tool will do
the equivalent of <function>pg_start_backup()</>, copy and
<function>pg_stop_backup()</> steps automatically, and transfers the
backup over a regular <productname>PostgreSQL</productname> connection
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>
......
......@@ -59,10 +59,9 @@ PostgreSQL documentation
</para>
<para>
Only one backup can be concurrently active in
<productname>PostgreSQL</productname>, meaning that only one instance of
<application>pg_basebackup</application> can run at the same time
against a single database cluster.
There can be multiple pg_basebackups running at the same time, but it is
better from a performance point of view to take only one backup, and copy
the the result.
</para>
</refsect1>
......
This diff is collapsed.
......@@ -42,8 +42,10 @@ typedef struct
static int64 sendDir(char *path, int basepathlen, bool sizeonly);
static void sendFile(char *path, int basepathlen, struct stat * statbuf);
static void _tarWriteHeader(char *filename, char *linktarget,
static void sendFile(char *readfilename, char *tarfilename,
struct stat * statbuf);
static void sendFileWithContent(const char *filename, const char *content);
static void _tarWriteHeader(const char *filename, char *linktarget,
struct stat * statbuf);
static void send_int8_string(StringInfoData *buf, int64 intval);
static void SendBackupHeader(List *tablespaces);
......@@ -87,8 +89,9 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
{
XLogRecPtr startptr;
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);
{
......@@ -144,6 +147,10 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
pq_sendint(&buf, 0, 2); /* natts */
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,
ti->path == NULL ? 1 : strlen(ti->path),
false);
......@@ -164,7 +171,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
}
PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
endptr = do_pg_stop_backup();
endptr = do_pg_stop_backup(labelfile);
if (opt->includewal)
{
......@@ -299,8 +306,9 @@ parse_basebackup_options(List *options, basebackup_options *opt)
/*
* SendBaseBackup() - send a complete base backup.
*
* The function will take care of running pg_start_backup() and
* pg_stop_backup() for the user.
* The function will put the system into backup mode like pg_start_backup()
* does, so that the backup is consistent even though we read directly from
* the filesystem, bypassing the buffer cache.
*/
void
SendBaseBackup(BaseBackupCmd *cmd)
......@@ -423,7 +431,52 @@ SendBackupHeader(List *tablespaces)
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
sendDir(char *path, int basepathlen, bool sizeonly)
{
......@@ -446,6 +499,14 @@ sendDir(char *path, int basepathlen, bool sizeonly)
strlen(PG_TEMP_FILE_PREFIX)) == 0)
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
* with an error in that case. The error handler further up
......@@ -532,7 +593,7 @@ sendDir(char *path, int basepathlen, bool sizeonly)
/* Add size, rounded up to 512byte block */
size += ((statbuf.st_size + 511) & ~511);
if (!sizeonly)
sendFile(pathbuf, basepathlen, &statbuf);
sendFile(pathbuf, pathbuf + basepathlen + 1, &statbuf);
size += 512; /* Size of the header of the file */
}
else
......@@ -590,7 +651,7 @@ _tarChecksum(char *header)
/* Given the member, write the TAR header & send the file */
static void
sendFile(char *filename, int basepathlen, struct stat * statbuf)
sendFile(char *readfilename, char *tarfilename, struct stat *statbuf)
{
FILE *fp;
char buf[TAR_SEND_SIZE];
......@@ -598,11 +659,11 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
pgoff_t len = 0;
size_t pad;
fp = AllocateFile(filename, "rb");
fp = AllocateFile(readfilename, "rb");
if (fp == NULL)
ereport(ERROR,
(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
......@@ -611,9 +672,9 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
if (statbuf->st_size > MAX_TAR_MEMBER_FILELEN)
ereport(ERROR,
(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)
{
......@@ -660,7 +721,7 @@ sendFile(char *filename, int basepathlen, struct stat * statbuf)
static void
_tarWriteHeader(char *filename, char *linktarget, struct stat * statbuf)
_tarWriteHeader(const char *filename, char *linktarget, struct stat * statbuf)
{
char h[512];
int lastSum = 0;
......
......@@ -312,8 +312,15 @@ extern void HandleStartupProcInterrupts(void);
extern void StartupProcessMain(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);
/* File path names (all relative to $PGDATA) */
#define BACKUP_LABEL_FILE "backup_label"
#define BACKUP_LABEL_OLD "backup_label.old"
#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