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 ...@@ -180,7 +180,8 @@ PostgreSQL documentation
target directory, the tar contents will be written to target directory, the tar contents will be written to
standard output, suitable for piping to for example standard output, suitable for piping to for example
<productname>gzip</productname>. This is only possible if <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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -323,6 +324,10 @@ PostgreSQL documentation ...@@ -323,6 +324,10 @@ PostgreSQL documentation
If the log has been rotated when it's time to transfer it, the If the log has been rotated when it's time to transfer it, the
backup will fail and be unusable. backup will fail and be unusable.
</para> </para>
<para>
The transaction log files will be written to
the <filename>base.tar</filename> file.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -339,6 +344,11 @@ PostgreSQL documentation ...@@ -339,6 +344,11 @@ PostgreSQL documentation
client can keep up with transaction log received, using this mode client can keep up with transaction log received, using this mode
requires no extra transaction logs to be saved on the master. requires no extra transaction logs to be saved on the master.
</para> </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> </listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
...@@ -353,7 +363,8 @@ PostgreSQL documentation ...@@ -353,7 +363,8 @@ PostgreSQL documentation
<para> <para>
Enables gzip compression of tar file output, with the default Enables gzip compression of tar file output, with the default
compression level. Compression is only available when using 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -366,7 +377,8 @@ PostgreSQL documentation ...@@ -366,7 +377,8 @@ PostgreSQL documentation
Enables gzip compression of tar file output, and specifies the Enables gzip compression of tar file output, and specifies the
compression level (0 through 9, 0 being no compression and 9 being best compression level (0 through 9, 0 being no compression and 9 being best
compression). Compression is only available when using the tar 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> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global ...@@ -19,7 +19,7 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)
LDFLAGS += -L$(top_builddir)/src/fe_utils -lpgfeutils -lpq 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 all: pg_basebackup pg_receivexlog pg_recvlogical
......
...@@ -449,7 +449,7 @@ typedef struct ...@@ -449,7 +449,7 @@ typedef struct
{ {
PGconn *bgconn; PGconn *bgconn;
XLogRecPtr startptr; XLogRecPtr startptr;
char xlogdir[MAXPGPATH]; char xlog[MAXPGPATH]; /* directory or tarfile depending on mode */
char *sysidentifier; char *sysidentifier;
int timeline; int timeline;
} logstreamer_param; } logstreamer_param;
...@@ -470,9 +470,13 @@ LogStreamerMain(logstreamer_param *param) ...@@ -470,9 +470,13 @@ LogStreamerMain(logstreamer_param *param)
stream.synchronous = false; stream.synchronous = false;
stream.do_sync = do_sync; stream.do_sync = do_sync;
stream.mark_done = true; stream.mark_done = true;
stream.basedir = param->xlogdir;
stream.partial_suffix = NULL; 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)) if (!ReceiveXlogStream(param->bgconn, &stream))
/* /*
...@@ -482,6 +486,14 @@ LogStreamerMain(logstreamer_param *param) ...@@ -482,6 +486,14 @@ LogStreamerMain(logstreamer_param *param)
*/ */
return 1; 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); PQfinish(param->bgconn);
return 0; return 0;
} }
...@@ -533,28 +545,32 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier) ...@@ -533,28 +545,32 @@ StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
exit(1); exit(1);
/* In post-10 cluster, pg_xlog has been renamed to pg_wal */ /* 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, basedir,
PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ? PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?
"pg_xlog" : "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"), * Create pg_wal/archive_status or pg_xlog/archive_status (and thus
progname, statusdir, strerror(errno)); * pg_wal or pg_xlog) depending on the target server so we can write to
disconnect_and_exit(1); * 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) ...@@ -2245,16 +2261,6 @@ main(int argc, char **argv)
exit(1); 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) if (replication_slot && !streamwal)
{ {
fprintf(stderr, fprintf(stderr,
......
...@@ -338,11 +338,19 @@ StreamLog(void) ...@@ -338,11 +338,19 @@ StreamLog(void)
stream.synchronous = synchronous; stream.synchronous = synchronous;
stream.do_sync = true; stream.do_sync = true;
stream.mark_done = false; stream.mark_done = false;
stream.basedir = basedir; stream.walmethod = CreateWalDirectoryMethod(basedir, stream.do_sync);
stream.partial_suffix = ".partial"; stream.partial_suffix = ".partial";
ReceiveXlogStream(conn, &stream); ReceiveXlogStream(conn, &stream);
if (!stream.walmethod->finish())
{
fprintf(stderr,
_("%s: could not finish writing WAL files: %s\n"),
progname, strerror(errno));
return;
}
PQfinish(conn); PQfinish(conn);
conn = NULL; conn = NULL;
} }
......
This diff is collapsed.
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define RECEIVELOG_H #define RECEIVELOG_H
#include "libpq-fe.h" #include "libpq-fe.h"
#include "walmethods.h"
#include "access/xlogdefs.h" #include "access/xlogdefs.h"
...@@ -41,7 +42,7 @@ typedef struct StreamCtl ...@@ -41,7 +42,7 @@ typedef struct StreamCtl
stream_stop_callback stream_stop; /* Stop streaming when returns true */ 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 */ char *partial_suffix; /* Suffix appended to partially received files */
} StreamCtl; } StreamCtl;
......
...@@ -4,7 +4,7 @@ use Cwd; ...@@ -4,7 +4,7 @@ use Cwd;
use Config; use Config;
use PostgresNode; use PostgresNode;
use TestLib; use TestLib;
use Test::More tests => 67; use Test::More tests => 69;
program_help_ok('pg_basebackup'); program_help_ok('pg_basebackup');
program_version_ok('pg_basebackup'); program_version_ok('pg_basebackup');
...@@ -237,6 +237,10 @@ $node->command_ok( ...@@ -237,6 +237,10 @@ $node->command_ok(
'pg_basebackup -X stream runs'); 'pg_basebackup -X stream runs');
ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_wal")), ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_wal")),
'WAL files copied'); '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( $node->command_fails(
[ 'pg_basebackup', '-D', "$tempdir/fail", '-S', 'slot1' ], [ '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 ...@@ -22,4 +22,5 @@ enum tarError
extern enum tarError tarCreateHeader(char *h, const char *filename, const char *linktarget, 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); 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 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); extern int tarChecksum(char *header);
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
* support only non-negative numbers, so we don't worry about the GNU rules * support only non-negative numbers, so we don't worry about the GNU rules
* for handling negative numbers.) * for handling negative numbers.)
*/ */
static void void
print_tar_number(char *s, int len, uint64 val) print_tar_number(char *s, int len, uint64 val)
{ {
if (val < (((uint64) 1) << ((len - 1) * 3))) 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