pgsql: Integrate recovery.conf into postgresql.conf
Integrate recovery.conf into postgresql.conf
recovery.conf settings are now set in postgresql.conf (or other GUC
sources). Currently, all the affected settings are PGC_POSTMASTER;
this could be refined in the future case by case.
Recovery is now initiated by a file recovery.signal. Standby mode is
initiated by a file standby.signal. The standby_mode setting is
gone. If a recovery.conf file is found, an error is issued.
The trigger_file setting has been renamed to promote_trigger_file as
part of the move.
The documentation chapter "Recovery Configuration" has been integrated
into "Server Configuration".
pg_basebackup -R now appends settings to postgresql.auto.conf and
creates a standby.signal file.
Author: Fujii Masao <masao.fujii@gmail.com>
Author: Simon Riggs <simon@2ndquadrant.com>
Author: Abhijit Menon-Sen <ams@2ndquadrant.com>
Author: Sergei Kornilov <sk@zsrv.org>
Discussion: /messages/by-id/607741529606767@web3g.yandex.ru/
Branch
------
master
Details
-------
https://git.postgresql.org/pg/commitdiff/2dedf4d9a899b36d1a8ed29be5efbd1b31a8fe85
Modified Files
--------------
contrib/pg_standby/pg_standby.c | 2 +-
doc/src/sgml/backup.sgml | 22 +-
doc/src/sgml/config.sgml | 515 ++++++++++++++++++++-
doc/src/sgml/filelist.sgml | 1 -
doc/src/sgml/func.sgml | 2 +-
doc/src/sgml/high-availability.sgml | 56 +--
doc/src/sgml/pgstandby.sgml | 2 +-
doc/src/sgml/postgres.sgml | 1 -
doc/src/sgml/recovery-config.sgml | 510 --------------------
doc/src/sgml/ref/pg_basebackup.sgml | 7 +-
doc/src/sgml/ref/pg_rewind.sgml | 8 +-
doc/src/sgml/ref/pgarchivecleanup.sgml | 4 +-
doc/src/sgml/ref/pgupgrade.sgml | 2 +-
doc/src/sgml/release-10.sgml | 2 +-
doc/src/sgml/release-9.1.sgml | 5 +-
doc/src/sgml/release-9.4.sgml | 15 +-
doc/src/sgml/release-9.5.sgml | 11 +-
doc/src/sgml/release.sgml | 3 +-
src/backend/Makefile | 4 +-
src/backend/access/transam/recovery.conf.sample | 158 -------
src/backend/access/transam/xlog.c | 500 +++++++-------------
src/backend/access/transam/xlogarchive.c | 4 +-
src/backend/commands/extension.c | 4 +-
src/backend/utils/misc/guc.c | 421 +++++++++++++++++
src/backend/utils/misc/postgresql.conf.sample | 47 ++
src/bin/pg_archivecleanup/pg_archivecleanup.c | 2 +-
src/bin/pg_basebackup/pg_basebackup.c | 148 ++++--
src/bin/pg_basebackup/t/010_pg_basebackup.pl | 15 +-
src/bin/pg_rewind/RewindTest.pm | 10 +-
src/include/access/xlog.h | 39 +-
src/include/utils/guc_tables.h | 2 +
src/port/quotes.c | 2 +-
src/test/perl/PostgresNode.pm | 26 +-
src/test/recovery/t/001_stream_rep.pl | 4 +-
src/test/recovery/t/003_recovery_targets.pl | 2 +-
src/test/recovery/t/004_timeline_switch.pl | 4 +-
src/test/recovery/t/005_replay_delay.pl | 2 +-
src/test/recovery/t/009_twophase.pl | 6 +-
.../recovery/t/010_logical_decoding_timelines.pl | 2 +-
src/test/recovery/t/012_subtransactions.pl | 6 +-
40 files changed, 1403 insertions(+), 1173 deletions(-)
On Sun, Nov 25, 2018 at 03:49:23PM +0000, Peter Eisentraut wrote:
Integrate recovery.conf into postgresql.conf
recovery.conf settings are now set in postgresql.conf (or other GUC
sources). Currently, all the affected settings are PGC_POSTMASTER;
this could be refined in the future case by case.
The buildfarm is unhappy after this one:
https://buildfarm.postgresql.org/cgi-bin/show_history.pl?nm=culicidae&br=HEAD
If I use -DEXEC_BACKEND when compiling the tests complain about a
timeout in 003_recovery_targets. Here is what the buildfarm reports, I
can see the failure by myself as well:
# Postmaster PID for node "standby_6" is 26668
# poll_query_until timed out executing this query:
# SELECT '0/3022690'::pg_lsn <= pg_last_wal_replay_lsn()
# expecting this output:
# t
# last actual query output:
# f
# with stderr:
Timed out while waiting for standby to catch up at
t/003_recovery_targets.pl line 34.
Peter, could you look at that?
--
Michael
Hi
The buildfarm is unhappy after this one:
https://buildfarm.postgresql.org/cgi-bin/show_history.pl?nm=culicidae&br=HEADIf I use -DEXEC_BACKEND when compiling the tests complain about a
timeout in 003_recovery_targets. Here is what the buildfarm reports, I
can see the failure by myself as well:
# Postmaster PID for node "standby_6" is 26668
# poll_query_until timed out executing this query:
# SELECT '0/3022690'::pg_lsn <= pg_last_wal_replay_lsn()
# expecting this output:
# t
# last actual query output:
# f
# with stderr:
Timed out while waiting for standby to catch up at
t/003_recovery_targets.pl line 34.
I can reproduce this and notice wrong assign settings order. For example standby_6 has
recovery_target_name = '$recovery_name'
recovery_target_xid = '$recovery_txid'
recovery_target_time = '$recovery_time'
But recoveryTarget was set to RECOVERY_TARGET_XID
Without -DEXEC_BACKEND all fine.
As far as I understand the guc.c code with EXEC_BACKEND all processes uses different config processing logic. We serialize all GUC and restore at process start. And we sort GUC by name in build_guc_variables - so we restore settings in wrong order. I was afraid that the GUC system does not guarantee the order of settings...
What is preferable solution? Seems we can not simple change this logic.
I think i can reorganize all new recovery_target_* GUC into single one with format, for example, recovery_target = "xid:607" (was mentioned in patch discussion).
regards, Sergei
On 26/11/2018 13:32, Sergei Kornilov wrote:
Timed out while waiting for standby to catch up at
t/003_recovery_targets.pl line 34.I can reproduce this and notice wrong assign settings order. For example standby_6 has
recovery_target_name = '$recovery_name'
recovery_target_xid = '$recovery_txid'
recovery_target_time = '$recovery_time'But recoveryTarget was set to RECOVERY_TARGET_XID
Without -DEXEC_BACKEND all fine.As far as I understand the guc.c code with EXEC_BACKEND all processes uses different config processing logic. We serialize all GUC and restore at process start. And we sort GUC by name in build_guc_variables - so we restore settings in wrong order. I was afraid that the GUC system does not guarantee the order of settings...
What is preferable solution? Seems we can not simple change this logic.
What is the reason for allowing multiple recovery_target_* settings and
taking the last one? Could we change this aspect to make this behave
better?
I think i can reorganize all new recovery_target_* GUC into single one with format, for example, recovery_target = "xid:607" (was mentioned in patch discussion).
That would be another option.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Mon, Nov 26, 2018 at 02:06:36PM +0100, Peter Eisentraut wrote:
What is the reason for allowing multiple recovery_target_* settings and
taking the last one?
This came originally to check that recovery.conf handles correctly its
recovery targets when multiple ones are defined with the last one
winning, as users may want to append additional targets in an existing
recovery.conf. I have not checked the code in details, but I think that
we should be careful to keep that property, even with EXEC_BACKEND.
--
Michael
Hi
What is the reason for allowing multiple recovery_target_* settings and
taking the last one? Could we change this aspect to make this behave
better?
Correct response to user configuration, similar to old recovery.conf logic or other GUC - we allow redefine with rule "latest win".
I think we can disallow using multiple recovery_target_* settings. But currently i have no good idea how this check can be done in check_* callbacks.
regards, Sergei
Greetings,
* Michael Paquier (michael@paquier.xyz) wrote:
On Mon, Nov 26, 2018 at 02:06:36PM +0100, Peter Eisentraut wrote:
What is the reason for allowing multiple recovery_target_* settings and
taking the last one?This came originally to check that recovery.conf handles correctly its
recovery targets when multiple ones are defined with the last one
winning, as users may want to append additional targets in an existing
recovery.conf. I have not checked the code in details, but I think that
we should be careful to keep that property, even with EXEC_BACKEND.
Ugh, no, I don't agree that this property makes sense to keep and since
we're making these changes, I'd argue that it's exactly the right time
to do away with that.
I would think we'd have the different GUCs and then the check functions
would only validate that they're valid inputs and then when we get to
the point where we're starting to do recovery we check and make sure
we've been given a sane overall configuration- which means that only
*one* is set, and it matches the recovery target requested.
As with backup, recovery should be extremely clear and straight-forward,
with alarms going off if there's something unclear or inconsistent. One
of the arguments around using postgresql.auto.conf (or any other managed
config file, as discussed elsewhere) is that it can be machine-processed
and therefore tooling around it can make sure that what goes into that
file is correct and sane and doesn't need to rely on being able to just
append things to the file and hope that it works.
Thanks!
Stephen
At 2018-11-26 10:18:52 -0500, sfrost@snowman.net wrote:
This came originally to check that recovery.conf handles correctly
its recovery targets when multiple ones are defined with the last
one winning […]Ugh, no, I don't agree that this property makes sense to keep and
since we're making these changes, I'd argue that it's exactly the
right time to do away with that.
I strongly agree with everything Stephen said here.
-- Abhijit
On 11/26/18 10:35 AM, Abhijit Menon-Sen wrote:
At 2018-11-26 10:18:52 -0500, sfrost@snowman.net wrote:
This came originally to check that recovery.conf handles correctly
its recovery targets when multiple ones are defined with the last
one winning […]Ugh, no, I don't agree that this property makes sense to keep and
since we're making these changes, I'd argue that it's exactly the
right time to do away with that.I strongly agree with everything Stephen said here.
+1.
--
-David
david@pgmasters.net
Hello
I would think we'd have the different GUCs and then the check functions
would only validate that they're valid inputs and then when we get to
the point where we're starting to do recovery we check and make sure
we've been given a sane overall configuration- which means that only
*one* is set, and it matches the recovery target requested.
How about something like attached patch?
regards, Sergei
Attachments:
disallow_multiple_recovery_targets_v001.patchtext/x-diff; name=disallow_multiple_recovery_targets_v001.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index db1a2d4..d95d0a7 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -3221,7 +3221,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
<varname>recovery_target_lsn</varname>, <varname>recovery_target_name</varname>,
<varname>recovery_target_time</varname>, or <varname>recovery_target_xid</varname>
can be used; if more than one of these is specified in the configuration
- file, the last entry will be used.
+ file, a fatal error will be raised.
These parameters can only be set at server start.
</para>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c80b14e..01fbab8 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -261,11 +261,18 @@ static bool restoredFromArchive = false;
static char *replay_image_masked = NULL;
static char *master_image_masked = NULL;
+/* recovery_target* original GUC values */
+char *recovery_target_string;
+char *recovery_target_xid_string;
+char *recovery_target_time_string;
+char *recovery_target_name_string;
+char *recovery_target_lsn_string;
+
/* options formerly taken from recovery.conf for archive recovery */
char *recoveryRestoreCommand = NULL;
char *recoveryEndCommand = NULL;
char *archiveCleanupCommand = NULL;
-RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
+static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
bool recoveryTargetInclusive = true;
int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
TransactionId recoveryTargetXid;
@@ -5381,9 +5388,42 @@ readRecoverySignalFile(void)
static void
validateRecoveryParameters(void)
{
+ uint8 targetSettingsCount = 0;
+
if (!ArchiveRecoveryRequested)
return;
+ /* Check for mutually exclusive target actions */
+ if (strcmp(recovery_target_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
+ }
+ if (strcmp(recovery_target_name_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_NAME;
+ }
+ if (strcmp(recovery_target_lsn_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_LSN;
+ }
+ if (strcmp(recovery_target_xid_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_XID;
+ }
+ if (strcmp(recovery_target_time_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_TIME;
+ }
+ if (targetSettingsCount > 1)
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("can not specify multiple recovery targets")));
+
/*
* Check for compulsory parameters
*/
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6497393..fd85484 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -199,7 +199,6 @@ static const char *show_data_directory_mode(void);
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(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_time(char **newval, void **extra, GucSource source);
@@ -549,12 +548,6 @@ static bool data_checksums;
static bool integer_datetimes;
static bool assert_enabled;
static char *recovery_target_timeline_string;
-static char *recovery_target_string;
-static char *recovery_target_xid_string;
-static char *recovery_target_time_string;
-static char *recovery_target_name_string;
-static char *recovery_target_lsn_string;
-
/* should be static, but commands/variable.c needs to get at this */
char *role_string;
@@ -3385,7 +3378,7 @@ static struct config_string ConfigureNamesString[] =
},
&recovery_target_string,
"",
- check_recovery_target, assign_recovery_target, NULL
+ check_recovery_target, NULL, NULL
},
{
{"recovery_target_xid", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
@@ -11062,18 +11055,6 @@ check_recovery_target(char **newval, void **extra, GucSource source)
return true;
}
-static void
-assign_recovery_target(const char *newval, void *extra)
-{
- if (newval && strcmp(newval, "") != 0)
- recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
- else
- /*
- * Reset recoveryTarget to RECOVERY_TARGET_UNSET to proper handle user
- * setting multiple recovery_target with blank value on last.
- */
- recoveryTarget = RECOVERY_TARGET_UNSET;
-}
static bool
check_recovery_target_xid(char **newval, void **extra, GucSource source)
@@ -11100,11 +11081,8 @@ assign_recovery_target_xid(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_XID;
recoveryTargetXid = *((TransactionId *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11163,11 +11141,8 @@ assign_recovery_target_time(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_TIME;
recoveryTargetTime = *((TimestampTz *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11188,11 +11163,8 @@ assign_recovery_target_name(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_NAME;
recoveryTargetName = (char *) newval;
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11242,11 +11214,8 @@ assign_recovery_target_lsn(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_LSN;
recoveryTargetLSN = *((XLogRecPtr *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index f3a7ba4..0addbb7 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -127,13 +127,17 @@ extern int recoveryTargetAction;
extern int recovery_min_apply_delay;
extern char *PrimaryConnInfo;
extern char *PrimarySlotName;
+extern char *recovery_target_string;
+extern char *recovery_target_xid_string;
+extern char *recovery_target_time_string;
+extern char *recovery_target_name_string;
+extern char *recovery_target_lsn_string;
/* indirectly set via GUC system */
extern TransactionId recoveryTargetXid;
extern TimestampTz recoveryTargetTime;
extern char *recoveryTargetName;
extern XLogRecPtr recoveryTargetLSN;
-extern RecoveryTargetType recoveryTarget;
extern char *PromoteTriggerFile;
extern RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal;
extern TimeLineID recoveryTargetTLIRequested;
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index f6f2e8b..1a90ad9 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -3,7 +3,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
-use Test::More tests => 9;
+use Test::More tests => 5;
# Create and test a standby from given backup, with a certain recovery target.
# Choose $until_lsn later than the transaction commit that causes the row
@@ -114,32 +114,3 @@ test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
@recovery_params = ("recovery_target_lsn = '$recovery_lsn'");
test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
"5000", $lsn5);
-
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).
-@recovery_params = (
- "recovery_target_name = '$recovery_name'",
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'");
-test_recovery_standby('name + XID + time',
- 'standby_6', $node_master, \@recovery_params, "3000", $lsn3);
-@recovery_params = (
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'",
- "recovery_target_xid = '$recovery_txid'");
-test_recovery_standby('time + name + XID',
- 'standby_7', $node_master, \@recovery_params, "2000", $lsn2);
-@recovery_params = (
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'");
-test_recovery_standby('XID + time + name',
- 'standby_8', $node_master, \@recovery_params, "4000", $lsn4);
-@recovery_params = (
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'",
- "recovery_target_lsn = '$recovery_lsn'",);
-test_recovery_standby('XID + time + name + LSN',
- 'standby_9', $node_master, \@recovery_params, "5000", $lsn5);
Greetings,
* Sergei Kornilov (sk@zsrv.org) wrote:
I would think we'd have the different GUCs and then the check functions
would only validate that they're valid inputs and then when we get to
the point where we're starting to do recovery we check and make sure
we've been given a sane overall configuration- which means that only
*one* is set, and it matches the recovery target requested.How about something like attached patch?
I've not been following this very closely, but seems like
recovery_target_string is a bad idea.. Why not just make that
'recovery_target_immediate' and make it a boolean? Having a GUC that's
only got one valid value seems really odd.
+ if (targetSettingsCount > 1) + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("can not specify multiple recovery targets")));
I think I would have done 'if (targetSettingsCount != 1)' here.
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).
You could (and imv should) include a test that throws an expected error
if multiple targets are specified.
Thanks!
Stephen
On 2018-Nov-26, Stephen Frost wrote:
I would think we'd have the different GUCs and then the check functions
would only validate that they're valid inputs and then when we get to
the point where we're starting to do recovery we check and make sure
we've been given a sane overall configuration- which means that only
*one* is set, and it matches the recovery target requested.
I don't quite understand why it isn't sensible to specify more than one
and just stop recovery (or whatever) when at least one of them becomes
true. Maybe I want to terminate just before commit of transaction
12345, but no later than 2018-11-11 12:47 in any case.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 2018-11-26 15:09:43 -0300, Alvaro Herrera wrote:
On 2018-Nov-26, Stephen Frost wrote:
I would think we'd have the different GUCs and then the check functions
would only validate that they're valid inputs and then when we get to
the point where we're starting to do recovery we check and make sure
we've been given a sane overall configuration- which means that only
*one* is set, and it matches the recovery target requested.I don't quite understand why it isn't sensible to specify more than one
and just stop recovery (or whatever) when at least one of them becomes
true. Maybe I want to terminate just before commit of transaction
12345, but no later than 2018-11-11 12:47 in any case.
+1
Greetings,
On Mon, Nov 26, 2018 at 13:15 Andres Freund <andres@anarazel.de> wrote:
On 2018-11-26 15:09:43 -0300, Alvaro Herrera wrote:
On 2018-Nov-26, Stephen Frost wrote:
I would think we'd have the different GUCs and then the check functions
would only validate that they're valid inputs and then when we get to
the point where we're starting to do recovery we check and make sure
we've been given a sane overall configuration- which means that only
*one* is set, and it matches the recovery target requested.I don't quite understand why it isn't sensible to specify more than one
and just stop recovery (or whatever) when at least one of them becomes
true. Maybe I want to terminate just before commit of transaction
12345, but no later than 2018-11-11 12:47 in any case.
I really have doubts about that being a serious use-case. If you know the
xid then you almost certainly want everything before that xid, and you use
the time stamp when you don’t know the xid (which is pretty much always..).
+1
Well, we could start with: that isn’t how things work today, nor how it
used to work before this patch, and we’ve not had anyone asking for it
except for people on this thread making things up.
Let’s at least fix the currently committed patch before adding new features
or changing how things in recovery work.
Thanks!
Stephen
Show quoted text
On 2018-11-26 13:30:18 -0500, Stephen Frost wrote:
Well, we could start with: that isn’t how things work today, nor how it
used to work before this patch, and we’ve not had anyone asking for it
except for people on this thread making things up.
Thanks for assuming good faith.
Hello
I think I would have done 'if (targetSettingsCount != 1)' here.
Streaming replication does not have any recovery_target.
Well, i can check StandbyModeRequested too and allow 0 recovery_target only for standby mode.
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).You could (and imv should) include a test that throws an expected error
if multiple targets are specified.
We have some example for checking unable-to-start DB? Did not find yet
Let’s at least fix the currently committed patch before adding new features or changing how things in recovery work.
+1
regards, Sergei
Greetings,
* Sergei Kornilov (sk@zsrv.org) wrote:
I think I would have done 'if (targetSettingsCount != 1)' here.
Streaming replication does not have any recovery_target.
Well, i can check StandbyModeRequested too and allow 0 recovery_target only for standby mode.
Ah. Agreed.
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).You could (and imv should) include a test that throws an expected error
if multiple targets are specified.We have some example for checking unable-to-start DB? Did not find yet
The TAP system in general supports expected failures, but that might
only be with the simple binaries, not sure. I know src/bin/pg_dump has
those though.
Let’s at least fix the currently committed patch before adding new features or changing how things in recovery work.
+1
Thanks!
Stephen
On 11/26/18 1:15 PM, Andres Freund wrote:
On 2018-11-26 15:09:43 -0300, Alvaro Herrera wrote:
On 2018-Nov-26, Stephen Frost wrote:
I would think we'd have the different GUCs and then the check functions
would only validate that they're valid inputs and then when we get to
the point where we're starting to do recovery we check and make sure
we've been given a sane overall configuration- which means that only
*one* is set, and it matches the recovery target requested.I don't quite understand why it isn't sensible to specify more than one
and just stop recovery (or whatever) when at least one of them becomes
true. Maybe I want to terminate just before commit of transaction
12345, but no later than 2018-11-11 12:47 in any case.+1
-1. At least for now.
Prior to this patch the last target specified in recovery.conf was the
one used, not a combination of them.
The committed patch did not propose to change that behavior as far as I
can see. Since there is no way to determine the order of GUCs like
there was for options in recovery.conf, I think it makes sense to
restrict it to a single target type for now. This is not exactly the
behavior we had before but I think it comes the closest.
Allowing multiple targets could be considered as a separate patch if
anyone is interested.
Regards,
--
-David
david@pgmasters.net
Hello
Updated patch attached:
- added testcase to verify database does not start with multiple recovery targets
- recovery_target = immediate was replaced with recovery_target_immediate bool GUC
thank you!
regards, Sergei
Attachments:
disallow_multiple_recovery_targets_v002.patchtext/x-diff; name=disallow_multiple_recovery_targets_v002.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index db1a2d4..49caa0c 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -3217,19 +3217,19 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
<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>,
+ At most one of <varname>recovery_target_immediate</varname>,
<varname>recovery_target_lsn</varname>, <varname>recovery_target_name</varname>,
<varname>recovery_target_time</varname>, or <varname>recovery_target_xid</varname>
can be used; if more than one of these is specified in the configuration
- file, the last entry will be used.
+ file, a fatal error will be raised.
These parameters can only be set at server start.
</para>
<variablelist>
- <varlistentry id="guc-recovery-target" xreflabel="recovery_target">
- <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
+ <varlistentry id="guc-recovery-target-immediate" xreflabel="recovery_target_immediate">
+ <term><varname>recovery_target_immediate</varname> (<type>boolean</type>)
<indexterm>
- <primary><varname>recovery_target</varname> configuration parameter</primary>
+ <primary><varname>recovery_target_immediate</varname> configuration parameter</primary>
</indexterm>
</term>
<listitem>
@@ -3239,10 +3239,6 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
from an online backup, this means the point where taking the backup
ended.
</para>
- <para>
- Technically, this is a string parameter, but <literal>'immediate'</literal>
- is currently the only allowed value.
- </para>
</listitem>
</varlistentry>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c80b14e..cd29606 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -261,13 +261,21 @@ static bool restoredFromArchive = false;
static char *replay_image_masked = NULL;
static char *master_image_masked = NULL;
+/* recovery_target* original GUC values */
+char *recovery_target_string;
+char *recovery_target_xid_string;
+char *recovery_target_time_string;
+char *recovery_target_name_string;
+char *recovery_target_lsn_string;
+
/* options formerly taken from recovery.conf for archive recovery */
char *recoveryRestoreCommand = NULL;
char *recoveryEndCommand = NULL;
char *archiveCleanupCommand = NULL;
-RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
+static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
bool recoveryTargetInclusive = true;
int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
+bool recoveryTargetImmediate;
TransactionId recoveryTargetXid;
TimestampTz recoveryTargetTime;
char *recoveryTargetName;
@@ -5381,9 +5389,42 @@ readRecoverySignalFile(void)
static void
validateRecoveryParameters(void)
{
+ uint8 targetSettingsCount = 0;
+
if (!ArchiveRecoveryRequested)
return;
+ /* Check for mutually exclusive target actions */
+ if (recoveryTargetImmediate)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
+ }
+ if (strcmp(recovery_target_name_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_NAME;
+ }
+ if (strcmp(recovery_target_lsn_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_LSN;
+ }
+ if (strcmp(recovery_target_xid_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_XID;
+ }
+ if (strcmp(recovery_target_time_string, "") != 0)
+ {
+ ++targetSettingsCount;
+ recoveryTarget = RECOVERY_TARGET_TIME;
+ }
+ if (targetSettingsCount > 1)
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("can not specify multiple recovery targets")));
+
/*
* Check for compulsory parameters
*/
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6497393..9d6879c 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -198,8 +198,6 @@ static const char *show_log_file_mode(void);
static const char *show_data_directory_mode(void);
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(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_time(char **newval, void **extra, GucSource source);
@@ -549,12 +547,6 @@ static bool data_checksums;
static bool integer_datetimes;
static bool assert_enabled;
static char *recovery_target_timeline_string;
-static char *recovery_target_string;
-static char *recovery_target_xid_string;
-static char *recovery_target_time_string;
-static char *recovery_target_name_string;
-static char *recovery_target_lsn_string;
-
/* should be static, but commands/variable.c needs to get at this */
char *role_string;
@@ -1674,6 +1666,16 @@ static struct config_bool ConfigureNamesBool[] =
},
{
+ {"recovery_target_immediate", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
+ gettext_noop("End recovery as soon as a consistent state is reached."),
+ NULL
+ },
+ &recoveryTargetImmediate,
+ false,
+ NULL, NULL, NULL
+ },
+
+ {
{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
gettext_noop("Allows connections and queries during recovery."),
NULL
@@ -3379,15 +3381,6 @@ static struct config_string ConfigureNamesString[] =
},
{
- {"recovery_target", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
- gettext_noop("Set to 'immediate' to end recovery as soon as a consistent state is reached."),
- NULL
- },
- &recovery_target_string,
- "",
- check_recovery_target, assign_recovery_target, NULL
- },
- {
{"recovery_target_xid", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
gettext_noop("Sets the transaction ID up to which recovery will proceed."),
NULL
@@ -11052,30 +11045,6 @@ assign_recovery_target_timeline(const char *newval, void *extra)
}
static bool
-check_recovery_target(char **newval, void **extra, GucSource source)
-{
- if (strcmp(*newval, "immediate") != 0 && strcmp(*newval, "") != 0)
- {
- GUC_check_errdetail("The only allowed value is \"immediate\".");
- return false;
- }
- return true;
-}
-
-static void
-assign_recovery_target(const char *newval, void *extra)
-{
- if (newval && strcmp(newval, "") != 0)
- recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
- else
- /*
- * Reset recoveryTarget to RECOVERY_TARGET_UNSET to proper handle user
- * setting multiple recovery_target with blank value on last.
- */
- recoveryTarget = RECOVERY_TARGET_UNSET;
-}
-
-static bool
check_recovery_target_xid(char **newval, void **extra, GucSource source)
{
if (strcmp(*newval, "") != 0)
@@ -11100,11 +11069,8 @@ assign_recovery_target_xid(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_XID;
recoveryTargetXid = *((TransactionId *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11163,11 +11129,8 @@ assign_recovery_target_time(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_TIME;
recoveryTargetTime = *((TimestampTz *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11188,11 +11151,8 @@ assign_recovery_target_name(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_NAME;
recoveryTargetName = (char *) newval;
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11242,11 +11202,8 @@ assign_recovery_target_lsn(const char *newval, void *extra)
{
if (newval && strcmp(newval, "") != 0)
{
- recoveryTarget = RECOVERY_TARGET_LSN;
recoveryTargetLSN = *((XLogRecPtr *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index ee9ec6a..ccdc94d 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -246,8 +246,8 @@
# Set these only when performing a targeted recovery.
-#recovery_target = '' # 'immediate' to end recovery as soon as a
- # consistent state is reached
+#recovery_target_immediate = false # end recovery as soon as a
+ # consistent state is reached
# (change requires restart)
#recovery_target_name = '' # the named restore point to which recovery will proceed
# (change requires restart)
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index f3a7ba4..d257f4f 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -127,13 +127,17 @@ extern int recoveryTargetAction;
extern int recovery_min_apply_delay;
extern char *PrimaryConnInfo;
extern char *PrimarySlotName;
+extern bool recoveryTargetImmediate;
+extern char *recovery_target_xid_string;
+extern char *recovery_target_time_string;
+extern char *recovery_target_name_string;
+extern char *recovery_target_lsn_string;
/* indirectly set via GUC system */
extern TransactionId recoveryTargetXid;
extern TimestampTz recoveryTargetTime;
extern char *recoveryTargetName;
extern XLogRecPtr recoveryTargetLSN;
-extern RecoveryTargetType recoveryTarget;
extern char *PromoteTriggerFile;
extern RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal;
extern TimeLineID recoveryTargetTLIRequested;
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index f6f2e8b..df261f0 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -3,7 +3,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
-use Test::More tests => 9;
+use Test::More tests => 6;
# Create and test a standby from given backup, with a certain recovery target.
# Choose $until_lsn later than the transaction commit that causes the row
@@ -53,7 +53,7 @@ $node_master->init(has_archiving => 1, allows_streaming => 1);
$node_master->start;
# Create data before taking the backup, aimed at testing
-# recovery_target = 'immediate'
+# recovery_target_immediate
$node_master->safe_psql('postgres',
"CREATE TABLE tab_int AS SELECT generate_series(1,1000) AS a");
my $lsn1 =
@@ -99,7 +99,7 @@ $node_master->safe_psql('postgres',
$node_master->safe_psql('postgres', "SELECT pg_switch_wal()");
# Test recovery targets
-my @recovery_params = ("recovery_target = 'immediate'");
+my @recovery_params = ("recovery_target_immediate = true");
test_recovery_standby('immediate target',
'standby_1', $node_master, \@recovery_params, "1000", $lsn1);
@recovery_params = ("recovery_target_xid = '$recovery_txid'");
@@ -115,31 +115,11 @@ test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
"5000", $lsn5);
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).
-@recovery_params = (
- "recovery_target_name = '$recovery_name'",
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'");
-test_recovery_standby('name + XID + time',
- 'standby_6', $node_master, \@recovery_params, "3000", $lsn3);
-@recovery_params = (
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'",
- "recovery_target_xid = '$recovery_txid'");
-test_recovery_standby('time + name + XID',
- 'standby_7', $node_master, \@recovery_params, "2000", $lsn2);
-@recovery_params = (
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'");
-test_recovery_standby('XID + time + name',
- 'standby_8', $node_master, \@recovery_params, "4000", $lsn4);
-@recovery_params = (
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'",
- "recovery_target_lsn = '$recovery_lsn'",);
-test_recovery_standby('XID + time + name + LSN',
- 'standby_9', $node_master, \@recovery_params, "5000", $lsn5);
+# multiple recovery targets are disallowed
+my $node_standby6 = get_new_node('standby_6');
+$node_standby6->init_from_backup($node_master, 'my_backup',
+ has_restoring => 1);
+$node_standby6->append_conf('postgresql.conf', "recovery_target_lsn = '$recovery_lsn'");
+$node_standby6->append_conf('postgresql.conf', "recovery_target_xid = '$recovery_txid'");
+$node_standby6->command_fails(['pg_ctl', '-D', $node_standby6->data_dir, '-l',
+ $node_standby6->logfile, 'start']);
Greetings,
* Sergei Kornilov (sk@zsrv.org) wrote:
Updated patch attached:
- added testcase to verify database does not start with multiple recovery targets
- recovery_target = immediate was replaced with recovery_target_immediate bool GUC
I'd encourage you to look through the diff after you're finished hacking
before sending it to the list, in case things get left in that should be
removed, as below...
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index c80b14e..cd29606 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -261,13 +261,21 @@ static bool restoredFromArchive = false; static char *replay_image_masked = NULL; static char *master_image_masked = NULL;+/* recovery_target* original GUC values */ +char *recovery_target_string;
This shouldn't be here now, should it?
+char *recovery_target_xid_string; +char *recovery_target_time_string; +char *recovery_target_name_string; +char *recovery_target_lsn_string;
/* options formerly taken from recovery.conf for archive recovery */
Shouldn't all of the above be listed under this comment..?
char *recoveryRestoreCommand = NULL; char *recoveryEndCommand = NULL; char *archiveCleanupCommand = NULL; -RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; +static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; bool recoveryTargetInclusive = true; int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; +bool recoveryTargetImmediate;
Seems like this should, at least, be close to the char*'s that are
defining the other ways to specify a recovery targer.
TransactionId recoveryTargetXid;
TimestampTz recoveryTargetTime;
char *recoveryTargetName;
@@ -5381,9 +5389,42 @@ readRecoverySignalFile(void)
static void
validateRecoveryParameters(void)
{
+ uint8 targetSettingsCount = 0;
+
if (!ArchiveRecoveryRequested)
return;+ /* Check for mutually exclusive target actions */ + if (recoveryTargetImmediate) + { + ++targetSettingsCount; + recoveryTarget = RECOVERY_TARGET_IMMEDIATE; + } + if (strcmp(recovery_target_name_string, "") != 0) + { + ++targetSettingsCount; + recoveryTarget = RECOVERY_TARGET_NAME; + } + if (strcmp(recovery_target_lsn_string, "") != 0) + { + ++targetSettingsCount; + recoveryTarget = RECOVERY_TARGET_LSN; + } + if (strcmp(recovery_target_xid_string, "") != 0) + { + ++targetSettingsCount; + recoveryTarget = RECOVERY_TARGET_XID; + } + if (strcmp(recovery_target_time_string, "") != 0) + { + ++targetSettingsCount; + recoveryTarget = RECOVERY_TARGET_TIME; + } + if (targetSettingsCount > 1) + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("can not specify multiple recovery targets")));
Doesn't look like you changed this based on my prior comment..?
Thanks!
Stephen
Hi
I'd encourage you to look through the diff after you're finished hacking
before sending it to the list, in case things get left in that should be
removed, as below...
I am so sorry. I have a look before sending, but...
It's night in my timezone. I will fix tomorrow.
+ if (targetSettingsCount > 1)
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("can not specify multiple recovery targets")));Doesn't look like you changed this based on my prior comment..?
Do you mean this one?
I think I would have done 'if (targetSettingsCount != 1)' here.
To be sure, we need check we have one recovery target without standby mode and none or one in standby mode? Or some another check?
regards, Sergei
Greetings,
* Sergei Kornilov (sk@zsrv.org) wrote:
I'd encourage you to look through the diff after you're finished hacking
before sending it to the list, in case things get left in that should be
removed, as below...I am so sorry. I have a look before sending, but...
It's night in my timezone. I will fix tomorrow.
Sure, I don't think there's any need to rush any of this.
+ if (targetSettingsCount > 1)
+ ereport(FATAL,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("can not specify multiple recovery targets")));Doesn't look like you changed this based on my prior comment..?
Do you mean this one?
I think I would have done 'if (targetSettingsCount != 1)' here.
To be sure, we need check we have one recovery target without standby mode and none or one in standby mode? Or some another check?
Right, I think that's what the idea was, although that might require
something for the explicit 'recover to the end case', now that I think
about it, so perhaps the >1 isn't so bad. Still seems a bit odd to me
though and I do wonder if there might be a better approach.
Thanks!
Stephen
On 26/11/2018 21:30, Sergei Kornilov wrote:
- recovery_target = immediate was replaced with recovery_target_immediate bool GUC
Why?
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Hello
- recovery_target = immediate was replaced with recovery_target_immediate bool GUC
Why?
Due this comment: /messages/by-id/20181126172118.GY3415@tamriel.snowman.net
I've not been following this very closely, but seems like
recovery_target_string is a bad idea.. Why not just make that
'recovery_target_immediate' and make it a boolean? Having a GUC that's
only got one valid value seems really odd.
regards, Sergei
On 27/11/2018 10:10, Sergei Kornilov wrote:
Hello
- recovery_target = immediate was replaced with recovery_target_immediate bool GUC
Why?
Due this comment: /messages/by-id/20181126172118.GY3415@tamriel.snowman.net
I've not been following this very closely, but seems like
recovery_target_string is a bad idea.. Why not just make that
'recovery_target_immediate' and make it a boolean? Having a GUC that's
only got one valid value seems really odd.
It is a bit odd, but that's the way it's been, and I don't see a reason
to change it as part of this fix. We are attempting to fix the way the
GUC parameters are parsed, not change the name and meaning of the
parameters themselves.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On 27/11/2018 13:29, Peter Eisentraut wrote:
On 27/11/2018 10:10, Sergei Kornilov wrote:
Hello
- recovery_target = immediate was replaced with recovery_target_immediate bool GUC
Why?
Due this comment: /messages/by-id/20181126172118.GY3415@tamriel.snowman.net
I've not been following this very closely, but seems like
recovery_target_string is a bad idea.. Why not just make that
'recovery_target_immediate' and make it a boolean? Having a GUC that's
only got one valid value seems really odd.It is a bit odd, but that's the way it's been, and I don't see a reason
to change it as part of this fix. We are attempting to fix the way the
GUC parameters are parsed, not change the name and meaning of the
parameters themselves.
The attached seems to be the simplest way to fix this. (Needs
documentation updates, test updates, error message refinement.)
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Attachments:
0001-WIP-Only-allow-one-recovery-target-setting.patchtext/plain; charset=UTF-8; name=0001-WIP-Only-allow-one-recovery-target-setting.patch; x-mac-creator=0; x-mac-type=0Download
From d5cb3f3a30bd045283c9848bd39ff012c817d908 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter_e@gmx.net>
Date: Tue, 27 Nov 2018 13:43:54 +0100
Subject: [PATCH] WIP Only allow one recovery target setting
---
src/backend/utils/misc/guc.c | 29 +++++++++++++++--------------
src/include/access/xlog.h | 2 +-
2 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6497393c03..dd2caee95b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -11065,14 +11065,11 @@ check_recovery_target(char **newval, void **extra, GucSource source)
static void
assign_recovery_target(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "multiple recovery targets specified");
+
if (newval && strcmp(newval, "") != 0)
recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
- else
- /*
- * Reset recoveryTarget to RECOVERY_TARGET_UNSET to proper handle user
- * setting multiple recovery_target with blank value on last.
- */
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11098,13 +11095,14 @@ check_recovery_target_xid(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_xid(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "multiple recovery targets specified");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_XID;
recoveryTargetXid = *((TransactionId *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11161,13 +11159,14 @@ check_recovery_target_time(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_time(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "multiple recovery targets specified");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_TIME;
recoveryTargetTime = *((TimestampTz *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11186,13 +11185,14 @@ check_recovery_target_name(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_name(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "multiple recovery targets specified");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_NAME;
recoveryTargetName = (char *) newval;
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11240,13 +11240,14 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_lsn(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "multiple recovery targets specified");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_LSN;
recoveryTargetLSN = *((XLogRecPtr *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index f3a7ba4d42..1fd4aec3c7 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -79,7 +79,7 @@ extern HotStandbyState standbyState;
*/
typedef enum
{
- RECOVERY_TARGET_UNSET,
+ RECOVERY_TARGET_UNSET = 0,
RECOVERY_TARGET_XID,
RECOVERY_TARGET_TIME,
RECOVERY_TARGET_NAME,
--
2.19.1
Hi
The attached seems to be the simplest way to fix this. (Needs
documentation updates, test updates, error message refinement.)
Thank you!
I add documentation change, tests and rephrased error message.
regards, Sergei
Attachments:
0001-WIP-Only-allow-one-recovery-target-setting-v002.patchtext/x-diff; name=0001-WIP-Only-allow-one-recovery-target-setting-v002.patchDownload
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index db1a2d4..7cbea6e 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -3221,7 +3221,7 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
<varname>recovery_target_lsn</varname>, <varname>recovery_target_name</varname>,
<varname>recovery_target_time</varname>, or <varname>recovery_target_xid</varname>
can be used; if more than one of these is specified in the configuration
- file, the last entry will be used.
+ file, a error will be raised.
These parameters can only be set at server start.
</para>
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6497393..290a157 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -11065,14 +11065,11 @@ check_recovery_target(char **newval, void **extra, GucSource source)
static void
assign_recovery_target(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "can not specify multiple recovery targets");
+
if (newval && strcmp(newval, "") != 0)
recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
- else
- /*
- * Reset recoveryTarget to RECOVERY_TARGET_UNSET to proper handle user
- * setting multiple recovery_target with blank value on last.
- */
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11098,13 +11095,14 @@ check_recovery_target_xid(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_xid(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "can not specify multiple recovery targets");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_XID;
recoveryTargetXid = *((TransactionId *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11161,13 +11159,14 @@ check_recovery_target_time(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_time(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "can not specify multiple recovery targets");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_TIME;
recoveryTargetTime = *((TimestampTz *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11186,13 +11185,14 @@ check_recovery_target_name(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_name(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "can not specify multiple recovery targets");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_NAME;
recoveryTargetName = (char *) newval;
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
@@ -11240,13 +11240,14 @@ check_recovery_target_lsn(char **newval, void **extra, GucSource source)
static void
assign_recovery_target_lsn(const char *newval, void *extra)
{
+ if (recoveryTarget)
+ elog(ERROR, "can not specify multiple recovery targets");
+
if (newval && strcmp(newval, "") != 0)
{
recoveryTarget = RECOVERY_TARGET_LSN;
recoveryTargetLSN = *((XLogRecPtr *) extra);
}
- else
- recoveryTarget = RECOVERY_TARGET_UNSET;
}
static bool
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index f3a7ba4..1fd4aec 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -79,7 +79,7 @@ extern HotStandbyState standbyState;
*/
typedef enum
{
- RECOVERY_TARGET_UNSET,
+ RECOVERY_TARGET_UNSET = 0,
RECOVERY_TARGET_XID,
RECOVERY_TARGET_TIME,
RECOVERY_TARGET_NAME,
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index f6f2e8b..cba367c 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -3,7 +3,7 @@ use strict;
use warnings;
use PostgresNode;
use TestLib;
-use Test::More tests => 9;
+use Test::More tests => 10;
# Create and test a standby from given backup, with a certain recovery target.
# Choose $until_lsn later than the transaction commit that causes the row
@@ -115,31 +115,44 @@ test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
"5000", $lsn5);
-# Multiple targets
-# Last entry has priority (note that an array respects the order of items
-# not hashes).
+# multiple recovery targets are disallowed
+sub test_recovery_multiple_targets
+{
+ my $test_name = shift;
+ my $node_name = shift;
+ my $node_master = shift;
+ my $recovery_params = shift;
+
+ my $node_standby = get_new_node($node_name);
+ $node_standby->init_from_backup($node_master, 'my_backup',
+ has_restoring => 1);
+
+ foreach my $param_item (@$recovery_params)
+ {
+ $node_standby->append_conf('postgresql.conf', qq($param_item));
+ }
+
+ $node_standby->command_fails(['pg_ctl', '-D', $node_standby->data_dir, '-l',
+ $node_standby->logfile, 'start'], "check standby content for $test_name");
+}
+
+@recovery_params = (
+ "recovery_target = 'immediate'",
+ "recovery_target_xid = '$recovery_txid'",);
+test_recovery_multiple_targets('multi XID', 'standby_6', $node_master, \@recovery_params);
@recovery_params = (
- "recovery_target_name = '$recovery_name'",
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'");
-test_recovery_standby('name + XID + time',
- 'standby_6', $node_master, \@recovery_params, "3000", $lsn3);
+ "recovery_target = 'immediate'",
+ "recovery_target_time = '$recovery_time'",);
+test_recovery_multiple_targets('multi time', 'standby_7', $node_master, \@recovery_params);
@recovery_params = (
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'",
- "recovery_target_xid = '$recovery_txid'");
-test_recovery_standby('time + name + XID',
- 'standby_7', $node_master, \@recovery_params, "2000", $lsn2);
+ "recovery_target = 'immediate'",
+ "recovery_target_name = '$recovery_name'",);
+test_recovery_multiple_targets('multi name', 'standby_8', $node_master, \@recovery_params);
@recovery_params = (
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'");
-test_recovery_standby('XID + time + name',
- 'standby_8', $node_master, \@recovery_params, "4000", $lsn4);
+ "recovery_target = 'immediate'",
+ "recovery_target_lsn = '$recovery_lsn'",);
+test_recovery_multiple_targets('multi LSN', 'standby_9', $node_master, \@recovery_params);
@recovery_params = (
- "recovery_target_xid = '$recovery_txid'",
- "recovery_target_time = '$recovery_time'",
- "recovery_target_name = '$recovery_name'",
- "recovery_target_lsn = '$recovery_lsn'",);
-test_recovery_standby('XID + time + name + LSN',
- 'standby_9', $node_master, \@recovery_params, "5000", $lsn5);
+ "recovery_target_xid = '$recovery_txid'",
+ "recovery_target = 'immediate'",);
+test_recovery_multiple_targets('multi immediate', 'standby_10', $node_master, \@recovery_params);
Greetings,
* Peter Eisentraut (peter.eisentraut@2ndquadrant.com) wrote:
On 27/11/2018 13:29, Peter Eisentraut wrote:
On 27/11/2018 10:10, Sergei Kornilov wrote:
- recovery_target = immediate was replaced with recovery_target_immediate bool GUC
Why?
Due this comment: /messages/by-id/20181126172118.GY3415@tamriel.snowman.net
I've not been following this very closely, but seems like
recovery_target_string is a bad idea.. Why not just make that
'recovery_target_immediate' and make it a boolean? Having a GUC that's
only got one valid value seems really odd.It is a bit odd, but that's the way it's been, and I don't see a reason
to change it as part of this fix. We are attempting to fix the way the
GUC parameters are parsed, not change the name and meaning of the
parameters themselves.
I disagree quite a bit- we're whacking around how recovery works and
this has been a wart since it went in because the contemplated later
changes to have more than one value be accepted there never
materialized, so we might as well change it while we're changing
everything else and clean it up.
If we really want to change it later we can do that, but we've evidently
decided to go the opposite direction and have multiple GUCs instead, so
let's be consistent.
The attached seems to be the simplest way to fix this. (Needs
documentation updates, test updates, error message refinement.)
Sure, this approach seems fine to me and is perhaps a bit cleaner.
Thanks!
Stephen
On 27/11/2018 15:16, Sergei Kornilov wrote:
Hi
The attached seems to be the simplest way to fix this. (Needs
documentation updates, test updates, error message refinement.)Thank you!
I add documentation change, tests and rephrased error message.
I have committed it along these lines. It ended up being a bit more
involved, in order to support setting the same parameter multiple times
or unsetting one parameter and setting another. But it should pass now
on EXEC_BACKEND.
--
Peter Eisentraut http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services