Hi. Here's an updated version of my patch, which now applies on top of the patch that Simon posted earlier (recovery_startup_r10_api.v1b.patch).
A few notes: 1. I merged in recovery_target_lsn as a new GUC setting. 2. I fixed various minor nits in the earlier patch, not worth mentioning individually. 3. I haven't added back trigger_file, because Simon's patch removes it. I can add it back in separately after discussion (otherwise Simon's and my patches will conflict). 4. I've tested this to the extent that setting things in postgresql.conf works, and recovery.conf is still read if it exists, and so on. One open issue is the various assign_recovery_target_xxx functions, which Michael noted in his earlier review: +static void +assign_recovery_target_xid(const char *newval, void *extra) +{ + recovery_target_xid = *((TransactionId *) extra); + + if (recovery_target_xid != InvalidTransactionId) + recovery_target = RECOVERY_TARGET_XID; + else if (recovery_target_name && *recovery_target_name) + recovery_target = RECOVERY_TARGET_NAME; + else if (recovery_target_time != 0) + recovery_target = RECOVERY_TARGET_TIME; + else if (recovery_target_lsn != 0) + recovery_target = RECOVERY_TARGET_LSN; + else + recovery_target = RECOVERY_TARGET_UNSET; +} (Note how recovery_target_lsn caused this—and three other functions besides—to grow an extra branch.) I don't like this code, but I'm not yet sure what to replace it with. I think we should address the underlying problem—that the UI doesn't map cleanly to what the code wants. There's been some discussion about this earlier, but not any consensus that I could see. Do we want something like this (easy to implement and document, perhaps not especially convenient to use): recovery_target = 'xid' # or 'time'/'name'/'lsn'/'immediate' recovery_target_xid = xxx? # the only setting we care about now recovery_target_otherthings = parsed_but_ignored Or something like this (a bit harder to implement): recovery_target = 'xid:xxx' # or 'time:xxx' etc. Alternatively, the do-nothing option is to move the tests from guc.c to StartupXLOG and do it only once in some defined order (which would also break the current last-mention-wins behaviour). Thoughts? (I've added Fujii to the Cc: list, in case he has any comments, since this is based on his earlier patch.) -- Abhijit
commit c20d735648f5ea867fda5afc499a63d3877536a3 Author: Abhijit Menon-Sen <a...@2ndquadrant.com> Date: Thu Sep 1 09:01:04 2016 +0530 Convert recovery.conf settings to GUCs Based on unite_recoveryconf_postgresqlconf_v3.patch by Fujii Masao. diff --git a/contrib/pg_standby/pg_standby.c b/contrib/pg_standby/pg_standby.c index e4136f9..8fcb85c 100644 --- a/contrib/pg_standby/pg_standby.c +++ b/contrib/pg_standby/pg_standby.c @@ -520,7 +520,7 @@ usage(void) printf(" -w MAXWAITTIME max seconds to wait for a file (0=no limit) (default=0)\n"); printf(" -?, --help show this help, then exit\n"); printf("\n" - "Main intended use as restore_command in recovery.conf:\n" + "Main intended use as restore_command in postgresql.conf:\n" " restore_command = 'pg_standby [OPTION]... ARCHIVELOCATION %%f %%p %%r'\n" "e.g.\n" " restore_command = 'pg_standby /mnt/server/archiverdir %%f %%p %%r'\n"); diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml index 0f09d82..f43e41e 100644 --- a/doc/src/sgml/backup.sgml +++ b/doc/src/sgml/backup.sgml @@ -1174,8 +1174,15 @@ SELECT pg_stop_backup(); </listitem> <listitem> <para> - Create a recovery command file <filename>recovery.conf</> in the cluster - data directory (see <xref linkend="recovery-config">). You might + Set up recovery parameters in <filename>postgresql.conf</> (see + <xref linkend="runtime-config-wal-archive-recovery"> and + <xref linkend="runtime-config-wal-recovery-target">). + </para> + </listitem> + <listitem> + <para> + Create a recovery trigger file <filename>recovery.trigger</> + in the cluster data directory. You might also want to temporarily modify <filename>pg_hba.conf</> to prevent ordinary users from connecting until you are sure the recovery was successful. </para> @@ -1187,7 +1194,7 @@ SELECT pg_stop_backup(); recovery be terminated because of an external error, the server can simply be restarted and it will continue recovery. Upon completion of the recovery process, the server will rename - <filename>recovery.conf</> to <filename>recovery.done</> (to prevent + <filename>recovery.trigger</> to <filename>recovery.done</> (to prevent accidentally re-entering recovery mode later) and then commence normal database operations. </para> @@ -1203,12 +1210,11 @@ SELECT pg_stop_backup(); </para> <para> - The key part of all this is to set up a recovery configuration file that - describes how you want to recover and how far the recovery should - run. You can use <filename>recovery.conf.sample</> (normally - located in the installation's <filename>share/</> directory) as a - prototype. The one thing that you absolutely must specify in - <filename>recovery.conf</> is the <varname>restore_command</>, + The key part of all this is to set up recovery parameters that + specify how you want to recover and how far the recovery should + run. The one thing that you absolutely must specify in + <filename>postgresql.conf</> to recover from the backup is + the <varname>restore_command</>, which tells <productname>PostgreSQL</> how to retrieve archived WAL file segments. Like the <varname>archive_command</>, this is a shell command string. It can contain <literal>%f</>, which is @@ -1270,7 +1276,7 @@ restore_command = 'cp /mnt/server/archivedir/%f %p' <para> If you want to recover to some previous point in time (say, right before the junior DBA dropped your main transaction table), just specify the - required <link linkend="recovery-target-settings">stopping point</link> in <filename>recovery.conf</>. You can specify + required <link linkend="recovery-target-settings">stopping point</link> in <filename>postgresql.conf</>. You can specify the stop point, known as the <quote>recovery target</>, either by date/time, named restore point or by completion of a specific transaction ID. As of this writing only the date/time and named restore point options @@ -1367,8 +1373,9 @@ restore_command = 'cp /mnt/server/archivedir/%f %p' The default behavior of recovery is to recover along the same timeline that was current when the base backup was taken. If you wish to recover into some child timeline (that is, you want to return to some state that - was itself generated after a recovery attempt), you need to specify the - target timeline ID in <filename>recovery.conf</>. You cannot recover into + was itself generated after a recovery attempt), you need to set + <xref linkend="guc-recovery-target-timeline"> to the + target timeline ID in <filename>postgresql.conf</>. You cannot recover into timelines that branched off earlier than the base backup. </para> </sect2> diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 7c483c6..c277ad8 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -2840,6 +2840,348 @@ include_dir 'conf.d' </variablelist> </sect2> + <sect2 id="runtime-config-wal-archive-recovery"> + <title>Archive Recovery</title> + + <variablelist> + <varlistentry id="guc-restore-command" xreflabel="restore_command"> + <term><varname>restore_command</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>restore_command</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + The shell command to execute to retrieve an archived segment of + the WAL file series. This parameter is required for archive recovery, + but optional for streaming replication. + Any <literal>%f</> in the string is + replaced by the name of the file to retrieve from the archive, + and any <literal>%p</> is replaced by the copy destination path name + on the server. + (The path name is relative to the current working directory, + i.e., the cluster's data directory.) + Any <literal>%r</> is replaced by the name of the file containing the + last valid restart point. That is the earliest file that must be kept + to allow a restore to be restartable, so this information can be used + to truncate the archive to just the minimum required to support + restarting from the current restore. <literal>%r</> is typically only + used by warm-standby configurations + (see <xref linkend="warm-standby">). + Write <literal>%%</> to embed an actual <literal>%</> character. + </para> + <para> + It is important for the command to return a zero exit status + only if it succeeds. The command <emphasis>will</> be asked for file + names that are not present in the archive; it must return nonzero + when so asked. Examples: +<programlisting> +restore_command = 'cp /mnt/server/archivedir/%f "%p"' +restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows +</programlisting> + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect during archive + recovery or in standby mode. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-archive-cleanup-command" xreflabel="archive_cleanup_command"> + <term><varname>archive_cleanup_command</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>archive_cleanup_command</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + The shell command that will be executed at every restartpoint. + The purpose of <varname>archive_cleanup_command</> is to + provide a mechanism for cleaning up old archived WAL files that + are no longer needed by the standby server. + Any <literal>%r</> is replaced by the name of the file containing the + last valid restart point. + That is the earliest file that must be <emphasis>kept</> to allow a + restore to be restartable, and so all files earlier than <literal>%r</> + may be safely removed. + This information can be used to truncate the archive to just the + minimum required to support restart from the current restore. + The <xref linkend="pgarchivecleanup"> module + is often used in <varname>archive_cleanup_command</> for + single-standby configurations, for example: +<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting> + Note however that if multiple standby servers are restoring from the + same archive directory, you will need to ensure that you do not delete + WAL files until they are no longer needed by any of the servers. + <varname>archive_cleanup_command</> would typically be used in a + warm-standby configuration (see <xref linkend="warm-standby">). + Write <literal>%%</> to embed an actual <literal>%</> character in the + command. + </para> + <para> + If the command returns a non-zero exit status then a WARNING log + message will be written. + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect during archive + recovery or in standby mode. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-recovery-end-command" xreflabel="recovery_end_command"> + <term><varname>recovery_end_command</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>recovery_end_command</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + The shell command that will be executed once only + at the end of recovery. This parameter is optional. The purpose of the + <varname>recovery_end_command</> is to provide a mechanism for cleanup + following replication or recovery. + Any <literal>%r</> is replaced by the name of the file containing the + last valid restart point, like in <varname>archive_cleanup_command</>. + </para> + <para> + If the command returns a non-zero exit status then a WARNING log + message will be written and the database will proceed to start up + anyway. An exception is that if the command was terminated by a + signal, the database will not proceed with startup. + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect during archive + recovery or in standby mode. + </para> + </listitem> + </varlistentry> + </variablelist> + </sect2> + + <sect2 id="runtime-config-wal-recovery-target"> + <title>Recovery Target</title> + + <para> + By default, recovery will recover to the end of the WAL log. The + following parameters can be used to specify an earlier stopping point. + At most one of <varname>recovery_target</>, + <varname>recovery_target_lsn</>, <varname>recovery_target_name</>, + <varname>recovery_target_time</>, or <varname>recovery_target_xid</> + can be used; if more than one of these is specified in the configuration + file, the last entry will be used. + </para> + + <variablelist> + <varlistentry id="recovery-target" xreflabel="recovery_target"> + <term><varname>recovery_target</varname><literal> = 'immediate'</literal> + <indexterm> + <primary><varname>recovery_target</> recovery parameter</primary> + </indexterm> + </term> + <listitem> + <para> + This parameter specifies that recovery should end as soon as a + consistent state is reached, i.e. as early as possible. When restoring + from an online backup, this means the point where taking the backup + ended. + </para> + <para> + Technically, this is a string parameter, but <literal>'immediate'</> + is currently the only allowed value. + </para> + </listitem> + </varlistentry> + + <variablelist> + <varlistentry id="guc-recovery-target-name" xreflabel="recovery_target_name"> + <term><varname>recovery_target_name</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>recovery_target_name</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the named restore point, created with + <function>pg_create_restore_point()</> to which recovery will proceed. + At most one of <varname>recovery_target_name</>, + <varname>recovery_target_time</> or + <varname>recovery_target_xid</> can be specified. The default + value is an empty string, which will recover to the end of the WAL log. + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect during archive + recovery or in standby mode. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-recovery-target-time" xreflabel="recovery_target_time"> + <term><varname>recovery_target_time</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>recovery_target_time</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the time stamp up to which recovery will proceed. + This parameter must be specified in the date/time format + (see <xref linkend="datatype-datetime-input"> for details). + At most one of <varname>recovery_target_time</>, + <varname>recovery_target_name</> or + <varname>recovery_target_xid</> can be specified. + The default value is an empty string, which will recover to + the end of the WAL log. The precise stopping point is also + influenced by <varname>recovery_target_inclusive</>. + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect during archive + recovery or in standby mode. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-recovery-target-xid" xreflabel="recovery_target_xid"> + <term><varname>recovery_target_xid</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>recovery_target_xid</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the transaction ID up to which recovery will proceed. + Keep in mind that while transaction IDs are assigned sequentially + at transaction start, transactions can complete in a different + numeric order. The transactions that will be recovered are + those that committed before (and optionally including) + the specified one. At most one of <varname>recovery_target_xid</>, + <varname>recovery_target_name</> or + <varname>recovery_target_time</> can be specified. + The default value is an empty string, which will recover to the end of + the WAL log. The precise stopping point is also influenced by + <varname>recovery_target_inclusive</>. + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect during archive + recovery or in standby mode. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-recovery-target-inclusive" xreflabel="recovery_target_inclusive"> + <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>)</term> + <indexterm> + <primary><varname>recovery_target_inclusive</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies whether we stop just after the specified recovery target + (<literal>on</>), or just before the recovery target (<literal>off</>). + Applies to both <varname>recovery_target_time</> + and <varname>recovery_target_xid</>, whichever one is + specified for this recovery. This indicates whether transactions + having exactly the target commit time or ID, respectively, will + be included in the recovery. Default is <literal>on</>. + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect during archive + recovery or in standby mode. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-recovery-target-timeline" xreflabel="recovery_target_timeline"> + <term><varname>recovery_target_timeline</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>recovery_target_timeline</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies recovering into a particular timeline. The default value is + an empty string, which will recover along the same timeline that was + current when the base backup was taken. Setting this to + <literal>latest</> recovers to the latest timeline found in the archive, + which is useful in a standby server. Other than that you only need to + set this parameter in complex re-recovery situations, where you need + to return to a state that itself was reached after a point-in-time + recovery. See <xref linkend="backup-timelines"> for discussion. + </para> + <para> + This parameter can only be set at server start. It only has effect + during archive recovery or in standby mode. + </para> + </listitem> + </varlistentry> + + <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn"> + <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>) + <indexterm> + <primary><varname>recovery_target_lsn</> recovery parameter</primary> + </indexterm> + </term> + <listitem> + <para> + This parameter specifies the LSN of the transaction log location up + to which recovery will proceed. The precise stopping point is also + influenced by <xref linkend="recovery-target-inclusive">. This + parameter is parsed using the system data type + <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-recovery-target-action" xreflabel="recovery_target_action"> + <term><varname>recovery_target_action</varname> (<type>enum</type>) + <indexterm> + <primary><varname>recovery_target_action</> recovery parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Specifies what action the server should take once the recovery target is + reached. The default is <literal>pause</>, which means recovery will + be paused. <literal>promote</> means the recovery process will finish + and the server will start to accept connections. + Finally <literal>shutdown</> will stop the server after reaching the + recovery target. + </para> + <para> + The intended use of the <literal>pause</> setting is to allow queries + to be executed against the database to check if this recovery target + is the most desirable point for recovery. + The paused state can be resumed by + using <function>pg_xlog_replay_resume()</> (see + <xref linkend="functions-recovery-control-table">), which then + causes recovery to end. If this recovery target is not the + desired stopping point, then shut down the server, change the + recovery target settings to a later target and restart to + continue recovery. + </para> + <para> + The <literal>shutdown</> setting is useful to have the instance ready + at the exact replay point desired. The instance will still be able to + replay more WAL records (and in fact will have to replay WAL records + since the last checkpoint next time it is started). + </para> + <para> + Note that because <filename>recovery.trigger</> will not be renamed when + <varname>recovery_target_action</> is set to <literal>shutdown</>, + any subsequent start will end with immediate shutdown unless the + configuration is changed or the <filename>recovery.trigger</> file is + removed manually. + </para> + <para> + This setting has no effect if no recovery target is set. + If <xref linkend="guc-hot-standby"> is not enabled, a setting of + <literal>pause</> will act the same as <literal>shutdown</>. + </para> + </listitem> + </varlistentry> + + </variablelist> + </sect2> + </sect1> <sect1 id="runtime-config-replication"> @@ -3143,6 +3485,93 @@ include_dir 'conf.d' <variablelist> + <varlistentry id="guc-standby-mode" xreflabel="standby_mode"> + <term><varname>standby_mode</varname> (<type>boolean</type>)</term> + <indexterm> + <primary><varname>standby_mode</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies whether to start the <productname>PostgreSQL</> server as + a standby when recovery trigger file <filename>recovery.trigger</> exists. + The default value is <literal>off</>. + If this parameter is <literal>on</>, the server will not + stop recovery when the end of archived WAL is reached, + but will keep trying to continue recovery by fetching new WAL segments + using <varname>restore_command</> and/or by connecting to + the primary server as specified by the <varname>primary_conninfo</> + setting. + </para> + <para> + This parameter can only be set at server start. It only has effect + if recovery trigger file <filename>recovery.trigger</> exists. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-primary-conninfo" xreflabel="primary_conninfo"> + <term><varname>primary_conninfo</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>primary_conninfo</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies a connection string to be used for the standby server + to connect with the primary. This string is in the format + accepted by the libpq <function>PQconnectdb</function> function, + described in <xref linkend="libpq-connect">. If any option is + unspecified in this string, then the corresponding environment + variable (see <xref linkend="libpq-envars">) is checked. If the + environment variable is not set either, then defaults are used. + If this parameter is an empty string (the default), no attempt is + made to connect to the master. + </para> + <para> + The connection string should specify the host name (or address) + of the primary server, as well as the port number if it is not + the same as the standby server's default. + Also specify a user name corresponding to a role that has the + <literal>REPLICATION</> and <literal>LOGIN</> privileges on the + primary (see + <xref linkend="streaming-replication-authentication">). + A password needs to be provided too, if the primary demands password + authentication. It can be provided in the + <varname>primary_conninfo</varname> string, or in a separate + <filename>~/.pgpass</> file on the standby server (use + <literal>replication</> as the database name). + Do not specify a database name in the + <varname>primary_conninfo</varname> string. + </para> + <para> + This parameter can only be set in the <filename>postgresql.conf</> + file or on the server command line. It only has effect in standby mode. + </para> + <para> + If this parameter is changed while replication is in progress, + the standby terminates replication, and then tries to restart + replication with new setting. + </para> + </listitem> + </varlistentry> + + <varlistentry id="primary-slot-name" xreflabel="primary_slot_name"> + <term><varname>primary_slot_name</varname> (<type>string</type>) + <indexterm> + <primary><varname>primary_slot_name</> recovery parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Optionally specifies an existing replication slot to be used when + connecting to the primary via streaming replication to control + resource removal on the upstream node + (see <xref linkend="streaming-replication-slots">). + This setting has no effect if <varname>primary_conninfo</> is not + set. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-hot-standby" xreflabel="hot_standby"> <term><varname>hot_standby</varname> (<type>boolean</type>) <indexterm> @@ -3223,6 +3652,57 @@ include_dir 'conf.d' </listitem> </varlistentry> + <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay"> + <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>) + <indexterm> + <primary><varname>recovery_min_apply_delay</> recovery parameter</primary> + </indexterm> + </term> + <listitem> + <para> + By default, a standby server restores WAL records from the + primary as soon as possible. It may be useful to have a time-delayed + copy of the data, offering opportunities to correct data loss errors. + This parameter allows you to delay recovery by a fixed period of time, + measured in milliseconds if no unit is specified. For example, if + you set this parameter to <literal>5min</literal>, the standby will + replay each transaction commit only when the system time on the standby + is at least five minutes past the commit time reported by the master. + </para> + <para> + It is possible that the replication delay between servers exceeds the + value of this parameter, in which case no delay is added. + Note that the delay is calculated between the WAL time stamp as written + on master and the current time on the standby. Delays in transfer + because of network lag or cascading replication configurations + may reduce the actual wait time significantly. If the system + clocks on master and standby are not synchronized, this may lead to + recovery applying records earlier than expected; but that is not a + major issue because useful settings of this parameter are much larger + than typical time deviations between servers. + </para> + <para> + The delay occurs only on WAL records for transaction commits. + Other records are replayed as quickly as possible, which + is not a problem because MVCC visibility rules ensure their effects + are not visible until the corresponding commit record is applied. + </para> + <para> + The delay occurs once the database in recovery has reached a consistent + state, until the standby is promoted or triggered. After that the standby + will end recovery without further waiting. + </para> + <para> + This parameter is intended for use with streaming replication deployments; + however, if the parameter is specified it will be honored in all cases. + Synchronous replication is not affected by this setting because there is + not yet any setting to request synchronous apply of transaction commits. + <varname>hot_standby_feedback</> will be delayed by use of this feature + which could lead to bloat on the master; use both together with care. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-wal-receiver-status-interval" xreflabel="wal_receiver_status_interval"> <term><varname>wal_receiver_status_interval</varname> (<type>integer</type>) <indexterm> diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml index 4383711..e9a6236 100644 --- a/doc/src/sgml/filelist.sgml +++ b/doc/src/sgml/filelist.sgml @@ -44,7 +44,6 @@ <!ENTITY manage-ag SYSTEM "manage-ag.sgml"> <!ENTITY monitoring SYSTEM "monitoring.sgml"> <!ENTITY regress SYSTEM "regress.sgml"> -<!ENTITY recovery-config SYSTEM "recovery-config.sgml"> <!ENTITY runtime SYSTEM "runtime.sgml"> <!ENTITY config SYSTEM "config.sgml"> <!ENTITY user-manag SYSTEM "user-manag.sgml"> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 5148095..1a01a59 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -18092,7 +18092,7 @@ postgres=# select pg_start_backup('label_goes_here'); <function>pg_create_restore_point</> creates a named transaction log record that can be used as recovery target, and returns the corresponding transaction log location. The given name can then be used with - <xref linkend="recovery-target-name"> to specify the point up to which + <xref linkend="guc-recovery-target-name"> to specify the point up to which recovery will proceed. Avoid creating multiple restore points with the same name, since recovery will stop at the first one whose name matches the recovery target. diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml index 06f49db..d2fe0c3 100644 --- a/doc/src/sgml/high-availability.sgml +++ b/doc/src/sgml/high-availability.sgml @@ -591,7 +591,7 @@ protocol to make nodes agree on a serializable transactional order. <para> In standby mode, the server continuously applies WAL received from the master server. The standby server can read WAL from a WAL archive - (see <xref linkend="restore-command">) or directly from the master + (see <xref linkend="guc-restore-command">) or directly from the master over a TCP connection (streaming replication). The standby server will also attempt to restore any WAL found in the standby cluster's <filename>pg_xlog</> directory. That typically happens after a server @@ -660,8 +660,8 @@ protocol to make nodes agree on a serializable transactional order. <para> To set up the standby server, restore the base backup taken from primary server (see <xref linkend="backup-pitr-recovery">). Create a recovery - command file <filename>recovery.conf</> in the standby's cluster data - directory, and turn on <varname>standby_mode</>. Set + trigger file <filename>recovery.trigger</> in the standby's cluster data + directory. Turn on <varname>standby_mode</> and set <varname>restore_command</> to a simple command to copy files from the WAL archive. If you plan to have multiple standby servers for high availability purposes, set <varname>recovery_target_timeline</> to @@ -697,7 +697,7 @@ protocol to make nodes agree on a serializable transactional order. <para> If you're using a WAL archive, its size can be minimized using the <xref - linkend="archive-cleanup-command"> parameter to remove files that are no + linkend="guc-archive-cleanup-command"> parameter to remove files that are no longer required by the standby server. The <application>pg_archivecleanup</> utility is designed specifically to be used with <varname>archive_cleanup_command</> in typical single-standby @@ -708,7 +708,7 @@ protocol to make nodes agree on a serializable transactional order. </para> <para> - A simple example of a <filename>recovery.conf</> is: + A simple example of standby settings is: <programlisting> standby_mode = 'on' primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' @@ -766,8 +766,8 @@ archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r' To use streaming replication, set up a file-based log-shipping standby server as described in <xref linkend="warm-standby">. The step that turns a file-based log-shipping standby into streaming replication - standby is setting <varname>primary_conninfo</> setting in the - <filename>recovery.conf</> file to point to the primary server. Set + standby is setting <varname>primary_conninfo</> to + point to the primary server. Set <xref linkend="guc-listen-addresses"> and authentication options (see <filename>pg_hba.conf</>) on the primary so that the standby server can connect to the <literal>replication</> pseudo-database on the primary @@ -827,15 +827,14 @@ host replication foo 192.168.1.100/32 md5 </para> <para> The host name and port number of the primary, connection user name, - and password are specified in the <filename>recovery.conf</> file. + and password are specified in <varname>primary_conninfo</>. The password can also be set in the <filename>~/.pgpass</> file on the standby (specify <literal>replication</> in the <replaceable>database</> field). For example, if the primary is running on host IP <literal>192.168.1.50</>, port <literal>5432</literal>, the account name for replication is <literal>foo</>, and the password is <literal>foopass</>, the administrator - can add the following line to the <filename>recovery.conf</> file on the - standby: + can set <varname>primary_conninfo</> on the standby like this: <programlisting> # The standby connects to the primary that is running on host 192.168.1.50 @@ -940,7 +939,7 @@ postgres=# SELECT * FROM pg_replication_slots; (1 row) </programlisting> To configure the standby to use this slot, <varname>primary_slot_name</> - should be configured in the standby's <filename>recovery.conf</>. + should be configured in the standby's <filename>postgresql.conf</>. Here is a simple example: <programlisting> standby_mode = 'on' @@ -1387,8 +1386,8 @@ synchronous_standby_names = '2 (s1, s2, s3)' <para> To trigger failover of a log-shipping standby server, run <command>pg_ctl promote</> or create a trigger - file with the file name and path specified by the <varname>trigger_file</> - setting in <filename>recovery.conf</>. If you're planning to use + file with the file name and path specified by the <varname>trigger_file</>. + If you're planning to use <command>pg_ctl promote</> to fail over, <varname>trigger_file</> is not required. If you're setting up the reporting servers that are only used to offload read-only queries from the primary, not for high @@ -1433,8 +1432,7 @@ synchronous_standby_names = '2 (s1, s2, s3)' The magic that makes the two loosely coupled servers work together is simply a <varname>restore_command</> used on the standby that, when asked for the next WAL file, waits for it to become available from - the primary. The <varname>restore_command</> is specified in the - <filename>recovery.conf</> file on the standby server. Normal recovery + the primary. Normal recovery processing would request a file from the WAL archive, reporting failure if the file was unavailable. For standby processing it is normal for the next WAL file to be unavailable, so the standby must wait for @@ -1521,8 +1519,14 @@ if (!triggered) </listitem> <listitem> <para> + Create a recovery trigger file <filename>recovery.trigger</> + in the standby's cluster data directory. + </para> + </listitem> + <listitem> + <para> Begin recovery on the standby server from the local WAL - archive, using a <filename>recovery.conf</> that specifies a + archive, specifying a <varname>restore_command</> that waits as described previously (see <xref linkend="backup-pitr-recovery">). </para> @@ -2018,9 +2022,9 @@ if (!triggered) <title>Administrator's Overview</title> <para> - If <varname>hot_standby</> is turned <literal>on</> in - <filename>postgresql.conf</> and there is a <filename>recovery.conf</> - file present, the server will run in Hot Standby mode. + If <varname>hot_standby</> is turned <literal>on</> + and there is a recovery trigger file + <filename>recovery.trigger</> present, the server will run in Hot Standby mode. However, it may take some time for Hot Standby connections to be allowed, because the server will not accept connections until it has completed sufficient recovery to provide a consistent state against which queries diff --git a/doc/src/sgml/pgstandby.sgml b/doc/src/sgml/pgstandby.sgml index 80c6f60..c9e6185 100644 --- a/doc/src/sgml/pgstandby.sgml +++ b/doc/src/sgml/pgstandby.sgml @@ -46,8 +46,8 @@ <para> To configure a standby - server to use <application>pg_standby</>, put this into its - <filename>recovery.conf</filename> configuration file: + server to use <application>pg_standby</>, specify + <xref linkend="guc-restore-command"> like this: <programlisting> restore_command = 'pg_standby <replaceable>archiveDir</> %f %p %r' </programlisting> diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml index 0346d36..c47bc6b 100644 --- a/doc/src/sgml/postgres.sgml +++ b/doc/src/sgml/postgres.sgml @@ -155,7 +155,6 @@ &maintenance; &backup; &high-availability; - &recovery-config; &monitoring; &diskusage; &wal; diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml deleted file mode 100644 index de3fb10..0000000 --- a/doc/src/sgml/recovery-config.sgml +++ /dev/null @@ -1,501 +0,0 @@ -<!-- doc/src/sgml/recovery-config.sgml --> - -<chapter id="recovery-config"> - <title>Recovery Configuration</title> - - <indexterm> - <primary>configuration</primary> - <secondary>of recovery</secondary> - <tertiary>of a standby server</tertiary> - </indexterm> - - <para> - This chapter describes the settings available in the - <filename>recovery.conf</><indexterm><primary>recovery.conf</></> - file. They apply only for the duration of the - recovery. They must be reset for any subsequent recovery you wish to - perform. They cannot be changed once recovery has begun. - </para> - - <para> - Settings in <filename>recovery.conf</> are specified in the format - <literal>name = 'value'</>. One parameter is specified per line. - Hash marks (<literal>#</literal>) designate the rest of the - line as a comment. To embed a single quote in a parameter - value, write two quotes (<literal>''</>). - </para> - - <para> - A sample file, <filename>share/recovery.conf.sample</>, - is provided in the installation's <filename>share/</> directory. - </para> - - <sect1 id="archive-recovery-settings"> - - <title>Archive Recovery Settings</title> - <variablelist> - - <varlistentry id="restore-command" xreflabel="restore_command"> - <term><varname>restore_command</varname> (<type>string</type>) - <indexterm> - <primary><varname>restore_command</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - The local shell command to execute to retrieve an archived segment of - the WAL file series. This parameter is required for archive recovery, - but optional for streaming replication. - Any <literal>%f</> in the string is - replaced by the name of the file to retrieve from the archive, - and any <literal>%p</> is replaced by the copy destination path name - on the server. - (The path name is relative to the current working directory, - i.e., the cluster's data directory.) - Any <literal>%r</> is replaced by the name of the file containing the - last valid restart point. That is the earliest file that must be kept - to allow a restore to be restartable, so this information can be used - to truncate the archive to just the minimum required to support - restarting from the current restore. <literal>%r</> is typically only - used by warm-standby configurations - (see <xref linkend="warm-standby">). - Write <literal>%%</> to embed an actual <literal>%</> character. - </para> - - <para> - It is important for the command to return a zero exit status - only if it succeeds. The command <emphasis>will</> be asked for file - names that are not present in the archive; it must return nonzero - when so asked. Examples: -<programlisting> -restore_command = 'cp /mnt/server/archivedir/%f "%p"' -restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows -</programlisting> - An exception is that if the command was terminated by a signal (other - than <systemitem>SIGTERM</systemitem>, which is used as part of a - database server shutdown) or an error by the shell (such as command - not found), then recovery will abort and the server will not start up. - </para> - </listitem> - </varlistentry> - - <varlistentry id="archive-cleanup-command" xreflabel="archive_cleanup_command"> - <term><varname>archive_cleanup_command</varname> (<type>string</type>) - <indexterm> - <primary><varname>archive_cleanup_command</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - This optional parameter specifies a shell command that will be executed - at every restartpoint. The purpose of - <varname>archive_cleanup_command</> is to provide a mechanism for - cleaning up old archived WAL files that are no longer needed by the - standby server. - Any <literal>%r</> is replaced by the name of the file containing the - last valid restart point. - That is the earliest file that must be <emphasis>kept</> to allow a - restore to be restartable, and so all files earlier than <literal>%r</> - may be safely removed. - This information can be used to truncate the archive to just the - minimum required to support restart from the current restore. - The <xref linkend="pgarchivecleanup"> module - is often used in <varname>archive_cleanup_command</> for - single-standby configurations, for example: -<programlisting>archive_cleanup_command = 'pg_archivecleanup /mnt/server/archivedir %r'</programlisting> - Note however that if multiple standby servers are restoring from the - same archive directory, you will need to ensure that you do not delete - WAL files until they are no longer needed by any of the servers. - <varname>archive_cleanup_command</> would typically be used in a - warm-standby configuration (see <xref linkend="warm-standby">). - Write <literal>%%</> to embed an actual <literal>%</> character in the - command. - </para> - <para> - If the command returns a nonzero exit status then a warning log - message will be written. An exception is that if the command was - terminated by a signal or an error by the shell (such as command not - found), a fatal error will be raised. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-end-command" xreflabel="recovery_end_command"> - <term><varname>recovery_end_command</varname> (<type>string</type>) - <indexterm> - <primary><varname>recovery_end_command</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - This parameter specifies a shell command that will be executed once only - at the end of recovery. This parameter is optional. The purpose of the - <varname>recovery_end_command</> is to provide a mechanism for cleanup - following replication or recovery. - Any <literal>%r</> is replaced by the name of the file containing the - last valid restart point, like in <xref linkend="archive-cleanup-command">. - </para> - <para> - If the command returns a nonzero exit status then a warning log - message will be written and the database will proceed to start up - anyway. An exception is that if the command was terminated by a - signal or an error by the shell (such as command not found), the - database will not proceed with startup. - </para> - </listitem> - </varlistentry> - - </variablelist> - - </sect1> - - <sect1 id="recovery-target-settings"> - - <title>Recovery Target Settings</title> - - <para> - By default, recovery will recover to the end of the WAL log. The - following parameters can be used to specify an earlier stopping point. - At most one of <varname>recovery_target</>, - <varname>recovery_target_lsn</>, <varname>recovery_target_name</>, - <varname>recovery_target_time</>, or <varname>recovery_target_xid</> - can be used; if more than one of these is specified in the configuration - file, the last entry will be used. - </para> - - <variablelist> - <varlistentry id="recovery-target" xreflabel="recovery_target"> - <term><varname>recovery_target</varname><literal> = 'immediate'</literal> - <indexterm> - <primary><varname>recovery_target</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - This parameter specifies that recovery should end as soon as a - consistent state is reached, i.e. as early as possible. When restoring - from an online backup, this means the point where taking the backup - ended. - </para> - <para> - Technically, this is a string parameter, but <literal>'immediate'</> - is currently the only allowed value. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-target-name" xreflabel="recovery_target_name"> - <term><varname>recovery_target_name</varname> (<type>string</type>) - <indexterm> - <primary><varname>recovery_target_name</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - This parameter specifies the named restore point (created with - <function>pg_create_restore_point()</>) to which recovery will proceed. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-target-time" xreflabel="recovery_target_time"> - <term><varname>recovery_target_time</varname> (<type>timestamp</type>) - <indexterm> - <primary><varname>recovery_target_time</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - This parameter specifies the time stamp up to which recovery - will proceed. - The precise stopping point is also influenced by - <xref linkend="recovery-target-inclusive">. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-target-xid" xreflabel="recovery_target_xid"> - <term><varname>recovery_target_xid</varname> (<type>string</type>) - <indexterm> - <primary><varname>recovery_target_xid</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - This parameter specifies the transaction ID up to which recovery - will proceed. Keep in mind - that while transaction IDs are assigned sequentially at transaction - start, transactions can complete in a different numeric order. - The transactions that will be recovered are those that committed - before (and optionally including) the specified one. - The precise stopping point is also influenced by - <xref linkend="recovery-target-inclusive">. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-target-lsn" xreflabel="recovery_target_lsn"> - <term><varname>recovery_target_lsn</varname> (<type>pg_lsn</type>) - <indexterm> - <primary><varname>recovery_target_lsn</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - This parameter specifies the LSN of the transaction log location up - to which recovery will proceed. The precise stopping point is also - influenced by <xref linkend="recovery-target-inclusive">. This - parameter is parsed using the system data type - <link linkend="datatype-pg-lsn"><type>pg_lsn</></link>. - </para> - </listitem> - </varlistentry> - </variablelist> - - <para> - The following options further specify the recovery target, and affect - what happens when the target is reached: - </para> - - <variablelist> - <varlistentry id="recovery-target-inclusive" - xreflabel="recovery_target_inclusive"> - <term><varname>recovery_target_inclusive</varname> (<type>boolean</type>) - <indexterm> - <primary><varname>recovery_target_inclusive</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - Specifies whether to stop just after the specified recovery target - (<literal>true</literal>), or just before the recovery target - (<literal>false</literal>). - Applies when either <xref linkend="recovery-target-time"> - or <xref linkend="recovery-target-xid"> is specified. - This setting controls whether transactions - having exactly the target commit time or ID, respectively, will - be included in the recovery. Default is <literal>true</>. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-target-timeline" - xreflabel="recovery_target_timeline"> - <term><varname>recovery_target_timeline</varname> (<type>string</type>) - <indexterm> - <primary><varname>recovery_target_timeline</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - Specifies recovering into a particular timeline. The default is - to recover along the same timeline that was current when the - base backup was taken. Setting this to <literal>latest</> recovers - to the latest timeline found in the archive, which is useful in - a standby server. Other than that you only need to set this parameter - in complex re-recovery situations, where you need to return to - a state that itself was reached after a point-in-time recovery. - See <xref linkend="backup-timelines"> for discussion. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-target-action" - xreflabel="recovery_target_action"> - <term><varname>recovery_target_action</varname> (<type>enum</type>) - <indexterm> - <primary><varname>recovery_target_action</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - Specifies what action the server should take once the recovery target is - reached. The default is <literal>pause</>, which means recovery will - be paused. <literal>promote</> means the recovery process will finish - and the server will start to accept connections. - Finally <literal>shutdown</> will stop the server after reaching the - recovery target. - </para> - <para> - The intended use of the <literal>pause</> setting is to allow queries - to be executed against the database to check if this recovery target - is the most desirable point for recovery. - The paused state can be resumed by - using <function>pg_xlog_replay_resume()</> (see - <xref linkend="functions-recovery-control-table">), which then - causes recovery to end. If this recovery target is not the - desired stopping point, then shut down the server, change the - recovery target settings to a later target and restart to - continue recovery. - </para> - <para> - The <literal>shutdown</> setting is useful to have the instance ready - at the exact replay point desired. The instance will still be able to - replay more WAL records (and in fact will have to replay WAL records - since the last checkpoint next time it is started). - </para> - <para> - Note that because <filename>recovery.conf</> will not be renamed when - <varname>recovery_target_action</> is set to <literal>shutdown</>, - any subsequent start will end with immediate shutdown unless the - configuration is changed or the <filename>recovery.conf</> file is - removed manually. - </para> - <para> - This setting has no effect if no recovery target is set. - If <xref linkend="guc-hot-standby"> is not enabled, a setting of - <literal>pause</> will act the same as <literal>shutdown</>. - </para> - </listitem> - </varlistentry> - - </variablelist> - </sect1> - - <sect1 id="standby-settings"> - - <title>Standby Server Settings</title> - <variablelist> - - <varlistentry id="standby-mode" xreflabel="standby_mode"> - <term><varname>standby_mode</varname> (<type>boolean</type>) - <indexterm> - <primary><varname>standby_mode</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - Specifies whether to start the <productname>PostgreSQL</> server as - a standby. If this parameter is <literal>on</>, the server will - not stop recovery when the end of archived WAL is reached, but - will keep trying to continue recovery by fetching new WAL segments - using <varname>restore_command</> - and/or by connecting to the primary server as specified by the - <varname>primary_conninfo</> setting. - </para> - </listitem> - </varlistentry> - <varlistentry id="primary-conninfo" xreflabel="primary_conninfo"> - <term><varname>primary_conninfo</varname> (<type>string</type>) - <indexterm> - <primary><varname>primary_conninfo</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - Specifies a connection string to be used for the standby server - to connect with the primary. This string is in the format - described in <xref linkend="libpq-connstring">. If any option is - unspecified in this string, then the corresponding environment - variable (see <xref linkend="libpq-envars">) is checked. If the - environment variable is not set either, then - defaults are used. - </para> - <para> - The connection string should specify the host name (or address) - of the primary server, as well as the port number if it is not - the same as the standby server's default. - Also specify a user name corresponding to a suitably-privileged role - on the primary (see - <xref linkend="streaming-replication-authentication">). - A password needs to be provided too, if the primary demands password - authentication. It can be provided in the - <varname>primary_conninfo</varname> string, or in a separate - <filename>~/.pgpass</> file on the standby server (use - <literal>replication</> as the database name). - Do not specify a database name in the - <varname>primary_conninfo</varname> string. - </para> - <para> - This setting has no effect if <varname>standby_mode</> is <literal>off</>. - </para> - </listitem> - </varlistentry> - <varlistentry id="primary-slot-name" xreflabel="primary_slot_name"> - <term><varname>primary_slot_name</varname> (<type>string</type>) - <indexterm> - <primary><varname>primary_slot_name</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - Optionally specifies an existing replication slot to be used when - connecting to the primary via streaming replication to control - resource removal on the upstream node - (see <xref linkend="streaming-replication-slots">). - This setting has no effect if <varname>primary_conninfo</> is not - set. - </para> - </listitem> - </varlistentry> - <varlistentry id="trigger-file" xreflabel="trigger_file"> - <term><varname>trigger_file</varname> (<type>string</type>) - <indexterm> - <primary><varname>trigger_file</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - Specifies a trigger file whose presence ends recovery in the - standby. Even if this value is not set, you can still promote - the standby using <command>pg_ctl promote</>. - This setting has no effect if <varname>standby_mode</> is <literal>off</>. - </para> - </listitem> - </varlistentry> - - <varlistentry id="recovery-min-apply-delay" xreflabel="recovery_min_apply_delay"> - <term><varname>recovery_min_apply_delay</varname> (<type>integer</type>) - <indexterm> - <primary><varname>recovery_min_apply_delay</> recovery parameter</primary> - </indexterm> - </term> - <listitem> - <para> - By default, a standby server restores WAL records from the - primary as soon as possible. It may be useful to have a time-delayed - copy of the data, offering opportunities to correct data loss errors. - This parameter allows you to delay recovery by a fixed period of time, - measured in milliseconds if no unit is specified. For example, if - you set this parameter to <literal>5min</literal>, the standby will - replay each transaction commit only when the system time on the standby - is at least five minutes past the commit time reported by the master. - </para> - <para> - It is possible that the replication delay between servers exceeds the - value of this parameter, in which case no delay is added. - Note that the delay is calculated between the WAL time stamp as written - on master and the current time on the standby. Delays in transfer - because of network lag or cascading replication configurations - may reduce the actual wait time significantly. If the system - clocks on master and standby are not synchronized, this may lead to - recovery applying records earlier than expected; but that is not a - major issue because useful settings of this parameter are much larger - than typical time deviations between servers. - </para> - <para> - The delay occurs only on WAL records for transaction commits. - Other records are replayed as quickly as possible, which - is not a problem because MVCC visibility rules ensure their effects - are not visible until the corresponding commit record is applied. - </para> - <para> - The delay occurs once the database in recovery has reached a consistent - state, until the standby is promoted or triggered. After that the standby - will end recovery without further waiting. - </para> - <para> - This parameter is intended for use with streaming replication deployments; - however, if the parameter is specified it will be honored in all cases. - Synchronous replication is not affected by this setting because there is - not yet any setting to request synchronous apply of transaction commits. - <varname>hot_standby_feedback</> will be delayed by use of this feature - which could lead to bloat on the master; use both together with care. - </para> - </listitem> - </varlistentry> - - </variablelist> - </sect1> - -</chapter> diff --git a/doc/src/sgml/ref/pgarchivecleanup.sgml b/doc/src/sgml/ref/pgarchivecleanup.sgml index abe01be..dc29854 100644 --- a/doc/src/sgml/ref/pgarchivecleanup.sgml +++ b/doc/src/sgml/ref/pgarchivecleanup.sgml @@ -38,8 +38,8 @@ <para> To configure a standby - server to use <application>pg_archivecleanup</>, put this into its - <filename>recovery.conf</filename> configuration file: + server to use <application>pg_archivecleanup</>, specify + <xref linkend="guc-archive-cleanup-command"> like this: <programlisting> archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r' </programlisting> @@ -47,7 +47,7 @@ archive_cleanup_command = 'pg_archivecleanup <replaceable>archivelocation</> %r' files should be removed. </para> <para> - When used within <xref linkend="archive-cleanup-command">, all WAL files + When used within <varname>archive_cleanup_command</>, all WAL files logically preceding the value of the <literal>%r</> argument will be removed from <replaceable>archivelocation</>. This minimizes the number of files that need to be retained, while preserving crash-restart capability. Use of diff --git a/doc/src/sgml/release-9.1.sgml b/doc/src/sgml/release-9.1.sgml index a66ca0d..afac09c 100644 --- a/doc/src/sgml/release-9.1.sgml +++ b/doc/src/sgml/release-9.1.sgml @@ -9601,7 +9601,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400 <para> These named restore points can be specified as recovery targets using the new <filename>recovery.conf</> setting - <link linkend="recovery-target-name"><varname>recovery_target_name</></link>. + <link linkend="guc-recovery-target-name"><varname>recovery_target_name</></link>. </para> </listitem> @@ -9633,8 +9633,7 @@ Branch: REL9_0_STABLE [9d6af7367] 2015-08-15 11:02:34 -0400 <listitem> <para> - Allow <link - linkend="recovery-config"><filename>recovery.conf</></link> + Allow <filename>recovery.conf</> to use the same quoting behavior as <filename>postgresql.conf</> (Dimitri Fontaine) </para> diff --git a/doc/src/sgml/release.sgml b/doc/src/sgml/release.sgml index 472c1f6..4237a66 100644 --- a/doc/src/sgml/release.sgml +++ b/doc/src/sgml/release.sgml @@ -6,7 +6,7 @@ Typical markup: &<> use & escapes PostgreSQL <productname> postgresql.conf, pg_hba.conf, - recovery.conf <filename> + recovery.trigger <filename> [A-Z][A-Z_ ]+[A-Z_] <command>, <literal>, <envar>, <acronym> [A-Za-z_][A-Za-z0-9_]+() <function> -[-A-Za-z_]+ <option> diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample index 7a16751..15ce784 100644 --- a/src/backend/access/transam/recovery.conf.sample +++ b/src/backend/access/transam/recovery.conf.sample @@ -2,23 +2,14 @@ # PostgreSQL recovery config file # ------------------------------- # -# Edit this file to provide the parameters that PostgreSQL needs to -# perform an archive recovery of a database, or to act as a replication -# standby. +# PostgreSQL 10.0 or later, recovery.conf is no longer used. Instead, +# specify all recovery parameters in postgresql.conf and create +# recovery.trigger to enter archive recovery or standby mode. # -# If "recovery.conf" is present in the PostgreSQL data directory, it is -# read on postmaster startup. After successful recovery, it is renamed -# to "recovery.done" to ensure that we do not accidentally re-enter -# archive recovery or standby mode. +# If you must use recovery.conf, specify "include directives" in +# postgresql.conf like this: # -# This file consists of lines of the form: -# -# name = value -# -# Comments are introduced with '#'. -# -# The complete list of option names and allowed values can be found -# in the PostgreSQL documentation. +# include 'recovery.conf' # #--------------------------------------------------------------------------- # ARCHIVE RECOVERY PARAMETERS @@ -131,14 +122,6 @@ # #primary_slot_name = '' # -# By default, a standby server keeps restoring XLOG records from the -# primary indefinitely. If you want to stop the standby mode, finish recovery -# and open the system in read/write mode, specify a path to a trigger file. -# The server will poll the trigger file path periodically and start as a -# primary server when it's found. -# -#trigger_file = '' -# # By default, a standby server restores XLOG records from the primary as # soon as possible. If you want to explicitly delay the replay of committed # transactions from the master, specify a minimum apply delay. For example, diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 2808423..183a7a6 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -77,7 +77,6 @@ extern uint32 bootstrap_data_checksum_version; /* File path names (all relative to $PGDATA) */ -#define RECOVERY_COMMAND_FILE "recovery.conf" #define RECOVERY_SIGNAL_FILE "recovery.trigger" #define PROMOTE_SIGNAL_FILE "promote.trigger" @@ -100,6 +99,25 @@ int wal_level = WAL_LEVEL_MINIMAL; int CommitDelay = 0; /* precommit delay in microseconds */ int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; +char *restore_command = NULL; +char *archive_cleanup_command = NULL; +char *recovery_end_command = NULL; +bool standby_mode = false; +char *primary_conninfo = NULL; +char *primary_slot_name = NULL; +int recovery_min_apply_delay = 0; +RecoveryTargetType recovery_target = RECOVERY_TARGET_UNSET; +TransactionId recovery_target_xid = InvalidTransactionId; +TimestampTz recovery_target_time = 0; +char *recovery_target_name = NULL; +XLogRecPtr recovery_target_lsn; +bool recovery_target_inclusive = true; +char *recovery_target_timeline_string = NULL; +char *recovery_target_action_string = NULL; +char *recovery_target_lsn_string = NULL; +char *recovery_target_string = NULL; +TimeLineID recovery_target_timeline = 0; +RecoveryTargetAction recovery_target_action = RECOVERY_TARGET_ACTION_PAUSE; #ifdef WAL_DEBUG bool XLOG_DEBUG = false; @@ -244,24 +262,10 @@ bool InArchiveRecovery = false; /* Was the last xlog file restored from archive, or local? */ static bool restoredFromArchive = false; -/* options taken from recovery.conf for archive recovery */ -char *recoveryRestoreCommand = NULL; -static char *recoveryEndCommand = NULL; -static char *archiveCleanupCommand = NULL; -static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; -static bool recoveryTargetInclusive = true; -static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; -static TransactionId recoveryTargetXid; -static TimestampTz recoveryTargetTime; -static char *recoveryTargetName; -static XLogRecPtr recoveryTargetLSN; -static int recovery_min_apply_delay = 0; static TimestampTz recoveryDelayUntilTime; /* options taken from recovery.conf for XLOG streaming */ static bool StandbyModeRequested = false; -static char *PrimaryConnInfo = NULL; -static char *PrimarySlotName = NULL; /* are we currently in standby mode? */ bool StandbyMode = false; @@ -270,12 +274,15 @@ bool StandbyMode = false; * if recoveryStopsBefore/After returns true, it saves information of the stop * point here */ +static RecoveryTargetType recoveryStopTarget; static TransactionId recoveryStopXid; static TimestampTz recoveryStopTime; static XLogRecPtr recoveryStopLSN; static char recoveryStopName[MAXFNAMELEN]; static bool recoveryStopAfter; +static TimestampTz recoveryDelayUntilTime; + /* * During normal operation, the only timeline we care about is ThisTimeLineID. * During recovery, however, things are more complicated. To simplify life @@ -577,10 +584,10 @@ typedef struct XLogCtlData TimeLineID PrevTimeLineID; /* - * archiveCleanupCommand is read from recovery.conf but needs to be in - * shared memory so that the checkpointer process can access it. + * archive_cleanup_command is read from recovery.conf but needs to + * be in shared memory so that the checkpointer process can access it. */ - char archiveCleanupCommand[MAXPGPATH]; + char archive_cleanup_command[MAXPGPATH]; /* * SharedRecoveryInProgress indicates if we're still in crash or archive @@ -783,7 +790,7 @@ static bool holdingAllLocks = false; static MemoryContext walDebugCxt = NULL; #endif -static void readRecoveryCommandFile(void); +static void CheckRecoveryParameters(void); static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog); static bool recoveryStopsBefore(XLogReaderState *record); static bool recoveryStopsAfter(XLogReaderState *record); @@ -3979,7 +3986,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode, /* * We only end up here without a message when XLogPageRead() * failed - in that case we already logged something. In - * StandbyMode that only happens if we have been triggered, so we + * standby_mode that only happens if we have been triggered, so we * shouldn't loop anymore in that case. */ if (errormsg) @@ -4037,8 +4044,6 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode, ereport(DEBUG1, (errmsg_internal("reached end of WAL in pg_xlog, entering archive recovery"))); InArchiveRecovery = true; - if (StandbyModeRequested) - StandbyMode = true; /* initialize minRecoveryPoint to this record */ LWLockAcquire(ControlFileLock, LW_EXCLUSIVE); @@ -4068,7 +4073,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode, } /* In standby mode, loop back to retry. Otherwise, give up. */ - if (StandbyMode && !CheckForPromoteTrigger()) + if (standby_mode && !CheckForPromoteTrigger()) continue; else return NULL; @@ -4929,305 +4934,58 @@ str_time(pg_time_t tnow) } /* - * See if there is a recovery command file (recovery.trigger), and if so - * read in parameters for archive recovery and XLOG streaming. - * - * The file is parsed using the main configuration parser. + * Check recovery parameters and determine recovery target timeline. */ static void -readRecoveryCommandFile(void) +CheckRecoveryParameters(void) { - FILE *fd; - TimeLineID rtli = 0; - bool rtliGiven = false; - ConfigVariable *item, - *head = NULL, - *tail = NULL; - bool recoveryTargetActionSet = false; - - - fd = AllocateFile(RECOVERY_COMMAND_FILE, "r"); - if (fd == NULL) - { - if (errno == ENOENT) - return; /* not there, so no archive recovery */ - ereport(FATAL, - (errcode_for_file_access(), - errmsg("could not open recovery command file \"%s\": %m", - RECOVERY_COMMAND_FILE))); - } - - /* - * Since we're asking ParseConfigFp() to report errors as FATAL, there's - * no need to check the return value. - */ - (void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail); - - FreeFile(fd); - - for (item = head; item; item = item->next) - { - if (strcmp(item->name, "restore_command") == 0) - { - recoveryRestoreCommand = pstrdup(item->value); - ereport(DEBUG2, - (errmsg_internal("restore_command = '%s'", - recoveryRestoreCommand))); - } - else if (strcmp(item->name, "recovery_end_command") == 0) - { - recoveryEndCommand = pstrdup(item->value); - ereport(DEBUG2, - (errmsg_internal("recovery_end_command = '%s'", - recoveryEndCommand))); - } - else if (strcmp(item->name, "archive_cleanup_command") == 0) - { - archiveCleanupCommand = pstrdup(item->value); - ereport(DEBUG2, - (errmsg_internal("archive_cleanup_command = '%s'", - archiveCleanupCommand))); - } - else if (strcmp(item->name, "recovery_target_action") == 0) - { - if (strcmp(item->value, "pause") == 0) - recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; - else if (strcmp(item->value, "promote") == 0) - recoveryTargetAction = RECOVERY_TARGET_ACTION_PROMOTE; - else if (strcmp(item->value, "shutdown") == 0) - recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for recovery parameter \"%s\": \"%s\"", - "recovery_target_action", - item->value), - errhint("Valid values are \"pause\", \"promote\", and \"shutdown\"."))); - - ereport(DEBUG2, - (errmsg_internal("recovery_target_action = '%s'", - item->value))); - - recoveryTargetActionSet = true; - } - else if (strcmp(item->name, "recovery_target_timeline") == 0) - { - rtliGiven = true; - if (strcmp(item->value, "latest") == 0) - rtli = 0; - else - { - errno = 0; - rtli = (TimeLineID) strtoul(item->value, NULL, 0); - if (errno == EINVAL || errno == ERANGE) - ereport(FATAL, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("recovery_target_timeline is not a valid number: \"%s\"", - item->value))); - } - if (rtli) - ereport(DEBUG2, - (errmsg_internal("recovery_target_timeline = %u", rtli))); - else - ereport(DEBUG2, - (errmsg_internal("recovery_target_timeline = latest"))); - } - else if (strcmp(item->name, "recovery_target_xid") == 0) - { - errno = 0; - recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0); - if (errno == EINVAL || errno == ERANGE) - ereport(FATAL, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("recovery_target_xid is not a valid number: \"%s\"", - item->value))); - ereport(DEBUG2, - (errmsg_internal("recovery_target_xid = %u", - recoveryTargetXid))); - recoveryTarget = RECOVERY_TARGET_XID; - } - else if (strcmp(item->name, "recovery_target_time") == 0) - { - recoveryTarget = RECOVERY_TARGET_TIME; - - /* - * Convert the time string given by the user to TimestampTz form. - */ - recoveryTargetTime = - DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in, - CStringGetDatum(item->value), - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(-1))); - ereport(DEBUG2, - (errmsg_internal("recovery_target_time = '%s'", - timestamptz_to_str(recoveryTargetTime)))); - } - else if (strcmp(item->name, "recovery_target_name") == 0) - { - recoveryTarget = RECOVERY_TARGET_NAME; - - recoveryTargetName = pstrdup(item->value); - if (strlen(recoveryTargetName) >= MAXFNAMELEN) - ereport(FATAL, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("recovery_target_name is too long (maximum %d characters)", - MAXFNAMELEN - 1))); - - ereport(DEBUG2, - (errmsg_internal("recovery_target_name = '%s'", - recoveryTargetName))); - } - else if (strcmp(item->name, "recovery_target_lsn") == 0) - { - recoveryTarget = RECOVERY_TARGET_LSN; - - /* - * Convert the LSN string given by the user to XLogRecPtr form. - */ - recoveryTargetLSN = - DatumGetLSN(DirectFunctionCall3(pg_lsn_in, - CStringGetDatum(item->value), - ObjectIdGetDatum(InvalidOid), - Int32GetDatum(-1))); - ereport(DEBUG2, - (errmsg_internal("recovery_target_lsn = '%X/%X'", - (uint32) (recoveryTargetLSN >> 32), - (uint32) recoveryTargetLSN))); - } - else if (strcmp(item->name, "recovery_target") == 0) - { - if (strcmp(item->value, "immediate") == 0) - recoveryTarget = RECOVERY_TARGET_IMMEDIATE; - else - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid value for recovery parameter \"%s\": \"%s\"", - "recovery_target", - item->value), - errhint("The only allowed value is \"immediate\"."))); - ereport(DEBUG2, - (errmsg_internal("recovery_target = '%s'", - item->value))); - } - else if (strcmp(item->name, "recovery_target_inclusive") == 0) - { - /* - * does nothing if a recovery_target is not also set - */ - if (!parse_bool(item->value, &recoveryTargetInclusive)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("parameter \"%s\" requires a Boolean value", - "recovery_target_inclusive"))); - ereport(DEBUG2, - (errmsg_internal("recovery_target_inclusive = %s", - item->value))); - } - else if (strcmp(item->name, "standby_mode") == 0) - { - if (!parse_bool(item->value, &StandbyModeRequested)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("parameter \"%s\" requires a Boolean value", - "standby_mode"))); - ereport(DEBUG2, - (errmsg_internal("standby_mode = '%s'", item->value))); - } - else if (strcmp(item->name, "primary_conninfo") == 0) - { - PrimaryConnInfo = pstrdup(item->value); - ereport(DEBUG2, - (errmsg_internal("primary_conninfo = '%s'", - PrimaryConnInfo))); - } - else if (strcmp(item->name, "primary_slot_name") == 0) - { - ReplicationSlotValidateName(item->value, ERROR); - PrimarySlotName = pstrdup(item->value); - ereport(DEBUG2, - (errmsg_internal("primary_slot_name = '%s'", - PrimarySlotName))); - } - else if (strcmp(item->name, "recovery_min_apply_delay") == 0) - { - const char *hintmsg; - - if (!parse_int(item->value, &recovery_min_apply_delay, GUC_UNIT_MS, - &hintmsg)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("parameter \"%s\" requires a temporal value", - "recovery_min_apply_delay"), - hintmsg ? errhint("%s", _(hintmsg)) : 0)); - ereport(DEBUG2, - (errmsg_internal("recovery_min_apply_delay = '%s'", item->value))); - } - else - ereport(FATAL, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("unrecognized recovery parameter \"%s\"", - item->name))); - } - - /* - * Check for compulsory parameters - */ - if (StandbyModeRequested) - { - if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL) - ereport(WARNING, - (errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command", - RECOVERY_COMMAND_FILE), - errhint("The database server will regularly poll the pg_xlog subdirectory to check for files placed there."))); - } - else - { - if (recoveryRestoreCommand == NULL) - ereport(FATAL, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled", - RECOVERY_COMMAND_FILE))); - } + /* + * Check for compulsory parameters + */ + if (standby_mode && !restore_command[0] && !primary_conninfo[0]) + ereport(WARNING, + (errmsg("neither primary_conninfo nor restore_command is specified"), + errhint("The database server will regularly poll the pg_xlog subdirectory to " + "check for files placed there until either of them is set in postgresql.conf."))); + CheckRestoreCommandSet(); + /* - * Override any inconsistent requests. Not that this is a change of + * Override any inconsistent requests. Note that this is a change of * behaviour in 9.5; prior to this we simply ignored a request to pause if * hot_standby = off, which was surprising behaviour. */ - if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE && - recoveryTargetActionSet && + if (recovery_target_action == RECOVERY_TARGET_ACTION_PAUSE && + strcmp(recovery_target_action_string, "") == 0 && !EnableHotStandby) - recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN; + recovery_target_action = RECOVERY_TARGET_ACTION_SHUTDOWN; /* * We don't support standby_mode in standalone backends; that requires * other processes such as the WAL receiver to be alive. */ - if (StandbyModeRequested && !IsUnderPostmaster) + if (standby_mode && !IsUnderPostmaster) ereport(FATAL, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("standby mode is not supported by single-user servers"))); - /* Enable fetching from archive recovery area */ - ArchiveRecoveryRequested = true; - /* * If user specified recovery_target_timeline, validate it or compute the * "latest" value. We can't do this until after we've gotten the restore * command and set InArchiveRecovery, because we need to fetch timeline * history files from the archive. */ - if (rtliGiven) + if (strcmp(recovery_target_timeline_string, "") != 0) { - if (rtli) + if (recovery_target_timeline) { /* Timeline 1 does not have a history file, all else should */ - if (rtli != 1 && !existsTimeLineHistory(rtli)) + if (recovery_target_timeline != 1 && !existsTimeLineHistory(recovery_target_timeline)) ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("recovery target timeline %u does not exist", - rtli))); - recoveryTargetTLI = rtli; + recovery_target_timeline))); + recoveryTargetTLI = recovery_target_timeline; recoveryTargetIsLatest = false; } else @@ -5237,8 +4995,6 @@ readRecoveryCommandFile(void) recoveryTargetIsLatest = true; } } - - FreeConfigVariables(head); } /* @@ -5401,7 +5157,7 @@ recoveryStopsBefore(XLogReaderState *record) TransactionId recordXid; /* Check if we should stop as soon as reaching consistency */ - if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency) + if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency) { ereport(LOG, (errmsg("recovery stopping after reaching consistency"))); @@ -5415,9 +5171,9 @@ recoveryStopsBefore(XLogReaderState *record) } /* Check if target LSN has been reached */ - if (recoveryTarget == RECOVERY_TARGET_LSN && - !recoveryTargetInclusive && - record->ReadRecPtr >= recoveryTargetLSN) + if (recovery_target == RECOVERY_TARGET_LSN && + !recovery_target_inclusive && + record->ReadRecPtr >= recovery_target_lsn) { recoveryStopAfter = false; recoveryStopXid = InvalidTransactionId; @@ -5472,7 +5228,7 @@ recoveryStopsBefore(XLogReaderState *record) else return false; - if (recoveryTarget == RECOVERY_TARGET_XID && !recoveryTargetInclusive) + if (recovery_target == RECOVERY_TARGET_XID && !recovery_target_inclusive) { /* * There can be only one transaction end record with this exact @@ -5483,10 +5239,10 @@ recoveryStopsBefore(XLogReaderState *record) * they complete. A higher numbered xid will complete before you about * 50% of the time... */ - stopsHere = (recordXid == recoveryTargetXid); + stopsHere = (recordXid == recovery_target_xid); } - if (recoveryTarget == RECOVERY_TARGET_TIME && + if (recovery_target == RECOVERY_TARGET_TIME && getRecordTimestamp(record, &recordXtime)) { /* @@ -5494,14 +5250,15 @@ recoveryStopsBefore(XLogReaderState *record) * we stop after the last one, if we are inclusive, or stop at the * first one if we are exclusive */ - if (recoveryTargetInclusive) - stopsHere = (recordXtime > recoveryTargetTime); + if (recovery_target_inclusive) + stopsHere = (recordXtime > recovery_target_time); else - stopsHere = (recordXtime >= recoveryTargetTime); + stopsHere = (recordXtime >= recovery_target_time); } if (stopsHere) { + recoveryStopTarget = recovery_target; recoveryStopAfter = false; recoveryStopXid = recordXid; recoveryStopTime = recordXtime; @@ -5548,14 +5305,14 @@ recoveryStopsAfter(XLogReaderState *record) * There can be many restore points that share the same name; we stop at * the first one. */ - if (recoveryTarget == RECOVERY_TARGET_NAME && + if (recovery_target == RECOVERY_TARGET_NAME && rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT) { xl_restore_point *recordRestorePointData; recordRestorePointData = (xl_restore_point *) XLogRecGetData(record); - if (strcmp(recordRestorePointData->rp_name, recoveryTargetName) == 0) + if (strcmp(recordRestorePointData->rp_name, recovery_target_name) == 0) { recoveryStopAfter = true; recoveryStopXid = InvalidTransactionId; @@ -5572,9 +5329,9 @@ recoveryStopsAfter(XLogReaderState *record) } /* Check if the target LSN has been reached */ - if (recoveryTarget == RECOVERY_TARGET_LSN && - recoveryTargetInclusive && - record->ReadRecPtr >= recoveryTargetLSN) + if (recovery_target == RECOVERY_TARGET_LSN && + recovery_target_inclusive && + record->ReadRecPtr >= recovery_target_lsn) { recoveryStopAfter = true; recoveryStopXid = InvalidTransactionId; @@ -5637,8 +5394,8 @@ recoveryStopsAfter(XLogReaderState *record) * they complete. A higher numbered xid will complete before you about * 50% of the time... */ - if (recoveryTarget == RECOVERY_TARGET_XID && recoveryTargetInclusive && - recordXid == recoveryTargetXid) + if (recovery_target == RECOVERY_TARGET_XID && recovery_target_inclusive && + recordXid == recovery_target_xid) { recoveryStopAfter = true; recoveryStopXid = recordXid; @@ -5667,7 +5424,7 @@ recoveryStopsAfter(XLogReaderState *record) } /* Check if we should stop as soon as reaching consistency */ - if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE && reachedConsistency) + if (recovery_target == RECOVERY_TARGET_IMMEDIATE && reachedConsistency) { ereport(LOG, (errmsg("recovery stopping after reaching consistency"))); @@ -5964,6 +5721,19 @@ CheckRequiredParameterValues(void) } /* + * Check to see if restore_command must be set for archive recovery + * when standby mode is not enabled + */ +void +CheckRestoreCommandSet(void) +{ + if (InArchiveRecovery && !standby_mode && !restore_command[0]) + ereport(FATAL, + (errmsg("restore_command must be specified for archive recovery " + "when standby mode is not enabled"))); +} + +/* * This must be called ONCE during postmaster or standalone-backend startup */ void @@ -6074,40 +5844,32 @@ StartupXLOG(void) * Check for recovery trigger file, and if so set up state for offline * recovery */ - CheckForRecoveryTrigger(); - readRecoveryCommandFile(); // remove this later - - /* - * Save archive_cleanup_command in shared memory so that other processes - * can see it. - */ - strlcpy(XLogCtl->archiveCleanupCommand, - archiveCleanupCommand ? archiveCleanupCommand : "", - sizeof(XLogCtl->archiveCleanupCommand)); + if (CheckForRecoveryTrigger()) + CheckRecoveryParameters(); if (ArchiveRecoveryRequested) { if (StandbyModeRequested) ereport(LOG, (errmsg("entering standby mode"))); - else if (recoveryTarget == RECOVERY_TARGET_XID) + else if (recovery_target == RECOVERY_TARGET_XID) ereport(LOG, (errmsg("starting point-in-time recovery to XID %u", - recoveryTargetXid))); - else if (recoveryTarget == RECOVERY_TARGET_TIME) + recovery_target_xid))); + else if (recovery_target == RECOVERY_TARGET_TIME) ereport(LOG, (errmsg("starting point-in-time recovery to %s", - timestamptz_to_str(recoveryTargetTime)))); - else if (recoveryTarget == RECOVERY_TARGET_NAME) + timestamptz_to_str(recovery_target_time)))); + else if (recovery_target == RECOVERY_TARGET_NAME) ereport(LOG, (errmsg("starting point-in-time recovery to \"%s\"", - recoveryTargetName))); - else if (recoveryTarget == RECOVERY_TARGET_LSN) + recovery_target_name))); + else if (recovery_target == RECOVERY_TARGET_LSN) ereport(LOG, (errmsg("starting point-in-time recovery to WAL position (LSN) \"%X/%X\"", - (uint32) (recoveryTargetLSN >> 32), - (uint32) recoveryTargetLSN))); - else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE) + (uint32) (recovery_target_lsn >> 32), + (uint32) recovery_target_lsn))); + else if (recovery_target == RECOVERY_TARGET_IMMEDIATE) ereport(LOG, (errmsg("starting point-in-time recovery to earliest consistent point"))); else @@ -6119,7 +5881,7 @@ StartupXLOG(void) * Take ownership of the wakeup latch if we're going to sleep during * recovery. */ - if (StandbyModeRequested) + if (standby_mode) OwnLatch(&XLogCtl->recoveryWakeupLatch); /* Set up XLOG reader facility */ @@ -6143,8 +5905,6 @@ StartupXLOG(void) * archive recovery directly. */ InArchiveRecovery = true; - if (StandbyModeRequested) - StandbyMode = true; /* * When a backup_label file is present, we want to roll forward from @@ -6270,8 +6030,6 @@ StartupXLOG(void) ControlFile->state == DB_SHUTDOWNED)) { InArchiveRecovery = true; - if (StandbyModeRequested) - StandbyMode = true; } /* @@ -6287,7 +6045,7 @@ StartupXLOG(void) (errmsg("checkpoint record is at %X/%X", (uint32) (checkPointLoc >> 32), (uint32) checkPointLoc))); } - else if (StandbyMode) + else if (standby_mode) { /* * The last valid checkpoint record required for a streaming @@ -7006,7 +6764,7 @@ StartupXLOG(void) * this, Resource Managers may choose to do permanent * corrective actions at end of recovery. */ - switch (recoveryTargetAction) + switch (recovery_target_action) { case RECOVERY_TARGET_ACTION_SHUTDOWN: @@ -7075,7 +6833,7 @@ StartupXLOG(void) * We don't need the latch anymore. It's not strictly necessary to disown * it, but let's do it for the sake of tidiness. */ - if (StandbyModeRequested) + if (standby_mode) DisownLatch(&XLogCtl->recoveryWakeupLatch); /* @@ -7083,7 +6841,7 @@ StartupXLOG(void) * recovery to force fetching the files (which would be required at end of * recovery, e.g., timeline history file) from archive or pg_xlog. */ - StandbyMode = false; + SetConfigOption("standby_mode", "false", PGC_POSTMASTER, PGC_S_OVERRIDE); /* * Re-fetch the last valid or last applied record, so we can identify the @@ -7166,27 +6924,27 @@ StartupXLOG(void) * Create a comment for the history file to explain why and where * timeline changed. */ - if (recoveryTarget == RECOVERY_TARGET_XID) + if (recoveryStopTarget == RECOVERY_TARGET_XID) snprintf(reason, sizeof(reason), "%s transaction %u", recoveryStopAfter ? "after" : "before", recoveryStopXid); - else if (recoveryTarget == RECOVERY_TARGET_TIME) + else if (recoveryStopTarget == RECOVERY_TARGET_TIME) snprintf(reason, sizeof(reason), "%s %s\n", recoveryStopAfter ? "after" : "before", timestamptz_to_str(recoveryStopTime)); - else if (recoveryTarget == RECOVERY_TARGET_LSN) + else if (recoveryStopTarget == RECOVERY_TARGET_LSN) snprintf(reason, sizeof(reason), "%s LSN %X/%X\n", recoveryStopAfter ? "after" : "before", (uint32 ) (recoveryStopLSN >> 32), (uint32) recoveryStopLSN); - else if (recoveryTarget == RECOVERY_TARGET_NAME) + else if (recoveryStopTarget == RECOVERY_TARGET_NAME) snprintf(reason, sizeof(reason), "at restore point \"%s\"", recoveryStopName); - else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE) + else if (recoveryStopTarget == RECOVERY_TARGET_IMMEDIATE) snprintf(reason, sizeof(reason), "reached consistency"); else snprintf(reason, sizeof(reason), "no recovery target specified"); @@ -7325,8 +7083,8 @@ StartupXLOG(void) /* * And finally, execute the recovery_end_command, if any. */ - if (recoveryEndCommand) - ExecuteRecoveryCommand(recoveryEndCommand, + if (recovery_end_command) + ExecuteRecoveryCommand(recovery_end_command, "recovery_end_command", true); } @@ -8972,8 +8730,8 @@ CreateRestartPoint(int flags) /* * Finally, execute archive_cleanup_command, if any. */ - if (XLogCtl->archiveCleanupCommand[0]) - ExecuteRecoveryCommand(XLogCtl->archiveCleanupCommand, + if (archive_cleanup_command[0]) + ExecuteRecoveryCommand(archive_cleanup_command, "archive_cleanup_command", false); @@ -11154,7 +10912,7 @@ next_record_is_invalid: readSource = 0; /* In standby-mode, keep trying */ - if (StandbyMode) + if (standby_mode) goto retry; else return -1; @@ -11240,7 +10998,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, * trigger file, we still finish replaying as much as we * can from archive and pg_xlog before failover. */ - if (StandbyMode && CheckForPromoteTrigger()) + if (standby_mode && CheckForPromoteTrigger()) { ShutdownWalRcv(); return false; @@ -11250,7 +11008,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, * Not in standby mode, and we've now tried the archive * and pg_xlog. */ - if (!StandbyMode) + if (!standby_mode) return false; /* @@ -11263,7 +11021,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, * that when we later jump backwards to start redo at * RedoStartLSN, we will have the logs streamed already. */ - if (PrimaryConnInfo) + if (primary_conninfo) { XLogRecPtr ptr; TimeLineID tli; @@ -11284,8 +11042,8 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, tli, curFileTLI); } curFileTLI = tli; - RequestXLogStreaming(tli, ptr, PrimaryConnInfo, - PrimarySlotName); + RequestXLogStreaming(tli, ptr, primary_conninfo, + primary_slot_name); receivedUpto = 0; } diff --git a/src/backend/access/transam/xlogarchive.c b/src/backend/access/transam/xlogarchive.c index d153a44..585c3ec 100644 --- a/src/backend/access/transam/xlogarchive.c +++ b/src/backend/access/transam/xlogarchive.c @@ -67,7 +67,7 @@ RestoreArchivedFile(char *path, const char *xlogfname, TimeLineID restartTli; /* In standby mode, restore_command might not be supplied */ - if (recoveryRestoreCommand == NULL) + if (!restore_command[0]) goto not_available; /* @@ -150,7 +150,7 @@ RestoreArchivedFile(char *path, const char *xlogfname, endp = xlogRestoreCmd + MAXPGPATH - 1; *endp = '\0'; - for (sp = recoveryRestoreCommand; *sp; sp++) + for (sp = restore_command; *sp; sp++) { if (*sp == '%') { @@ -236,7 +236,7 @@ RestoreArchivedFile(char *path, const char *xlogfname, * incorrectly conclude we've reached the end of WAL and we're * done recovering ... */ - if (StandbyMode && stat_buf.st_size < expectedSize) + if (standby_mode && stat_buf.st_size < expectedSize) elevel = DEBUG1; else elevel = FATAL; @@ -409,7 +409,7 @@ ExecuteRecoveryCommand(char *command, char *commandName, bool failOnSignal) ereport((signaled && failOnSignal) ? FATAL : WARNING, /*------ - translator: First %s represents a recovery.conf parameter name like + translator: First %s represents a postgresql.conf parameter name like "recovery_end_command", the 2nd is the value of that parameter, the third an already translated error message. */ (errmsg("%s \"%s\": %s", commandName, diff --git a/src/backend/postmaster/startup.c b/src/backend/postmaster/startup.c index a7ae7e3..1a6adac 100644 --- a/src/backend/postmaster/startup.c +++ b/src/backend/postmaster/startup.c @@ -153,6 +153,8 @@ HandleStartupProcInterrupts(void) { got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); + + CheckRestoreCommandSet(); } /* diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index 413ee3a..ec2f252 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -415,9 +415,33 @@ WalReceiverMain(void) if (got_SIGHUP) { + char *conninfo = pstrdup(primary_conninfo); + char *slotname = pstrdup(primary_slot_name); + got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); XLogWalRcvSendHSFeedback(true); + + /* + * If primary_conninfo has been changed while walreceiver is running, + * shut down walreceiver so that a new walreceiver is started and + * initiates replication with the new primary_conninfo. + */ + if (strcmp(conninfo, primary_conninfo) != 0) + ereport(FATAL, + (errcode(ERRCODE_ADMIN_SHUTDOWN), + errmsg("terminating walreceiver process because primary_conninfo was changed"))); + + /* + * And the same for primary_slot_name. + */ + if (strcmp(slotname, primary_slot_name) != 0) + ereport(FATAL, + (errcode(ERRCODE_ADMIN_SHUTDOWN), + errmsg("terminating walreceiver process because primary_slot_name was changed"))); + + pfree(conninfo); + pfree(slotname); } /* See if we can read data immediately */ diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l index dae5015..10c052c 100644 --- a/src/backend/utils/misc/guc-file.l +++ b/src/backend/utils/misc/guc-file.l @@ -197,6 +197,8 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel) */ if (DataDir) { + struct stat stat_buf; + if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false, NULL, 0, 0, elevel, &head, &tail)) @@ -206,6 +208,22 @@ ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel) ConfFileWithError = PG_AUTOCONF_FILENAME; goto bail_out; } + + /* + * For backwards compatibility, we also read recovery.conf if + * it exists. + */ + + if (stat("recovery.conf", &stat_buf) == 0 && + !ParseConfigFile("recovery.conf", false, + NULL, 0, 0, elevel, + &head, &tail)) + { + /* Syntax error(s) detected in the file, so bail out */ + error = true; + ConfFileWithError = "recovery.conf"; + goto bail_out; + } } else { diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index c5178f7..f68e397 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -31,6 +31,7 @@ #include "access/transam.h" #include "access/twophase.h" #include "access/xact.h" +#include "access/xlog_internal.h" #include "catalog/namespace.h" #include "commands/async.h" #include "commands/prepare.h" @@ -76,6 +77,7 @@ #include "utils/guc_tables.h" #include "utils/memutils.h" #include "utils/pg_locale.h" +#include "utils/pg_lsn.h" #include "utils/plancache.h" #include "utils/portal.h" #include "utils/ps_status.h" @@ -181,6 +183,21 @@ static void assign_application_name(const char *newval, void *extra); static bool check_cluster_name(char **newval, void **extra, GucSource source); static const char *show_unix_socket_permissions(void); static const char *show_log_file_mode(void); +static bool check_recovery_target(char **newval, void **extra, GucSource source); +static void assign_recovery_target(const char *newval, void *extra); +static bool check_recovery_target_xid(char **newval, void **extra, GucSource source); +static void assign_recovery_target_xid(const char *newval, void *extra); +static bool check_recovery_target_name(char **newval, void **extra, GucSource source); +static void assign_recovery_target_name(const char *newval, void *extra); +static bool check_recovery_target_time(char **newval, void **extra, GucSource source); +static void assign_recovery_target_time(const char *newval, void *extra); +static bool check_recovery_target_lsn(char **newval, void **extra, GucSource source); +static void assign_recovery_target_lsn(const char *newval, void *extra); +static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source); +static void assign_recovery_target_timeline(const char *newval, void *extra); +static bool check_recovery_target_action(char **newval, void **extra, GucSource source); +static void assign_recovery_target_action(const char *newval, void *extra); +static bool check_primary_slot_name(char **newval, void **extra, GucSource source); /* Private functions in guc-file.l that need to be called from guc.c */ static ConfigVariable *ProcessConfigFileInternal(GucContext context, @@ -495,6 +512,8 @@ static bool data_checksums; static int wal_segment_size; static bool integer_datetimes; static bool assert_enabled; +static char *recovery_target_xid_string; +static char *recovery_target_time_string; /* should be static, but commands/variable.c needs to get at this */ char *role_string; @@ -576,6 +595,10 @@ const char *const config_group_names[] = gettext_noop("Write-Ahead Log / Checkpoints"), /* WAL_ARCHIVING */ gettext_noop("Write-Ahead Log / Archiving"), + /* WAL_ARCHIVE_RECOVERY */ + gettext_noop("Write-Ahead Log / Archive Recovery"), + /* WAL_RECOVERY_TARGET */ + gettext_noop("Write-Ahead Log / Recovery Target"), /* REPLICATION */ gettext_noop("Replication"), /* REPLICATION_SENDING */ @@ -1553,6 +1576,26 @@ static struct config_bool ConfigureNamesBool[] = }, { + {"recovery_target_inclusive", PGC_SIGHUP, WAL_RECOVERY_TARGET, + gettext_noop("Sets whether to include or exclude transaction with recovery target."), + NULL + }, + &recovery_target_inclusive, + true, + NULL, NULL, NULL + }, + + { + {"standby_mode", PGC_POSTMASTER, REPLICATION_STANDBY, + gettext_noop("Sets whether to start the server as a standby."), + NULL + }, + &standby_mode, + false, + NULL, NULL, NULL + }, + + { {"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY, gettext_noop("Allows connections and queries during recovery."), NULL @@ -1793,6 +1836,17 @@ static struct config_int ConfigureNamesInt[] = }, { + {"recovery_min_apply_delay", PGC_SIGHUP, REPLICATION_STANDBY, + gettext_noop("Sets the minimum delay to apply changes during recovery."), + NULL, + GUC_UNIT_MS + }, + &recovery_min_apply_delay, + 0, 0, INT_MAX, + NULL, NULL, NULL + }, + + { {"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY, gettext_noop("Sets the maximum interval between WAL receiver status reports to the primary."), NULL, @@ -2992,6 +3046,128 @@ static struct config_string ConfigureNamesString[] = }, { + {"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY, + gettext_noop("Sets the shell command that will retrieve an archived WAL file."), + NULL + }, + &restore_command, + "", + NULL, NULL, NULL + }, + + { + {"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY, + gettext_noop("Sets the shell command that will be executed at every restartpoint."), + NULL + }, + &archive_cleanup_command, + "", + NULL, NULL, NULL + }, + + { + {"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY, + gettext_noop("Sets the shell command that will be executed once only at the end of recovery."), + NULL + }, + &recovery_end_command, + "", + NULL, NULL, NULL + }, + + { + {"recovery_target", PGC_SIGHUP, WAL_RECOVERY_TARGET, + gettext_noop("Sets the desired recovery target."), + NULL + }, + &recovery_target_string, + "", + check_recovery_target, assign_recovery_target, NULL + }, + + { + {"recovery_target_xid", PGC_SIGHUP, WAL_RECOVERY_TARGET, + gettext_noop("Sets the transaction ID up to which recovery will proceed."), + NULL + }, + &recovery_target_xid_string, + "", + check_recovery_target_xid, assign_recovery_target_xid, NULL + }, + + { + {"recovery_target_name", PGC_SIGHUP, WAL_RECOVERY_TARGET, + gettext_noop("Sets the named restore point."), + NULL + }, + &recovery_target_name, + "", + check_recovery_target_name, assign_recovery_target_name, NULL + }, + + { + {"recovery_target_time", PGC_SIGHUP, WAL_RECOVERY_TARGET, + gettext_noop("Sets the time stamp up to which recovery will proceed."), + NULL + }, + &recovery_target_time_string, + "", + check_recovery_target_time, assign_recovery_target_time, NULL + }, + + { + {"recovery_target_lsn", PGC_SIGHUP, WAL_RECOVERY_TARGET, + gettext_noop("Sets the LSN up to which recovery will proceed."), + NULL + }, + &recovery_target_lsn_string, + "", + check_recovery_target_lsn, assign_recovery_target_lsn, NULL + }, + + { + {"recovery_target_timeline", PGC_POSTMASTER, WAL_RECOVERY_TARGET, + gettext_noop("Sets recoverying into a particular timeline."), + NULL + }, + &recovery_target_timeline_string, + "", + check_recovery_target_timeline, assign_recovery_target_timeline, NULL + }, + + { + {"recovery_target_action", PGC_POSTMASTER, WAL_RECOVERY_TARGET, + gettext_noop("Sets the action to perform upon reaching the recovery target."), + NULL + }, + &recovery_target_action_string, + "", + check_recovery_target_action, assign_recovery_target_action, NULL + }, + + { + {"primary_conninfo", PGC_SIGHUP, REPLICATION_STANDBY, + gettext_noop("Sets the connection string to be used to connect with the primary."), + NULL, + GUC_SUPERUSER_ONLY + }, + &primary_conninfo, + "", + NULL, NULL, NULL + }, + + { + {"primary_slot_name", PGC_SIGHUP, REPLICATION_STANDBY, + gettext_noop("Sets the name of the replication slot to use on the primary."), + NULL, + GUC_SUPERUSER_ONLY + }, + &primary_slot_name, + "", + check_primary_slot_name, NULL, NULL + }, + + { {"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE, gettext_noop("Sets the client's character set encoding."), NULL, @@ -10301,4 +10477,291 @@ show_log_file_mode(void) return buf; } +static bool +check_recovery_target(char **newval, void **extra, GucSource source) +{ + RecoveryTargetType rt = RECOVERY_TARGET_UNSET; + RecoveryTargetType *myextra; + + if (strcmp(*newval, "immediate") == 0) + rt = RECOVERY_TARGET_IMMEDIATE; + else if (strcmp(*newval, "") != 0) + { + GUC_check_errdetail("recovery_target is not valid: \"%s\"", *newval); + return false; + } + + myextra = (RecoveryTargetType *) guc_malloc(ERROR, sizeof(RecoveryTargetType)); + *myextra = rt; + *extra = (void *) myextra; + + return true; +} + +static void +assign_recovery_target(const char *newval, void *extra) +{ + recovery_target = *((RecoveryTargetType *) extra); +} + +static bool +check_recovery_target_xid(char **newval, void **extra, GucSource source) +{ + TransactionId xid; + TransactionId *myextra; + + if (strcmp(*newval, "") == 0) + xid = InvalidTransactionId; + else + { + errno = 0; + xid = (TransactionId) strtoul(*newval, NULL, 0); + if (errno == EINVAL || errno == ERANGE) + { + GUC_check_errdetail("recovery_target_xid is not a valid number: \"%s\"", + *newval); + return false; + } + } + + myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId)); + *myextra = xid; + *extra = (void *) myextra; + + return true; +} + +static void +assign_recovery_target_xid(const char *newval, void *extra) +{ + recovery_target_xid = *((TransactionId *) extra); + + if (recovery_target_xid != InvalidTransactionId) + recovery_target = RECOVERY_TARGET_XID; + else if (recovery_target_name && *recovery_target_name) + recovery_target = RECOVERY_TARGET_NAME; + else if (recovery_target_time != 0) + recovery_target = RECOVERY_TARGET_TIME; + else if (recovery_target_lsn != 0) + recovery_target = RECOVERY_TARGET_LSN; + else + recovery_target = RECOVERY_TARGET_UNSET; +} + +static bool +check_recovery_target_name(char **newval, void **extra, GucSource source) +{ + if (strlen(*newval) >= MAXFNAMELEN) + { + GUC_check_errdetail("\"recovery_target_name\" is too long (maximum %d characters)", + MAXFNAMELEN - 1); + return false; + } + return true; +} + +static void +assign_recovery_target_name(const char *newval, void *extra) +{ + if (recovery_target_xid != InvalidTransactionId) + recovery_target = RECOVERY_TARGET_XID; + else if (newval[0]) + recovery_target = RECOVERY_TARGET_NAME; + else if (recovery_target_time != 0) + recovery_target = RECOVERY_TARGET_TIME; + else if (recovery_target_lsn != 0) + recovery_target = RECOVERY_TARGET_LSN; + else + recovery_target = RECOVERY_TARGET_UNSET; +} + +static bool +check_recovery_target_time(char **newval, void **extra, GucSource source) +{ + TimestampTz time; + TimestampTz *myextra; + MemoryContext oldcontext = CurrentMemoryContext; + + PG_TRY(); + { + time = (strcmp(*newval, "") == 0) ? + 0 : + DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in, + CStringGetDatum(*newval), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1))); + } + PG_CATCH(); + { + ErrorData *edata; + + /* Save error info */ + MemoryContextSwitchTo(oldcontext); + edata = CopyErrorData(); + FlushErrorState(); + + /* Pass the error message */ + GUC_check_errdetail("%s", edata->message); + FreeErrorData(edata); + return false; + } + PG_END_TRY(); + + myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz)); + *myextra = time; + *extra = (void *) myextra; + + return true; +} + +static void +assign_recovery_target_time(const char *newval, void *extra) +{ + recovery_target_time = *((TimestampTz *) extra); + + if (recovery_target_xid != InvalidTransactionId) + recovery_target = RECOVERY_TARGET_XID; + else if (recovery_target_name && *recovery_target_name) + recovery_target = RECOVERY_TARGET_NAME; + else if (recovery_target_time != 0) + recovery_target = RECOVERY_TARGET_TIME; + else if (recovery_target_lsn != 0) + recovery_target = RECOVERY_TARGET_LSN; + else + recovery_target = RECOVERY_TARGET_UNSET; +} + +static bool +check_recovery_target_lsn(char **newval, void **extra, GucSource source) +{ + XLogRecPtr lsn; + XLogRecPtr *myextra; + MemoryContext oldcontext = CurrentMemoryContext; + + PG_TRY(); + { + lsn = (strcmp(*newval, "") == 0) ? + 0 : + DatumGetLSN(DirectFunctionCall3(pg_lsn_in, + CStringGetDatum(*newval), + ObjectIdGetDatum(InvalidOid), + Int32GetDatum(-1))); + } + PG_CATCH(); + { + ErrorData *edata; + + /* Save error info */ + MemoryContextSwitchTo(oldcontext); + edata = CopyErrorData(); + FlushErrorState(); + + /* Pass the error message */ + GUC_check_errdetail("%s", edata->message); + FreeErrorData(edata); + return false; + } + PG_END_TRY(); + + myextra = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr)); + *myextra = lsn; + *extra = (void *) myextra; + + return true; +} + +static void +assign_recovery_target_lsn(const char *newval, void *extra) +{ + recovery_target_lsn = *((XLogRecPtr *) extra); + + if (recovery_target_xid != InvalidTransactionId) + recovery_target = RECOVERY_TARGET_XID; + else if (recovery_target_name && *recovery_target_name) + recovery_target = RECOVERY_TARGET_NAME; + else if (recovery_target_time != 0) + recovery_target = RECOVERY_TARGET_TIME; + else if (recovery_target_lsn != 0) + recovery_target = RECOVERY_TARGET_LSN; + else + recovery_target = RECOVERY_TARGET_UNSET; +} + +static bool +check_recovery_target_timeline(char **newval, void **extra, GucSource source) +{ + TimeLineID tli = 0; + TimeLineID *myextra; + + if (strcmp(*newval, "") == 0 || strcmp(*newval, "latest") == 0) + tli = 0; + else + { + errno = 0; + tli = (TimeLineID) strtoul(*newval, NULL, 0); + if (errno == EINVAL || errno == ERANGE) + { + GUC_check_errdetail("recovery_target_timeline is not a valid number: \"%s\"", + *newval); + return false; + } + } + + myextra = (TimeLineID *) guc_malloc(ERROR, sizeof(TimeLineID)); + *myextra = tli; + *extra = (void *) myextra; + + return true; +} + +static void +assign_recovery_target_timeline(const char *newval, void *extra) +{ + recovery_target_timeline = *((TimeLineID *) extra); +} + +static bool +check_recovery_target_action(char **newval, void **extra, GucSource source) +{ + RecoveryTargetAction rta = RECOVERY_TARGET_ACTION_PAUSE; + RecoveryTargetAction *myextra; + + if (strcmp(*newval, "pause") == 0) + rta = RECOVERY_TARGET_ACTION_PAUSE; + else if (strcmp(*newval, "promote") == 0) + rta = RECOVERY_TARGET_ACTION_PROMOTE; + else if (strcmp(*newval, "shutdown") == 0) + rta = RECOVERY_TARGET_ACTION_SHUTDOWN; + else if (strcmp(*newval, "") != 0) + { + GUC_check_errdetail("recovery_target_action is not valid: \"%s\"", *newval); + return false; + } + + myextra = (RecoveryTargetAction *) guc_malloc(ERROR, sizeof(RecoveryTargetAction)); + *myextra = rta; + *extra = (void *) myextra; + + return true; +} + +static void +assign_recovery_target_action(const char *newval, void *extra) +{ + recovery_target_action = *((RecoveryTargetAction *) extra); +} + +static bool +check_primary_slot_name(char **newval, void **extra, GucSource source) +{ + if (strcmp(*newval,"") != 0 && + !ReplicationSlotValidateName(*newval, WARNING)) + { + GUC_check_errdetail("primary_slot_name is not valid: \"%s\"", *newval); + return false; + } + + return true; +} + #include "guc-file.c" diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 6d0666c..0edbf91 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -222,6 +222,25 @@ #archive_timeout = 0 # force a logfile segment switch after this # number of seconds; 0 disables +# - Archive Recovery - +#restore_command = '' # command to use to restore an archived logfile segment + # placeholders: %p = path of file to restore + # %f = file name only + # e.g. 'cp /mnt/server/archivedir/%f %p' +#archive_cleanup_command = '' # command to execute at every restartpoint +#recovery_end_command = '' # command to execute at completion of recovery + +# - Recovery Target - +#recovery_target_action = 'pause' # 'pause', 'promote', 'shutdown' +#recovery_target=immediate +#recovery_target_xid = '' +#recovery_target_name = '' +#recovery_target_time = '' +#recovery_target_lsn = '' +#recovery_target_timeline = '' # timeline ID or 'latest' + # (change requires restart) +#recovery_target_inclusive = on + #------------------------------------------------------------------------------ # REPLICATION @@ -254,6 +273,10 @@ # These settings are ignored on a master server. +#standby_mode = off # "on" starts the server as a standby + # (change requires restart) +#primary_conninfo = '' # connection string to connect to the master +#trigger_file = '' # trigger file to promote the standby #hot_standby = off # "on" allows queries during recovery # (change requires restart) #max_standby_archive_delay = 30s # max delay before canceling queries diff --git a/src/bin/pg_archivecleanup/pg_archivecleanup.c b/src/bin/pg_archivecleanup/pg_archivecleanup.c index 319038f..abbe367 100644 --- a/src/bin/pg_archivecleanup/pg_archivecleanup.c +++ b/src/bin/pg_archivecleanup/pg_archivecleanup.c @@ -270,7 +270,7 @@ usage(void) printf(" -x EXT clean up files if they have this extension\n"); printf(" -?, --help show this help, then exit\n"); printf("\n" - "For use as archive_cleanup_command in recovery.conf when standby_mode = on:\n" + "For use as archive_cleanup_command in postgresql.conf when standby_mode = on:\n" " archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n" "e.g.\n" " archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n"); diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index c9f332c..5a735bd 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -106,6 +106,26 @@ extern bool fullPageWrites; extern bool wal_log_hints; extern bool wal_compression; extern bool log_checkpoints; +extern char *restore_command; +extern char *archive_cleanup_command; +extern char *recovery_end_command; +extern bool standby_mode; +extern char *primary_conninfo; +extern char *primary_slot_name; +extern char *trigger_file; +extern RecoveryTargetType recovery_target; +extern TransactionId recovery_target_xid; +extern TimestampTz recovery_target_time; +extern XLogRecPtr recovery_target_lsn; +extern char *recovery_target_name; +extern bool recovery_target_inclusive; +extern bool pause_at_recovery_target; +extern char *recovery_target_timeline_string; +extern char *recovery_target_action_string; +extern char *recovery_target_lsn_string; +extern char *recovery_target_string; +extern TimeLineID recovery_target_timeline; +extern int recovery_min_apply_delay; extern int CheckPointSegments; @@ -268,6 +288,7 @@ extern void RemovePromoteSignalFiles(void); extern bool CheckPromoteSignal(void); extern void WakeupRecovery(void); extern void SetWalWriterSleeping(bool sleeping); +extern void CheckRestoreCommandSet(void); extern void XLogRequestWalReceiverReply(void); diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index 0a595cc..2f01c58 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -255,6 +255,8 @@ typedef enum RECOVERY_TARGET_ACTION_SHUTDOWN } RecoveryTargetAction; +extern RecoveryTargetAction recovery_target_action; + /* * Method table for resource managers. * diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h index b695f61..7f4389c 100644 --- a/src/include/utils/guc_tables.h +++ b/src/include/utils/guc_tables.h @@ -68,6 +68,8 @@ enum config_group WAL_SETTINGS, WAL_CHECKPOINTS, WAL_ARCHIVING, + WAL_ARCHIVE_RECOVERY, + WAL_RECOVERY_TARGET, REPLICATION, REPLICATION_SENDING, REPLICATION_MASTER,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers