On Mon, Jan 24, 2011 at 09:03, Fujii Masao <[email protected]> wrote:
> On Mon, Jan 24, 2011 at 4:47 PM, Magnus Hagander <[email protected]> wrote:
>> On Mon, Jan 24, 2011 at 08:45, Fujii Masao <[email protected]> wrote:
>>> On Fri, Jan 21, 2011 at 12:28 AM, Tom Lane <[email protected]> wrote:
>>>> Magnus Hagander <[email protected]> writes:
>>>>>> - Why not initialize logid and logseg like so?:
>>>>>>
>>>>>> int logid = startptr.xlogid;
>>>>>> int logseg = startptr.xrecoff / XLogSegSize;
>>>>>>
>>>>>> Then use those in your elog? Seems cleaner to me.
>>>>
>>>>> Hmm. Yes. Agreed.
>>>>
>>>> Marginal complaint here: int is the wrong type, I'm pretty sure.
>>>
>>> And, we should use XLByteToPrevSeg here instead of just =, I think.
>>
>> Not XLByteToSeg?
>
> Checking... yeah, you are right. We should use XLByteToSeg since
> the REDO starting WAL record doesn't exist in the previous WAL
> segment when the REDO starting location is a boundary byte.
Here's an updated version of the patch:
* rebased on the current git master (including changing the switch
from -w to -x since -w was used now)
* includes some documentation
* use XLogByteToSeg and uint32 as mentioned here
* refactored to remove SendBackupDirectory(), removing the ugly closetar boolean
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 73f26b4..c257413 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1460,7 +1460,7 @@ The commands accepted in walsender mode are:
</varlistentry>
<varlistentry>
- <term>BASE_BACKUP [<literal>LABEL</literal> <replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] [<literal>FAST</literal>]</term>
+ <term>BASE_BACKUP [<literal>LABEL</literal> <replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] [<literal>FAST</literal>] [<literal>WAL</literal>]</term>
<listitem>
<para>
Instructs the server to start streaming a base backup.
@@ -1505,6 +1505,18 @@ The commands accepted in walsender mode are:
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><literal>WAL</literal></term>
+ <listitem>
+ <para>
+ Include the necessary WAL segments in the backup. This will include
+ all the files between start and stop backup in the
+ <filename>pg_xlog</filename> directory of the base directory tar
+ file.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
<para>
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index 321c8ca..60ffa3c 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -145,6 +145,31 @@ PostgreSQL documentation
</varlistentry>
<varlistentry>
+ <term><option>-x</option></term>
+ <term><option>--xlog</option></term>
+ <listitem>
+ <para>
+ Includes the required transaction log files (WAL files) in the
+ backup. This will include all transaction logs generated during
+ the backup. If this option is specified, it is possible to start
+ a postmaster directly in the extracted directory without the need
+ to consult the log archive, thus making this a completely standalone
+ backup.
+ </para>
+ <note>
+ <para>
+ The transaction log files are collected at the end of the backup.
+ Therefore, it is necessary for the
+ <xref linkend="guc-wal-keep-segments"> parameter to be set high
+ enough that the log is not removed before the end of the backup.
+ If the log has been rotated when it's time to transfer it, the
+ backup will fail and be unusable.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-Z <replaceable class="parameter">level</replaceable></option></term>
<term><option>--compress=<replaceable class="parameter">level</replaceable></option></term>
<listitem>
diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c
index 73edcf2..9bffe17 100644
--- a/src/backend/replication/basebackup.c
+++ b/src/backend/replication/basebackup.c
@@ -37,6 +37,7 @@ typedef struct
const char *label;
bool progress;
bool fastcheckpoint;
+ bool includewal;
} basebackup_options;
@@ -46,7 +47,6 @@ static void _tarWriteHeader(char *filename, char *linktarget,
struct stat * statbuf);
static void send_int8_string(StringInfoData *buf, int64 intval);
static void SendBackupHeader(List *tablespaces);
-static void SendBackupDirectory(char *location, char *spcoid);
static void base_backup_cleanup(int code, Datum arg);
static void perform_base_backup(basebackup_options *opt, DIR *tblspcdir);
static void parse_basebackup_options(List *options, basebackup_options *opt);
@@ -78,7 +78,10 @@ base_backup_cleanup(int code, Datum arg)
static void
perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
{
- do_pg_start_backup(opt->label, opt->fastcheckpoint);
+ XLogRecPtr startptr;
+ XLogRecPtr endptr;
+
+ startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint);
PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
{
@@ -87,12 +90,6 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
struct dirent *de;
tablespaceinfo *ti;
-
- /* Add a node for the base directory */
- ti = palloc0(sizeof(tablespaceinfo));
- ti->size = opt->progress ? sendDir(".", 1, true) : -1;
- tablespaces = lappend(tablespaces, ti);
-
/* Collect information about all tablespaces */
while ((de = ReadDir(tblspcdir, "pg_tblspc")) != NULL)
{
@@ -120,6 +117,10 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
tablespaces = lappend(tablespaces, ti);
}
+ /* Add a node for the base directory at the end */
+ ti = palloc0(sizeof(tablespaceinfo));
+ ti->size = opt->progress ? sendDir(".", 1, true) : -1;
+ tablespaces = lappend(tablespaces, ti);
/* Send tablespace header */
SendBackupHeader(tablespaces);
@@ -128,13 +129,84 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
foreach(lc, tablespaces)
{
tablespaceinfo *ti = (tablespaceinfo *) lfirst(lc);
+ StringInfoData buf;
- SendBackupDirectory(ti->path, ti->oid);
+ /* Send CopyOutResponse message */
+ pq_beginmessage(&buf, 'H');
+ pq_sendbyte(&buf, 0); /* overall format */
+ pq_sendint(&buf, 0, 2); /* natts */
+ pq_endmessage(&buf);
+
+ sendDir(ti->path == NULL ? "." : ti->path,
+ ti->path == NULL ? 1 : strlen(ti->path),
+ false);
+
+ /*
+ * If we're including WAL, and this is the main data directory
+ * we don't terminate the tar stream here. Instead, we will append
+ * the xlog files below and terminate it then. This is safe since
+ * the main data directory is always sent *last*.
+ */
+ if (opt->includewal && ti->path == NULL)
+ {
+ Assert(lnext(lc) == NULL);
+ }
+ else
+ pq_putemptymessage('c'); /* CopyDone */
}
}
PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
- do_pg_stop_backup();
+ endptr = do_pg_stop_backup();
+
+ if (opt->includewal)
+ {
+ /*
+ * We've left the last tar file "open", so we can now append the
+ * required WAL files to it.
+ */
+ uint32 logid, logseg;
+ uint32 endlogid, endlogseg;
+
+ XLByteToSeg(startptr, logid, logseg);
+ XLByteToSeg(endptr, endlogid, endlogseg);
+ elog(DEBUG1, "Going to send wal from %i.%i to %i.%i",
+ logid, logseg,
+ endlogid, endlogseg);
+
+ while (true)
+ {
+ char xlogname[64];
+ char fn[MAXPGPATH];
+ struct stat statbuf;
+
+ /* Send the current WAL file */
+ XLogFileName(xlogname, ThisTimeLineID, logid, logseg);
+ sprintf(fn, "./pg_xlog/%s", xlogname);
+ if (lstat(fn, &statbuf) != 0)
+ ereport(ERROR,
+ (errcode(errcode_for_file_access()),
+ errmsg("could not stat file \"%s\": %m",
+ fn)));
+
+ if (!S_ISREG(statbuf.st_mode))
+ ereport(ERROR,
+ (errmsg("\"%s\" is not a file",
+ fn)));
+ sendFile(fn, 1, &statbuf);
+
+ /* Advance to the next WAL file */
+ NextLogSeg(logid, logseg);
+
+ /* Have we reached our stop position yet? */
+ if (logid > endptr.xlogid ||
+ (logid == endptr.xlogid && logseg >= endptr.xrecoff / XLogSegSize))
+ break;
+ }
+
+ /* Send CopyDone message for the last tar file*/
+ pq_putemptymessage('c');
+ }
}
/*
@@ -147,6 +219,7 @@ parse_basebackup_options(List *options, basebackup_options *opt)
bool o_label = false;
bool o_progress = false;
bool o_fast = false;
+ bool o_wal = false;
MemSet(opt, 0, sizeof(opt));
foreach(lopt, options)
@@ -180,6 +253,15 @@ parse_basebackup_options(List *options, basebackup_options *opt)
opt->fastcheckpoint = true;
o_fast = true;
}
+ else if (strcmp(defel->defname, "wal") == 0)
+ {
+ if (o_wal)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("duplicate option \"%s\"", defel->defname)));
+ opt->includewal = true;
+ o_wal = true;
+ }
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
@@ -316,26 +398,6 @@ SendBackupHeader(List *tablespaces)
pq_puttextmessage('C', "SELECT");
}
-static void
-SendBackupDirectory(char *location, char *spcoid)
-{
- StringInfoData buf;
-
- /* Send CopyOutResponse message */
- pq_beginmessage(&buf, 'H');
- pq_sendbyte(&buf, 0); /* overall format */
- pq_sendint(&buf, 0, 2); /* natts */
- pq_endmessage(&buf);
-
- /* tar up the data directory if NULL, otherwise the tablespace */
- sendDir(location == NULL ? "." : location,
- location == NULL ? 1 : strlen(location),
- false);
-
- /* Send CopyDone message */
- pq_putemptymessage('c');
-}
-
static int64
sendDir(char *path, int basepathlen, bool sizeonly)
diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y
index 879a0bd..1adcbdc 100644
--- a/src/backend/replication/repl_gram.y
+++ b/src/backend/replication/repl_gram.y
@@ -71,6 +71,7 @@ Node *replication_parse_result;
%token K_LABEL
%token K_PROGRESS
%token K_FAST
+%token K_WAL
%token K_START_REPLICATION
%type <node> command
@@ -136,7 +137,12 @@ base_backup_opt:
$$ = makeDefElem("fast",
(Node *)makeInteger(TRUE));
}
-
+ | K_WAL
+ {
+ $$ = makeDefElem("wal",
+ (Node *)makeInteger(TRUE));
+ }
+ ;
/*
* START_REPLICATION %X/%X
diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l
index e6dfb04..87e77d9 100644
--- a/src/backend/replication/repl_scanner.l
+++ b/src/backend/replication/repl_scanner.l
@@ -61,6 +61,7 @@ FAST { return K_FAST; }
IDENTIFY_SYSTEM { return K_IDENTIFY_SYSTEM; }
LABEL { return K_LABEL; }
PROGRESS { return K_PROGRESS; }
+WAL { return K_WAL; }
START_REPLICATION { return K_START_REPLICATION; }
"," { return ','; }
";" { return ';'; }
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index 5363034..baea186 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -33,6 +33,7 @@ char *label = "pg_basebackup base backup";
bool showprogress = false;
int verbose = 0;
int compresslevel = 0;
+bool includewal = false;
bool fastcheckpoint = false;
char *dbhost = NULL;
char *dbuser = NULL;
@@ -124,6 +125,7 @@ usage(void)
printf(_("\nOptions controlling the output:\n"));
printf(_(" -D, --pgdata=directory receive base backup into directory\n"));
printf(_(" -F, --format=p|t output format (plain, tar)\n"));
+ printf(_(" -x, --xlog include required WAL files in backup\n"));
printf(_(" -Z, --compress=0-9 compress tar output\n"));
printf(_("\nGeneral options:\n"));
printf(_(" -c, --checkpoint=fast|spread\n"
@@ -746,9 +748,10 @@ BaseBackup()
conn = GetConnection();
PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i);
- snprintf(current_path, sizeof(current_path), "BASE_BACKUP LABEL '%s' %s %s",
+ snprintf(current_path, sizeof(current_path), "BASE_BACKUP LABEL '%s' %s %s %s",
escaped_label,
showprogress ? "PROGRESS" : "",
+ includewal ? "WAL" : "",
fastcheckpoint ? "FAST" : "");
if (PQsendQuery(conn, current_path) == 0)
@@ -789,7 +792,7 @@ BaseBackup()
* first once since it can be relocated, and it will be checked before
* we do anything anyway.
*/
- if (format == 'p' && i > 0)
+ if (format == 'p' && !PQgetisnull(res, i, 1))
verify_dir_is_empty_or_create(PQgetvalue(res, i, 1));
}
@@ -848,6 +851,7 @@ main(int argc, char **argv)
{"pgdata", required_argument, NULL, 'D'},
{"format", required_argument, NULL, 'F'},
{"checkpoint", required_argument, NULL, 'c'},
+ {"xlog", no_argument, NULL, 'w'},
{"compress", required_argument, NULL, 'Z'},
{"label", required_argument, NULL, 'l'},
{"host", required_argument, NULL, 'h'},
@@ -881,7 +885,7 @@ main(int argc, char **argv)
}
}
- while ((c = getopt_long(argc, argv, "D:F:l:Z:c:h:p:U:wWvP",
+ while ((c = getopt_long(argc, argv, "D:F:l:Z:c:h:p:U:xwWvP",
long_options, &option_index)) != -1)
{
switch (c)
@@ -901,6 +905,9 @@ main(int argc, char **argv)
exit(1);
}
break;
+ case 'x':
+ includewal = true;
+ break;
case 'l':
label = xstrdup(optarg);
break;
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers