Commit ffd37740 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Add archive_mode='always' option.

In 'always' mode, the standby independently archives all files it receives
from the primary.

Original patch by Fujii Masao, docs and review by me.
parent f6d65f0c
...@@ -2521,7 +2521,7 @@ include_dir 'conf.d' ...@@ -2521,7 +2521,7 @@ include_dir 'conf.d'
<variablelist> <variablelist>
<varlistentry id="guc-archive-mode" xreflabel="archive_mode"> <varlistentry id="guc-archive-mode" xreflabel="archive_mode">
<term><varname>archive_mode</varname> (<type>boolean</type>) <term><varname>archive_mode</varname> (<type>enum</type>)
<indexterm> <indexterm>
<primary><varname>archive_mode</> configuration parameter</primary> <primary><varname>archive_mode</> configuration parameter</primary>
</indexterm> </indexterm>
...@@ -2530,7 +2530,16 @@ include_dir 'conf.d' ...@@ -2530,7 +2530,16 @@ include_dir 'conf.d'
<para> <para>
When <varname>archive_mode</> is enabled, completed WAL segments When <varname>archive_mode</> is enabled, completed WAL segments
are sent to archive storage by setting are sent to archive storage by setting
<xref linkend="guc-archive-command">. <xref linkend="guc-archive-command">. In addition to <literal>off</>,
to disable, there are two modes: <literal>on</>, and
<literal>always</>. During normal operation, there is no
difference between the two modes, but when set to <literal>always</>
the WAL archiver is enabled also during archive recovery or standby
mode. In <literal>always</> mode, all files restored from the archive
or streamed with streaming replication will be archived (again). See
<xref linkend="continuous-archiving-in-standby"> for details.
</para>
<para>
<varname>archive_mode</> and <varname>archive_command</> are <varname>archive_mode</> and <varname>archive_command</> are
separate variables so that <varname>archive_command</> can be separate variables so that <varname>archive_command</> can be
changed without leaving archiving mode. changed without leaving archiving mode.
......
...@@ -1220,6 +1220,45 @@ primary_slot_name = 'node_a_slot' ...@@ -1220,6 +1220,45 @@ primary_slot_name = 'node_a_slot'
</sect3> </sect3>
</sect2> </sect2>
<sect2 id="continuous-archiving-in-standby">
<title>Continuous archiving in standby</title>
<indexterm>
<primary>continuous archiving</primary>
<secondary>in standby</secondary>
</indexterm>
<para>
When continuous WAL archiving is used in a standby, there are two
different scenarios: the WAL archive can be shared between the primary
and the standby, or the standby can have its own WAL archive. When
the standby has its own WAL archive, set <varname>archive_mode</varname>
to <literal>always</literal>, and the standby will call the archive
command for every WAL segment it receives, whether it's by restoring
from the archive or by streaming replication. The shared archive can
be handled similarly, but the archive_command must test if the file
being archived exists already, and if the existing file has identical
contents. This requires more care in the archive_command, as it must
be careful to not overwrite an existing file with different contents,
but return success if the exactly same file is archived twice. And
all that must be done free of race conditions, if two servers attempt
to archive the same file at the same time.
</para>
</para>
If <varname>archive_mode</varname> is set to <literal>on</>, the
archiver is not enabled during recovery or standby mode. If the standby
server is promoted, it will start archiving after the promotion, but
will not archive any WAL it did not generate itself. To get a complete
series of WAL files in the archive, you must ensure that all WAL is
archived, before it reaches the standby. This is inherently true with
file-based log shipping, as the standby can only restore files that
are found in the archive, but not if streaming replication is enabled.
When a server is not in recovery mode, there is no difference between
<literal>on</literal> and <literal>always</literal> modes.
</para>
</sect2>
</sect1> </sect1>
<sect1 id="warm-standby-failover"> <sect1 id="warm-standby-failover">
......
...@@ -86,7 +86,7 @@ int min_wal_size = 5; /* 80 MB */ ...@@ -86,7 +86,7 @@ int min_wal_size = 5; /* 80 MB */
int wal_keep_segments = 0; int wal_keep_segments = 0;
int XLOGbuffers = -1; int XLOGbuffers = -1;
int XLogArchiveTimeout = 0; int XLogArchiveTimeout = 0;
bool XLogArchiveMode = false; int XLogArchiveMode = ARCHIVE_MODE_OFF;
char *XLogArchiveCommand = NULL; char *XLogArchiveCommand = NULL;
bool EnableHotStandby = false; bool EnableHotStandby = false;
bool fullPageWrites = true; bool fullPageWrites = true;
...@@ -140,6 +140,24 @@ const struct config_enum_entry sync_method_options[] = { ...@@ -140,6 +140,24 @@ const struct config_enum_entry sync_method_options[] = {
{NULL, 0, false} {NULL, 0, false}
}; };
/*
* Although only "on", "off", and "always" are documented,
* we accept all the likely variants of "on" and "off".
*/
const struct config_enum_entry archive_mode_options[] = {
{"always", ARCHIVE_MODE_ALWAYS, false},
{"on", ARCHIVE_MODE_ON, false},
{"off", ARCHIVE_MODE_OFF, false},
{"true", ARCHIVE_MODE_ON, true},
{"false", ARCHIVE_MODE_OFF, true},
{"yes", ARCHIVE_MODE_ON, true},
{"no", ARCHIVE_MODE_OFF, true},
{"1", ARCHIVE_MODE_ON, true},
{"0", ARCHIVE_MODE_OFF, true},
{NULL, 0, false}
};
/* /*
* Statistics for current checkpoint are collected in this global struct. * Statistics for current checkpoint are collected in this global struct.
* Because only the checkpointer or a stand-alone backend can perform * Because only the checkpointer or a stand-alone backend can perform
...@@ -767,7 +785,7 @@ static MemoryContext walDebugCxt = NULL; ...@@ -767,7 +785,7 @@ static MemoryContext walDebugCxt = NULL;
#endif #endif
static void readRecoveryCommandFile(void); static void readRecoveryCommandFile(void);
static void exitArchiveRecovery(TimeLineID endTLI, XLogSegNo endLogSegNo); static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog);
static bool recoveryStopsBefore(XLogReaderState *record); static bool recoveryStopsBefore(XLogReaderState *record);
static bool recoveryStopsAfter(XLogReaderState *record); static bool recoveryStopsAfter(XLogReaderState *record);
static void recoveryPausesHere(void); static void recoveryPausesHere(void);
......
...@@ -480,7 +480,10 @@ KeepFileRestoredFromArchive(char *path, char *xlogfname) ...@@ -480,7 +480,10 @@ KeepFileRestoredFromArchive(char *path, char *xlogfname)
* Create .done file forcibly to prevent the restored segment from being * Create .done file forcibly to prevent the restored segment from being
* archived again later. * archived again later.
*/ */
XLogArchiveForceDone(xlogfname); if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS)
XLogArchiveForceDone(xlogfname);
else
XLogArchiveNotify(xlogfname);
/* /*
* If the existing file was replaced, since walsenders might have it open, * If the existing file was replaced, since walsenders might have it open,
......
...@@ -828,9 +828,9 @@ PostmasterMain(int argc, char *argv[]) ...@@ -828,9 +828,9 @@ PostmasterMain(int argc, char *argv[])
write_stderr("%s: max_wal_senders must be less than max_connections\n", progname); write_stderr("%s: max_wal_senders must be less than max_connections\n", progname);
ExitPostmaster(1); ExitPostmaster(1);
} }
if (XLogArchiveMode && wal_level == WAL_LEVEL_MINIMAL) if (XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level == WAL_LEVEL_MINIMAL)
ereport(ERROR, ereport(ERROR,
(errmsg("WAL archival (archive_mode=on) requires wal_level \"archive\", \"hot_standby\", or \"logical\""))); (errmsg("WAL archival cannot be enabled when wal_level is \"minimal\"")));
if (max_wal_senders > 0 && wal_level == WAL_LEVEL_MINIMAL) if (max_wal_senders > 0 && wal_level == WAL_LEVEL_MINIMAL)
ereport(ERROR, ereport(ERROR,
(errmsg("WAL streaming (max_wal_senders > 0) requires wal_level \"archive\", \"hot_standby\", or \"logical\""))); (errmsg("WAL streaming (max_wal_senders > 0) requires wal_level \"archive\", \"hot_standby\", or \"logical\"")));
...@@ -1645,13 +1645,21 @@ ServerLoop(void) ...@@ -1645,13 +1645,21 @@ ServerLoop(void)
start_autovac_launcher = false; /* signal processed */ start_autovac_launcher = false; /* signal processed */
} }
/* If we have lost the archiver, try to start a new one */ /*
if (XLogArchivingActive() && PgArchPID == 0 && pmState == PM_RUN) * If we have lost the archiver, try to start a new one.
PgArchPID = pgarch_start(); *
* If WAL archiving is enabled always, we try to start a new archiver
/* If we have lost the stats collector, try to start a new one */ * even during recovery.
if (PgStatPID == 0 && pmState == PM_RUN) */
PgStatPID = pgstat_start(); if (PgArchPID == 0 && wal_level >= WAL_LEVEL_ARCHIVE)
{
if ((pmState == PM_RUN && XLogArchiveMode > ARCHIVE_MODE_OFF) ||
((pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY) &&
XLogArchiveMode == ARCHIVE_MODE_ALWAYS))
{
PgArchPID = pgarch_start();
}
}
/* If we need to signal the autovacuum launcher, do so now */ /* If we need to signal the autovacuum launcher, do so now */
if (avlauncher_needs_signal) if (avlauncher_needs_signal)
...@@ -4807,6 +4815,17 @@ sigusr1_handler(SIGNAL_ARGS) ...@@ -4807,6 +4815,17 @@ sigusr1_handler(SIGNAL_ARGS)
Assert(BgWriterPID == 0); Assert(BgWriterPID == 0);
BgWriterPID = StartBackgroundWriter(); BgWriterPID = StartBackgroundWriter();
/*
* Start the archiver if we're responsible for (re-)archiving received
* files.
*/
Assert(PgArchPID == 0);
if (wal_level >= WAL_LEVEL_ARCHIVE &&
XLogArchiveMode == ARCHIVE_MODE_ALWAYS)
{
PgArchPID = pgarch_start();
}
pmState = PM_RECOVERY; pmState = PM_RECOVERY;
} }
if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY) && if (CheckPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY) &&
......
...@@ -540,7 +540,10 @@ WalReceiverMain(void) ...@@ -540,7 +540,10 @@ WalReceiverMain(void)
* being archived later. * being archived later.
*/ */
XLogFileName(xlogfname, recvFileTLI, recvSegNo); XLogFileName(xlogfname, recvFileTLI, recvSegNo);
XLogArchiveForceDone(xlogfname); if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS)
XLogArchiveForceDone(xlogfname);
else
XLogArchiveNotify(xlogfname);
} }
recvFile = -1; recvFile = -1;
...@@ -897,7 +900,10 @@ XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr) ...@@ -897,7 +900,10 @@ XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr)
* from being archived later. * from being archived later.
*/ */
XLogFileName(xlogfname, recvFileTLI, recvSegNo); XLogFileName(xlogfname, recvFileTLI, recvSegNo);
XLogArchiveForceDone(xlogfname); if (XLogArchiveMode != ARCHIVE_MODE_ALWAYS)
XLogArchiveForceDone(xlogfname);
else
XLogArchiveNotify(xlogfname);
} }
recvFile = -1; recvFile = -1;
......
...@@ -396,6 +396,7 @@ static const struct config_enum_entry row_security_options[] = { ...@@ -396,6 +396,7 @@ static const struct config_enum_entry row_security_options[] = {
* Options for enum values stored in other modules * Options for enum values stored in other modules
*/ */
extern const struct config_enum_entry wal_level_options[]; extern const struct config_enum_entry wal_level_options[];
extern const struct config_enum_entry archive_mode_options[];
extern const struct config_enum_entry sync_method_options[]; extern const struct config_enum_entry sync_method_options[];
extern const struct config_enum_entry dynamic_shared_memory_options[]; extern const struct config_enum_entry dynamic_shared_memory_options[];
...@@ -1529,16 +1530,6 @@ static struct config_bool ConfigureNamesBool[] = ...@@ -1529,16 +1530,6 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL NULL, NULL, NULL
}, },
{
{"archive_mode", PGC_POSTMASTER, WAL_ARCHIVING,
gettext_noop("Allows archiving of WAL files using archive_command."),
NULL
},
&XLogArchiveMode,
false,
NULL, NULL, NULL
},
{ {
{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY, {"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
gettext_noop("Allows connections and queries during recovery."), gettext_noop("Allows connections and queries during recovery."),
...@@ -3551,6 +3542,16 @@ static struct config_enum ConfigureNamesEnum[] = ...@@ -3551,6 +3542,16 @@ static struct config_enum ConfigureNamesEnum[] =
NULL, assign_synchronous_commit, NULL NULL, assign_synchronous_commit, NULL
}, },
{
{"archive_mode", PGC_POSTMASTER, WAL_ARCHIVING,
gettext_noop("Allows archiving of WAL files using archive_command."),
NULL
},
&XLogArchiveMode,
ARCHIVE_MODE_OFF, archive_mode_options,
NULL, NULL, NULL
},
{ {
{"trace_recovery_messages", PGC_SIGHUP, DEVELOPER_OPTIONS, {"trace_recovery_messages", PGC_SIGHUP, DEVELOPER_OPTIONS,
gettext_noop("Enables logging of recovery-related debugging information."), gettext_noop("Enables logging of recovery-related debugging information."),
......
...@@ -206,7 +206,7 @@ ...@@ -206,7 +206,7 @@
# - Archiving - # - Archiving -
#archive_mode = off # allows archiving to be done #archive_mode = off # enables archiving; off, on, or always
# (change requires restart) # (change requires restart)
#archive_command = '' # command to use to archive a logfile segment #archive_command = '' # command to use to archive a logfile segment
# placeholders: %p = path of file to archive # placeholders: %p = path of file to archive
......
...@@ -98,7 +98,6 @@ extern int wal_keep_segments; ...@@ -98,7 +98,6 @@ extern int wal_keep_segments;
extern int XLOGbuffers; extern int XLOGbuffers;
extern int XLogArchiveTimeout; extern int XLogArchiveTimeout;
extern int wal_retrieve_retry_interval; extern int wal_retrieve_retry_interval;
extern bool XLogArchiveMode;
extern char *XLogArchiveCommand; extern char *XLogArchiveCommand;
extern bool EnableHotStandby; extern bool EnableHotStandby;
extern bool fullPageWrites; extern bool fullPageWrites;
...@@ -108,6 +107,15 @@ extern bool log_checkpoints; ...@@ -108,6 +107,15 @@ extern bool log_checkpoints;
extern int CheckPointSegments; extern int CheckPointSegments;
/* Archive modes */
typedef enum ArchiveMode
{
ARCHIVE_MODE_OFF = 0, /* disabled */
ARCHIVE_MODE_ON, /* enabled while server is running normally */
ARCHIVE_MODE_ALWAYS /* enabled always (even during recovery) */
} ArchiveMode;
extern int XLogArchiveMode;
/* WAL levels */ /* WAL levels */
typedef enum WalLevel typedef enum WalLevel
{ {
...@@ -118,7 +126,8 @@ typedef enum WalLevel ...@@ -118,7 +126,8 @@ typedef enum WalLevel
} WalLevel; } WalLevel;
extern int wal_level; extern int wal_level;
#define XLogArchivingActive() (XLogArchiveMode && wal_level >= WAL_LEVEL_ARCHIVE) #define XLogArchivingActive() \
(XLogArchiveMode > ARCHIVE_MODE_OFF && wal_level >= WAL_LEVEL_ARCHIVE)
#define XLogArchiveCommandSet() (XLogArchiveCommand[0] != '\0') #define XLogArchiveCommandSet() (XLogArchiveCommand[0] != '\0')
/* /*
......
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