On Mon, Jan 24, 2011 at 09:03, Fujii Masao <masao.fu...@gmail.com> wrote: > On Mon, Jan 24, 2011 at 4:47 PM, Magnus Hagander <mag...@hagander.net> wrote: >> On Mon, Jan 24, 2011 at 08:45, Fujii Masao <masao.fu...@gmail.com> wrote: >>> On Fri, Jan 21, 2011 at 12:28 AM, Tom Lane <t...@sss.pgh.pa.us> wrote: >>>> Magnus Hagander <mag...@hagander.net> 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 (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers