mirror of
https://github.com/postgres/postgres.git
synced 2025-06-29 10:41:53 +03:00
Make pg_receivexlog and pg_basebackup -X stream work across timeline switches.
This mirrors the changes done earlier to the server in standby mode. When receivelog reaches the end of a timeline, as reported by the server, it fetches the timeline history file of the next timeline, and restarts streaming from the new timeline by issuing a new START_STREAMING command. When pg_receivexlog crosses a timeline, it leaves the .partial suffix on the last segment on the old timeline. This helps you to tell apart a partial segment left in the directory because of a timeline switch, and a completed segment. If you just follow a single server, it won't make a difference, but it can be significant in more complicated scenarios where new WAL is still generated on the old timeline. This includes two small changes to the streaming replication protocol: First, when you reach the end of timeline while streaming, the server now sends the TLI of the next timeline in the server's history to the client. pg_receivexlog uses that as the next timeline, so that it doesn't need to parse the timeline history file like a standby server does. Second, when BASE_BACKUP command sends the begin and end WAL positions, it now also sends the timeline IDs corresponding the positions.
This commit is contained in:
@ -55,7 +55,7 @@ static void SendBackupHeader(List *tablespaces);
|
||||
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);
|
||||
static void SendXlogRecPtrResult(XLogRecPtr ptr);
|
||||
static void SendXlogRecPtrResult(XLogRecPtr ptr, TimeLineID tli);
|
||||
static int compareWalFileNames(const void *a, const void *b);
|
||||
|
||||
/* Was the backup currently in-progress initiated in recovery mode? */
|
||||
@ -94,13 +94,16 @@ static void
|
||||
perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
|
||||
{
|
||||
XLogRecPtr startptr;
|
||||
TimeLineID starttli;
|
||||
XLogRecPtr endptr;
|
||||
TimeLineID endtli;
|
||||
char *labelfile;
|
||||
|
||||
backup_started_in_recovery = RecoveryInProgress();
|
||||
|
||||
startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &labelfile);
|
||||
SendXlogRecPtrResult(startptr);
|
||||
startptr = do_pg_start_backup(opt->label, opt->fastcheckpoint, &starttli,
|
||||
&labelfile);
|
||||
SendXlogRecPtrResult(startptr, starttli);
|
||||
|
||||
PG_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
|
||||
{
|
||||
@ -218,7 +221,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
|
||||
}
|
||||
PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0);
|
||||
|
||||
endptr = do_pg_stop_backup(labelfile, !opt->nowait);
|
||||
endptr = do_pg_stop_backup(labelfile, !opt->nowait, &endtli);
|
||||
|
||||
if (opt->includewal)
|
||||
{
|
||||
@ -426,7 +429,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
|
||||
/* Send CopyDone message for the last tar file */
|
||||
pq_putemptymessage('c');
|
||||
}
|
||||
SendXlogRecPtrResult(endptr);
|
||||
SendXlogRecPtrResult(endptr, endtli);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -635,17 +638,15 @@ SendBackupHeader(List *tablespaces)
|
||||
* XlogRecPtr record (in text format)
|
||||
*/
|
||||
static void
|
||||
SendXlogRecPtrResult(XLogRecPtr ptr)
|
||||
SendXlogRecPtrResult(XLogRecPtr ptr, TimeLineID tli)
|
||||
{
|
||||
StringInfoData buf;
|
||||
char str[MAXFNAMELEN];
|
||||
|
||||
snprintf(str, sizeof(str), "%X/%X", (uint32) (ptr >> 32), (uint32) ptr);
|
||||
|
||||
pq_beginmessage(&buf, 'T'); /* RowDescription */
|
||||
pq_sendint(&buf, 1, 2); /* 1 field */
|
||||
pq_sendint(&buf, 2, 2); /* 2 fields */
|
||||
|
||||
/* Field header */
|
||||
/* Field headers */
|
||||
pq_sendstring(&buf, "recptr");
|
||||
pq_sendint(&buf, 0, 4); /* table oid */
|
||||
pq_sendint(&buf, 0, 2); /* attnum */
|
||||
@ -653,11 +654,29 @@ SendXlogRecPtrResult(XLogRecPtr ptr)
|
||||
pq_sendint(&buf, -1, 2);
|
||||
pq_sendint(&buf, 0, 4);
|
||||
pq_sendint(&buf, 0, 2);
|
||||
|
||||
pq_sendstring(&buf, "tli");
|
||||
pq_sendint(&buf, 0, 4); /* table oid */
|
||||
pq_sendint(&buf, 0, 2); /* attnum */
|
||||
/*
|
||||
* int8 may seem like a surprising data type for this, but in thory int4
|
||||
* would not be wide enough for this, as TimeLineID is unsigned.
|
||||
*/
|
||||
pq_sendint(&buf, INT8OID, 4); /* type oid */
|
||||
pq_sendint(&buf, -1, 2);
|
||||
pq_sendint(&buf, 0, 4);
|
||||
pq_sendint(&buf, 0, 2);
|
||||
pq_endmessage(&buf);
|
||||
|
||||
/* Data row */
|
||||
pq_beginmessage(&buf, 'D');
|
||||
pq_sendint(&buf, 1, 2); /* number of columns */
|
||||
pq_sendint(&buf, 2, 2); /* number of columns */
|
||||
|
||||
snprintf(str, sizeof(str), "%X/%X", (uint32) (ptr >> 32), (uint32) ptr);
|
||||
pq_sendint(&buf, strlen(str), 4); /* length */
|
||||
pq_sendbytes(&buf, str, strlen(str));
|
||||
|
||||
snprintf(str, sizeof(str), "%u", tli);
|
||||
pq_sendint(&buf, strlen(str), 4); /* length */
|
||||
pq_sendbytes(&buf, str, strlen(str));
|
||||
pq_endmessage(&buf);
|
||||
|
Reference in New Issue
Block a user