Commit 8366c780 authored by Simon Riggs's avatar Simon Riggs

Allow pg_basebackup from standby node with safety checking.

Base backup follows recommended procedure, plus goes to great
lengths to ensure that partial page writes are avoided.

Jun Ishizuka and Fujii Masao, with minor modifications
parent 74ab96a4
...@@ -64,6 +64,48 @@ PostgreSQL documentation ...@@ -64,6 +64,48 @@ PostgreSQL documentation
better from a performance point of view to take only one backup, and copy better from a performance point of view to take only one backup, and copy
the result. the result.
</para> </para>
<para>
<application>pg_basebackup</application> can make a base backup from
not only the master but also the standby. To take a backup from the standby,
set up the standby so that it can accept replication connections (that is, set
<varname>max_wal_senders</> and <xref linkend="guc-hot-standby">,
and configure <link linkend="auth-pg-hba-conf">host-based authentication</link>).
You will also need to enable <xref linkend="guc-full-page-writes"> on the master.
</para>
<para>
Note that there are some limitations in an online backup from the standby:
<itemizedlist>
<listitem>
<para>
The backup history file is not created in the database cluster backed up.
</para>
</listitem>
<listitem>
<para>
There is no guarantee that all WAL files required for the backup are archived
at the end of backup. If you are planning to use the backup for an archive
recovery and want to ensure that all required files are available at that moment,
you need to include them into the backup by using <literal>-x</> option.
</para>
</listitem>
<listitem>
<para>
If the standby is promoted to the master during online backup, the backup fails.
</para>
</listitem>
<listitem>
<para>
All WAL records required for the backup must contain sufficient full-page writes,
which requires you to enable <varname>full_page_writes</> on the master and
not to use the tool like <application>pg_compresslog</> as
<varname>archive_command</> to remove full-page writes from WAL files.
</para>
</listitem>
</itemizedlist>
</para>
</refsect1> </refsect1>
<refsect1> <refsect1>
......
This diff is collapsed.
...@@ -171,6 +171,7 @@ static void CheckArchiveTimeout(void); ...@@ -171,6 +171,7 @@ static void CheckArchiveTimeout(void);
static bool IsCheckpointOnSchedule(double progress); static bool IsCheckpointOnSchedule(double progress);
static bool ImmediateCheckpointRequested(void); static bool ImmediateCheckpointRequested(void);
static bool CompactCheckpointerRequestQueue(void); static bool CompactCheckpointerRequestQueue(void);
static void UpdateSharedMemoryConfig(void);
/* Signal handlers */ /* Signal handlers */
...@@ -351,8 +352,12 @@ CheckpointerMain(void) ...@@ -351,8 +352,12 @@ CheckpointerMain(void)
if (RecoveryInProgress()) if (RecoveryInProgress())
ThisTimeLineID = GetRecoveryTargetTLI(); ThisTimeLineID = GetRecoveryTargetTLI();
/* Do this once before starting the loop, then just at SIGHUP time. */ /*
SyncRepUpdateSyncStandbysDefined(); * Ensure all shared memory values are set correctly for the config.
* Doing this here ensures no race conditions from other concurrent
* updaters.
*/
UpdateSharedMemoryConfig();
/* /*
* Loop forever * Loop forever
...@@ -380,8 +385,19 @@ CheckpointerMain(void) ...@@ -380,8 +385,19 @@ CheckpointerMain(void)
{ {
got_SIGHUP = false; got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP); ProcessConfigFile(PGC_SIGHUP);
/* update global shmem state for sync rep */
SyncRepUpdateSyncStandbysDefined(); /*
* Checkpointer is the last process to shutdown, so we ask
* it to hold the keys for a range of other tasks required
* most of which have nothing to do with checkpointing at all.
*
* For various reasons, some config values can change
* dynamically so are the primary copy of them is held in
* shared memory to make sure all backends see the same value.
* We make Checkpointer responsible for updating the shared
* memory copy if the parameter setting changes because of SIGHUP.
*/
UpdateSharedMemoryConfig();
} }
if (checkpoint_requested) if (checkpoint_requested)
{ {
...@@ -1239,3 +1255,21 @@ AbsorbFsyncRequests(void) ...@@ -1239,3 +1255,21 @@ AbsorbFsyncRequests(void)
END_CRIT_SECTION(); END_CRIT_SECTION();
} }
/*
* Update any shared memory configurations based on config parameters
*/
static void
UpdateSharedMemoryConfig(void)
{
/* update global shmem state for sync rep */
SyncRepUpdateSyncStandbysDefined();
/*
* If full_page_writes has been changed by SIGHUP, we update it
* in shared memory and write an XLOG_FPW_CHANGE record.
*/
UpdateFullPageWrites();
elog(DEBUG2, "checkpointer updated shared memory configuration values");
}
...@@ -3067,8 +3067,8 @@ PostmasterStateMachine(void) ...@@ -3067,8 +3067,8 @@ PostmasterStateMachine(void)
else else
{ {
/* /*
* Terminate backup mode to avoid recovery after a clean fast * Terminate exclusive backup mode to avoid recovery after a clean fast
* shutdown. Since a backup can only be taken during normal * shutdown. Since an exclusive backup can only be taken during normal
* running (and not, for example, while running under Hot Standby) * running (and not, for example, while running under Hot Standby)
* it only makes sense to do this if we reached normal running. If * it only makes sense to do this if we reached normal running. If
* we're still in recovery, the backup file is one we're * we're still in recovery, the backup file is one we're
......
...@@ -180,6 +180,22 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) ...@@ -180,6 +180,22 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir)
ti->path == NULL ? 1 : strlen(ti->path), ti->path == NULL ? 1 : strlen(ti->path),
false); false);
/* In the main tar, include pg_control last. */
if (ti->path == NULL)
{
struct stat statbuf;
if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0)
{
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not stat control file \"%s\": %m",
XLOG_CONTROL_FILE)));
}
sendFile(XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf);
}
/* /*
* If we're including WAL, and this is the main data directory we * If we're including WAL, and this is the main data directory we
* don't terminate the tar stream here. Instead, we will append * don't terminate the tar stream here. Instead, we will append
...@@ -361,11 +377,6 @@ SendBaseBackup(BaseBackupCmd *cmd) ...@@ -361,11 +377,6 @@ SendBaseBackup(BaseBackupCmd *cmd)
MemoryContext old_context; MemoryContext old_context;
basebackup_options opt; basebackup_options opt;
if (am_cascading_walsender)
ereport(FATAL,
(errcode(ERRCODE_CANNOT_CONNECT_NOW),
errmsg("recovery is still in progress, can't accept WAL streaming connections for backup")));
parse_basebackup_options(cmd->options, &opt); parse_basebackup_options(cmd->options, &opt);
backup_context = AllocSetContextCreate(CurrentMemoryContext, backup_context = AllocSetContextCreate(CurrentMemoryContext,
...@@ -609,6 +620,10 @@ sendDir(char *path, int basepathlen, bool sizeonly) ...@@ -609,6 +620,10 @@ sendDir(char *path, int basepathlen, bool sizeonly)
strcmp(pathbuf, "./postmaster.opts") == 0) strcmp(pathbuf, "./postmaster.opts") == 0)
continue; continue;
/* Skip pg_control here to back up it last */
if (strcmp(pathbuf, "./global/pg_control") == 0)
continue;
if (lstat(pathbuf, &statbuf) != 0) if (lstat(pathbuf, &statbuf) != 0)
{ {
if (errno != ENOENT) if (errno != ENOENT)
......
...@@ -130,7 +130,6 @@ extern int CommitSiblings; ...@@ -130,7 +130,6 @@ extern int CommitSiblings;
extern char *default_tablespace; extern char *default_tablespace;
extern char *temp_tablespaces; extern char *temp_tablespaces;
extern bool synchronize_seqscans; extern bool synchronize_seqscans;
extern bool fullPageWrites;
extern int ssl_renegotiation_limit; extern int ssl_renegotiation_limit;
extern char *SSLCipherSuites; extern char *SSLCipherSuites;
......
...@@ -209,6 +209,8 @@ main(int argc, char *argv[]) ...@@ -209,6 +209,8 @@ main(int argc, char *argv[])
ControlFile.checkPointCopy.redo.xrecoff); ControlFile.checkPointCopy.redo.xrecoff);
printf(_("Latest checkpoint's TimeLineID: %u\n"), printf(_("Latest checkpoint's TimeLineID: %u\n"),
ControlFile.checkPointCopy.ThisTimeLineID); ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
printf(_("Latest checkpoint's NextXID: %u/%u\n"), printf(_("Latest checkpoint's NextXID: %u/%u\n"),
ControlFile.checkPointCopy.nextXidEpoch, ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid); ControlFile.checkPointCopy.nextXid);
...@@ -232,6 +234,9 @@ main(int argc, char *argv[]) ...@@ -232,6 +234,9 @@ main(int argc, char *argv[])
printf(_("Backup start location: %X/%X\n"), printf(_("Backup start location: %X/%X\n"),
ControlFile.backupStartPoint.xlogid, ControlFile.backupStartPoint.xlogid,
ControlFile.backupStartPoint.xrecoff); ControlFile.backupStartPoint.xrecoff);
printf(_("Backup end location: %X/%X\n"),
ControlFile.backupEndPoint.xlogid,
ControlFile.backupEndPoint.xrecoff);
printf(_("End-of-backup record required: %s\n"), printf(_("End-of-backup record required: %s\n"),
ControlFile.backupEndRequired ? _("yes") : _("no")); ControlFile.backupEndRequired ? _("yes") : _("no"));
printf(_("Current wal_level setting: %s\n"), printf(_("Current wal_level setting: %s\n"),
......
...@@ -489,6 +489,7 @@ GuessControlValues(void) ...@@ -489,6 +489,7 @@ GuessControlValues(void)
ControlFile.checkPointCopy.redo.xlogid = 0; ControlFile.checkPointCopy.redo.xlogid = 0;
ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogLongPHD; ControlFile.checkPointCopy.redo.xrecoff = SizeOfXLogLongPHD;
ControlFile.checkPointCopy.ThisTimeLineID = 1; ControlFile.checkPointCopy.ThisTimeLineID = 1;
ControlFile.checkPointCopy.fullPageWrites = false;
ControlFile.checkPointCopy.nextXidEpoch = 0; ControlFile.checkPointCopy.nextXidEpoch = 0;
ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId; ControlFile.checkPointCopy.nextXid = FirstNormalTransactionId;
ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId; ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
...@@ -503,7 +504,7 @@ GuessControlValues(void) ...@@ -503,7 +504,7 @@ GuessControlValues(void)
ControlFile.time = (pg_time_t) time(NULL); ControlFile.time = (pg_time_t) time(NULL);
ControlFile.checkPoint = ControlFile.checkPointCopy.redo; ControlFile.checkPoint = ControlFile.checkPointCopy.redo;
/* minRecoveryPoint and backupStartPoint can be left zero */ /* minRecoveryPoint, backupStartPoint and backupEndPoint can be left zero */
ControlFile.wal_level = WAL_LEVEL_MINIMAL; ControlFile.wal_level = WAL_LEVEL_MINIMAL;
ControlFile.MaxConnections = 100; ControlFile.MaxConnections = 100;
...@@ -569,6 +570,8 @@ PrintControlValues(bool guessed) ...@@ -569,6 +570,8 @@ PrintControlValues(bool guessed)
sysident_str); sysident_str);
printf(_("Latest checkpoint's TimeLineID: %u\n"), printf(_("Latest checkpoint's TimeLineID: %u\n"),
ControlFile.checkPointCopy.ThisTimeLineID); ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
printf(_("Latest checkpoint's NextXID: %u/%u\n"), printf(_("Latest checkpoint's NextXID: %u/%u\n"),
ControlFile.checkPointCopy.nextXidEpoch, ControlFile.checkPointCopy.nextXidEpoch,
ControlFile.checkPointCopy.nextXid); ControlFile.checkPointCopy.nextXid);
...@@ -637,6 +640,8 @@ RewriteControlFile(void) ...@@ -637,6 +640,8 @@ RewriteControlFile(void)
ControlFile.minRecoveryPoint.xrecoff = 0; ControlFile.minRecoveryPoint.xrecoff = 0;
ControlFile.backupStartPoint.xlogid = 0; ControlFile.backupStartPoint.xlogid = 0;
ControlFile.backupStartPoint.xrecoff = 0; ControlFile.backupStartPoint.xrecoff = 0;
ControlFile.backupEndPoint.xlogid = 0;
ControlFile.backupEndPoint.xrecoff = 0;
ControlFile.backupEndRequired = false; ControlFile.backupEndRequired = false;
/* /*
......
...@@ -192,6 +192,7 @@ extern int XLogArchiveTimeout; ...@@ -192,6 +192,7 @@ extern int XLogArchiveTimeout;
extern bool XLogArchiveMode; extern bool XLogArchiveMode;
extern char *XLogArchiveCommand; extern char *XLogArchiveCommand;
extern bool EnableHotStandby; extern bool EnableHotStandby;
extern bool fullPageWrites;
extern bool log_checkpoints; extern bool log_checkpoints;
/* WAL levels */ /* WAL levels */
...@@ -307,6 +308,7 @@ extern void CreateCheckPoint(int flags); ...@@ -307,6 +308,7 @@ extern void CreateCheckPoint(int flags);
extern bool CreateRestartPoint(int flags); extern bool CreateRestartPoint(int flags);
extern void XLogPutNextOid(Oid nextOid); extern void XLogPutNextOid(Oid nextOid);
extern XLogRecPtr XLogRestorePoint(const char *rpName); extern XLogRecPtr XLogRestorePoint(const char *rpName);
extern void UpdateFullPageWrites(void);
extern XLogRecPtr GetRedoRecPtr(void); extern XLogRecPtr GetRedoRecPtr(void);
extern XLogRecPtr GetInsertRecPtr(void); extern XLogRecPtr GetInsertRecPtr(void);
extern XLogRecPtr GetFlushRecPtr(void); extern XLogRecPtr GetFlushRecPtr(void);
......
...@@ -71,7 +71,7 @@ typedef struct XLogContRecord ...@@ -71,7 +71,7 @@ typedef struct XLogContRecord
/* /*
* Each page of XLOG file has a header like this: * Each page of XLOG file has a header like this:
*/ */
#define XLOG_PAGE_MAGIC 0xD069 /* can be used as WAL version indicator */ #define XLOG_PAGE_MAGIC 0xD070 /* can be used as WAL version indicator */
typedef struct XLogPageHeaderData typedef struct XLogPageHeaderData
{ {
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
/* Version identifier for this pg_control format */ /* Version identifier for this pg_control format */
#define PG_CONTROL_VERSION 921 #define PG_CONTROL_VERSION 922
/* /*
* Body of CheckPoint XLOG records. This is declared here because we keep * Body of CheckPoint XLOG records. This is declared here because we keep
...@@ -33,6 +33,7 @@ typedef struct CheckPoint ...@@ -33,6 +33,7 @@ typedef struct CheckPoint
XLogRecPtr redo; /* next RecPtr available when we began to XLogRecPtr redo; /* next RecPtr available when we began to
* create CheckPoint (i.e. REDO start point) */ * create CheckPoint (i.e. REDO start point) */
TimeLineID ThisTimeLineID; /* current TLI */ TimeLineID ThisTimeLineID; /* current TLI */
bool fullPageWrites; /* current full_page_writes */
uint32 nextXidEpoch; /* higher-order bits of nextXid */ uint32 nextXidEpoch; /* higher-order bits of nextXid */
TransactionId nextXid; /* next free XID */ TransactionId nextXid; /* next free XID */
Oid nextOid; /* next free OID */ Oid nextOid; /* next free OID */
...@@ -60,6 +61,7 @@ typedef struct CheckPoint ...@@ -60,6 +61,7 @@ typedef struct CheckPoint
#define XLOG_BACKUP_END 0x50 #define XLOG_BACKUP_END 0x50
#define XLOG_PARAMETER_CHANGE 0x60 #define XLOG_PARAMETER_CHANGE 0x60
#define XLOG_RESTORE_POINT 0x70 #define XLOG_RESTORE_POINT 0x70
#define XLOG_FPW_CHANGE 0x80
/* /*
...@@ -138,6 +140,12 @@ typedef struct ControlFileData ...@@ -138,6 +140,12 @@ typedef struct ControlFileData
* record, to make sure the end-of-backup record corresponds the base * record, to make sure the end-of-backup record corresponds the base
* backup we're recovering from. * backup we're recovering from.
* *
* backupEndPoint is the backup end location, if we are recovering from
* an online backup which was taken from the standby and haven't reached
* the end of backup yet. It is initialized to the minimum recovery point
* in pg_control which was backed up last. It is reset to zero when
* the end of backup is reached, and we mustn't start up before that.
*
* If backupEndRequired is true, we know for sure that we're restoring * If backupEndRequired is true, we know for sure that we're restoring
* from a backup, and must see a backup-end record before we can safely * from a backup, and must see a backup-end record before we can safely
* start up. If it's false, but backupStartPoint is set, a backup_label * start up. If it's false, but backupStartPoint is set, a backup_label
...@@ -146,6 +154,7 @@ typedef struct ControlFileData ...@@ -146,6 +154,7 @@ typedef struct ControlFileData
*/ */
XLogRecPtr minRecoveryPoint; XLogRecPtr minRecoveryPoint;
XLogRecPtr backupStartPoint; XLogRecPtr backupStartPoint;
XLogRecPtr backupEndPoint;
bool backupEndRequired; bool backupEndRequired;
/* /*
......
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