Commit 56c7d8d4 authored by Magnus Hagander's avatar Magnus Hagander

Allow pg_basebackup to stream transaction log in tar mode

This will write the received transaction log into a file called
pg_wal.tar(.gz) next to the other tarfiles instead of writing it to
base.tar. When using fetch mode, the transaction log is still written to
base.tar like before, and when used against a pre-10 server, the file
is named pg_xlog.tar.

To do this, implement a new concept of a "walmethod", which is
responsible for writing the WAL. Two implementations exist, one that
writes to a plain directory (which is also used by pg_receivexlog) and
one that writes to a tar file with optional compression.

Reviewed by Michael Paquier
parent 1885c884
......@@ -180,7 +180,8 @@ PostgreSQL documentation
target directory, the tar contents will be written to
standard output, suitable for piping to for example
<productname>gzip</productname>. This is only possible if
the cluster has no additional tablespaces.
the cluster has no additional tablespaces and transaction
log streaming is not used.
</para>
</listitem>
</varlistentry>
......@@ -323,6 +324,10 @@ PostgreSQL documentation
If the log has been rotated when it's time to transfer it, the
backup will fail and be unusable.
</para>
<para>
The transaction log files will be written to
the <filename>base.tar</filename> file.
</para>
</listitem>
</varlistentry>
......@@ -339,6 +344,11 @@ PostgreSQL documentation
client can keep up with transaction log received, using this mode
requires no extra transaction logs to be saved on the master.
</para>
<para>
The transaction log files are written to a separate file
named <filename>pg_wal.tar</filename> (if the server is a version
earlier than 10, the file will be named <filename>pg_xlog.tar</filename>).
</para>
</listitem>
</varlistentry>
</variablelist>
......@@ -353,7 +363,8 @@ PostgreSQL documentation
<para>
Enables gzip compression of tar file output, with the default
compression level. Compression is only available when using
the tar format.
the tar format, and the suffix <filename>.gz</filename> will
automatically be added to all tar filenames.
</para>
</listitem>
</varlistentry>
......@@ -366,7 +377,8 @@ PostgreSQL documentation
Enables gzip compression of tar file output, and specifies the
compression level (0 through 9, 0 being no compression and 9 being best
compression). Compression is only available when using the tar
format.
format, and the suffix <filename>.gz</filename> will
automatically be added to all tar filenames.
</para>
</listitem>
</varlistentry>
......
......@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
LDFLAGS += -L$(top_builddir)/src/fe_utils -lpgfeutils -lpq
OBJS=receivelog.o streamutil.o $(WIN32RES)
OBJS=receivelog.o streamutil.o walmethods.o $(WIN32RES)
all: pg_basebackup pg_receivexlog pg_recvlogical
......
......@@ -449,7 +449,7 @@ typedef struct
{
PGconn *bgconn;
XLogRecPtr startptr;
char xlogdir[MAXPGPATH];
char xlog[MAXPGPATH]; /* directory or tarfile depending on mode */
char *sysidentifier;
int timeline;
} logstreamer_param;
......@@ -470,9 +470,13 @@ LogStreamerMain(logstreamer_param *param)
stream.synchronous = false;
stream.do_sync = do_sync;
stream.mark_done = true;
stream.basedir = param->xlogdir;
stream.partial_suffix = NULL;
if (format == 'p')
stream.walmethod = CreateWalDirectoryMethod(param->xlog, do_sync);
else
stream.walmethod = CreateWalTarMethod(param->xlog, compresslevel, do_sync);
if (!ReceiveXlogStream(param->bgconn, &stream))
/*
......@@ -482,6 +486,14 @@ LogStreamerMain(logstreamer_param *param)
*/
return 1;
if (!stream.walmethod->finish())
{
fprintf(stderr,
_("%s: could not finish writing WAL files: %s\n"),
progname, strerror(errno));
return 1;
}
PQfinish(param->bgconn);
return 0;
}
......@@ -533,28 +545,32 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
exit(1);
/* In post-10 cluster, pg_xlog has been renamed to pg_wal */
snprintf(param->xlogdir, sizeof(param->xlogdir), "%s/%s",
snprintf(param->xlog, sizeof(param->xlog), "%s/%s",
basedir,
PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?
"pg_xlog" : "pg_wal");
/*
* Create pg_wal/archive_status or pg_xlog/archive_status (and thus
* pg_wal or pg_xlog) depending on the target server so we can write to
* basedir/pg_wal or basedir/pg_xlog as the directory entry in the tar
* file may arrive later.
*/
snprintf(statusdir, sizeof(statusdir), "%s/%s/archive_status",
basedir,
PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?
"pg_xlog" : "pg_wal");
if (pg_mkdir_p(statusdir, S_IRWXU) != 0 && errno != EEXIST)
if (format == 'p')
{
fprintf(stderr,
_("%s: could not create directory \"%s\": %s\n"),
progname, statusdir, strerror(errno));
disconnect_and_exit(1);
/*
* Create pg_wal/archive_status or pg_xlog/archive_status (and thus
* pg_wal or pg_xlog) depending on the target server so we can write to
* basedir/pg_wal or basedir/pg_xlog as the directory entry in the tar
* file may arrive later.
*/
snprintf(statusdir, sizeof(statusdir), "%s/%s/archive_status",
basedir,
PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?
"pg_xlog" : "pg_wal");
if (pg_mkdir_p(statusdir, S_IRWXU) != 0 && errno != EEXIST)
{
fprintf(stderr,
_("%s: could not create directory \"%s\": %s\n"),
progname, statusdir, strerror(errno));
disconnect_and_exit(1);
}
}
/*
......@@ -2245,16 +2261,6 @@ main(int argc, char **argv)
exit(1);
}
if (format != 'p' && streamwal)
{
fprintf(stderr,
_("%s: WAL streaming can only be used in plain mode\n"),
progname);
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
progname);
exit(1);
}
if (replication_slot && !streamwal)
{
fprintf(stderr,
......
......@@ -338,11 +338,19 @@ StreamLog(void)
stream.synchronous = synchronous;
stream.do_sync = true;
stream.mark_done = false;
stream.basedir = basedir;
stream.walmethod = CreateWalDirectoryMethod(basedir, stream.do_sync);
stream.partial_suffix = ".partial";
ReceiveXlogStream(conn, &stream);
if (!stream.walmethod->finish())
{
fprintf(stderr,
_("%s: could not finish writing WAL files: %s\n"),
progname, strerror(errno));
return;
}
PQfinish(conn);
conn = NULL;
}
......
This diff is collapsed.
......@@ -13,6 +13,7 @@
#define RECEIVELOG_H
#include "libpq-fe.h"
#include "walmethods.h"
#include "access/xlogdefs.h"
......@@ -41,7 +42,7 @@ typedef struct StreamCtl
stream_stop_callback stream_stop; /* Stop streaming when returns true */
char *basedir; /* Received segments written to this dir */
WalWriteMethod *walmethod; /* How to write the WAL */
char *partial_suffix; /* Suffix appended to partially received files */
} StreamCtl;
......
......@@ -4,7 +4,7 @@ use Cwd;
use Config;
use PostgresNode;
use TestLib;
use Test::More tests => 67;
use Test::More tests => 69;
program_help_ok('pg_basebackup');
program_version_ok('pg_basebackup');
......@@ -237,6 +237,10 @@ $node->command_ok(
'pg_basebackup -X stream runs');
ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_wal")),
'WAL files copied');
$node->command_ok(
[ 'pg_basebackup', '-D', "$tempdir/backupxst", '-X', 'stream', '-Ft' ],
'pg_basebackup -X stream runs in tar mode');
ok(-f "$tempdir/backupxst/pg_wal.tar", "tar file was created");
$node->command_fails(
[ 'pg_basebackup', '-D', "$tempdir/fail", '-S', 'slot1' ],
......
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* walmethods.h
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/bin/pg_basebackup/walmethods.h
*-------------------------------------------------------------------------
*/
typedef void *Walfile;
typedef enum
{
CLOSE_NORMAL,
CLOSE_UNLINK,
CLOSE_NO_RENAME,
} WalCloseMethod;
typedef struct WalWriteMethod WalWriteMethod;
struct WalWriteMethod
{
Walfile(*open_for_write) (const char *pathname, const char *temp_suffix, size_t pad_to_size);
int (*close) (Walfile f, WalCloseMethod method);
bool (*existsfile) (const char *pathname);
ssize_t (*get_file_size) (const char *pathname);
ssize_t (*write) (Walfile f, const void *buf, size_t count);
off_t (*get_current_pos) (Walfile f);
int (*fsync) (Walfile f);
bool (*finish) (void);
char *(*getlasterror) (void);
};
/*
* Available WAL methods:
* - WalDirectoryMethod - write WAL to regular files in a standard pg_xlog
* - TarDirectoryMethod - write WAL to a tarfile corresponding to pg_xlog
* (only implements the methods required for pg_basebackup,
* not all those required for pg_receivexlog)
*/
WalWriteMethod *CreateWalDirectoryMethod(const char *basedir, bool sync);
WalWriteMethod *CreateWalTarMethod(const char *tarbase, int compression, bool sync);
......@@ -22,4 +22,5 @@ enum tarError
extern enum tarError tarCreateHeader(char *h, const char *filename, const char *linktarget,
pgoff_t size, mode_t mode, uid_t uid, gid_t gid, time_t mtime);
extern uint64 read_tar_number(const char *s, int len);
extern void print_tar_number(char *s, int len, uint64 val);
extern int tarChecksum(char *header);
......@@ -16,7 +16,7 @@
* support only non-negative numbers, so we don't worry about the GNU rules
* for handling negative numbers.)
*/
static void
void
print_tar_number(char *s, int len, uint64 val)
{
if (val < (((uint64) 1) << ((len - 1) * 3)))
......
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