Commit dc788668 authored by Peter Eisentraut's avatar Peter Eisentraut

Fail if recovery target is not reached

Before, if a recovery target is configured, but the archive ended
before the target was reached, recovery would end and the server would
promote without further notice.  That was deemed to be pretty wrong.
With this change, if the recovery target is not reached, it is a fatal
error.
Based-on-patch-by: default avatarLeif Gunnar Erlandsen <leif@lako.no>
Reviewed-by: default avatarKyotaro Horiguchi <horikyota.ntt@gmail.com>
Discussion: https://www.postgresql.org/message-id/flat/993736dd3f1713ec1f63fc3b653839f5@lako.no
parent 29e321cd
...@@ -3571,6 +3571,11 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows ...@@ -3571,6 +3571,11 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
If <xref linkend="guc-hot-standby"/> is not enabled, a setting of If <xref linkend="guc-hot-standby"/> is not enabled, a setting of
<literal>pause</literal> will act the same as <literal>shutdown</literal>. <literal>pause</literal> will act the same as <literal>shutdown</literal>.
</para> </para>
<para>
In any case, if a recovery target is configured but the archive
recovery ends before the target is reached, the server will shut down
with a fatal error.
</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -6200,7 +6200,7 @@ StartupXLOG(void) ...@@ -6200,7 +6200,7 @@ StartupXLOG(void)
XLogCtlInsert *Insert; XLogCtlInsert *Insert;
CheckPoint checkPoint; CheckPoint checkPoint;
bool wasShutdown; bool wasShutdown;
bool reachedStopPoint = false; bool reachedRecoveryTarget = false;
bool haveBackupLabel = false; bool haveBackupLabel = false;
bool haveTblspcMap = false; bool haveTblspcMap = false;
XLogRecPtr RecPtr, XLogRecPtr RecPtr,
...@@ -7103,7 +7103,7 @@ StartupXLOG(void) ...@@ -7103,7 +7103,7 @@ StartupXLOG(void)
*/ */
if (recoveryStopsBefore(xlogreader)) if (recoveryStopsBefore(xlogreader))
{ {
reachedStopPoint = true; /* see below */ reachedRecoveryTarget = true;
break; break;
} }
...@@ -7258,7 +7258,7 @@ StartupXLOG(void) ...@@ -7258,7 +7258,7 @@ StartupXLOG(void)
/* Exit loop if we reached inclusive recovery target */ /* Exit loop if we reached inclusive recovery target */
if (recoveryStopsAfter(xlogreader)) if (recoveryStopsAfter(xlogreader))
{ {
reachedStopPoint = true; reachedRecoveryTarget = true;
break; break;
} }
...@@ -7270,7 +7270,7 @@ StartupXLOG(void) ...@@ -7270,7 +7270,7 @@ StartupXLOG(void)
* end of main redo apply loop * end of main redo apply loop
*/ */
if (reachedStopPoint) if (reachedRecoveryTarget)
{ {
if (!reachedConsistency) if (!reachedConsistency)
ereport(FATAL, ereport(FATAL,
...@@ -7327,7 +7327,18 @@ StartupXLOG(void) ...@@ -7327,7 +7327,18 @@ StartupXLOG(void)
/* there are no WAL records following the checkpoint */ /* there are no WAL records following the checkpoint */
ereport(LOG, ereport(LOG,
(errmsg("redo is not required"))); (errmsg("redo is not required")));
} }
/*
* This check is intentionally after the above log messages that
* indicate how far recovery went.
*/
if (ArchiveRecoveryRequested &&
recoveryTarget != RECOVERY_TARGET_UNSET &&
!reachedRecoveryTarget)
ereport(FATAL,
(errmsg("recovery ended before configured recovery target was reached")));
} }
/* /*
......
...@@ -653,6 +653,9 @@ Restoring WAL segments from archives using restore_command can be enabled ...@@ -653,6 +653,9 @@ Restoring WAL segments from archives using restore_command can be enabled
by passing the keyword parameter has_restoring => 1. This is disabled by by passing the keyword parameter has_restoring => 1. This is disabled by
default. default.
If has_restoring is used, standby mode is used by default. To use
recovery mode instead, pass the keyword parameter standby => 0.
The backup is copied, leaving the original unmodified. pg_hba.conf is The backup is copied, leaving the original unmodified. pg_hba.conf is
unconditionally set to enable replication connections. unconditionally set to enable replication connections.
...@@ -669,6 +672,7 @@ sub init_from_backup ...@@ -669,6 +672,7 @@ sub init_from_backup
$params{has_streaming} = 0 unless defined $params{has_streaming}; $params{has_streaming} = 0 unless defined $params{has_streaming};
$params{has_restoring} = 0 unless defined $params{has_restoring}; $params{has_restoring} = 0 unless defined $params{has_restoring};
$params{standby} = 1 unless defined $params{standby};
print print
"# Initializing node \"$node_name\" from backup \"$backup_name\" of node \"$root_name\"\n"; "# Initializing node \"$node_name\" from backup \"$backup_name\" of node \"$root_name\"\n";
...@@ -699,7 +703,7 @@ port = $port ...@@ -699,7 +703,7 @@ port = $port
"unix_socket_directories = '$host'"); "unix_socket_directories = '$host'");
} }
$self->enable_streaming($root_node) if $params{has_streaming}; $self->enable_streaming($root_node) if $params{has_streaming};
$self->enable_restoring($root_node) if $params{has_restoring}; $self->enable_restoring($root_node, $params{standby}) if $params{has_restoring};
return; return;
} }
...@@ -939,7 +943,7 @@ primary_conninfo='$root_connstr' ...@@ -939,7 +943,7 @@ primary_conninfo='$root_connstr'
# Internal routine to enable archive recovery command on a standby node # Internal routine to enable archive recovery command on a standby node
sub enable_restoring sub enable_restoring
{ {
my ($self, $root_node) = @_; my ($self, $root_node, $standby) = @_;
my $path = TestLib::perl2host($root_node->archive_dir); my $path = TestLib::perl2host($root_node->archive_dir);
my $name = $self->name; my $name = $self->name;
...@@ -961,7 +965,30 @@ sub enable_restoring ...@@ -961,7 +965,30 @@ sub enable_restoring
'postgresql.conf', qq( 'postgresql.conf', qq(
restore_command = '$copy_command' restore_command = '$copy_command'
)); ));
if ($standby)
{
$self->set_standby_mode(); $self->set_standby_mode();
}
else
{
$self->set_recovery_mode();
}
return;
}
=pod
=item $node->set_recovery_mode()
Place recovery.signal file.
=cut
sub set_recovery_mode
{
my ($self) = @_;
$self->append_conf('recovery.signal', '');
return; return;
} }
......
...@@ -3,7 +3,8 @@ use strict; ...@@ -3,7 +3,8 @@ use strict;
use warnings; use warnings;
use PostgresNode; use PostgresNode;
use TestLib; use TestLib;
use Test::More tests => 8; use Test::More tests => 9;
use Time::HiRes qw(usleep);
# Create and test a standby from given backup, with a certain recovery target. # 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 # Choose $until_lsn later than the transaction commit that causes the row
...@@ -145,3 +146,24 @@ ok(!$res, 'invalid recovery startup fails'); ...@@ -145,3 +146,24 @@ ok(!$res, 'invalid recovery startup fails');
my $logfile = slurp_file($node_standby->logfile()); my $logfile = slurp_file($node_standby->logfile());
ok($logfile =~ qr/multiple recovery targets specified/, ok($logfile =~ qr/multiple recovery targets specified/,
'multiple conflicting settings'); 'multiple conflicting settings');
# Check behavior when recovery ends before target is reached
$node_standby = get_new_node('standby_8');
$node_standby->init_from_backup($node_master, 'my_backup',
has_restoring => 1, standby => 0);
$node_standby->append_conf('postgresql.conf',
"recovery_target_name = 'does_not_exist'");
run_log(['pg_ctl', '-D', $node_standby->data_dir,
'-l', $node_standby->logfile, 'start']);
# wait up to 10 seconds for postgres to terminate
foreach my $i (0..100)
{
last if ! -f $node_standby->data_dir . '/postmaster.pid';
usleep(100_000);
}
$logfile = slurp_file($node_standby->logfile());
ok($logfile =~ qr/FATAL: recovery ended before configured recovery target was reached/,
'recovery end before target reached is a fatal error');
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment