New recovery target recovery_target_lsn

Michael Paquier
This commit is contained in:
Simon Riggs 2016-09-03 17:48:01 +01:00
parent 0c40ab3a88
commit 35250b6ad7
5 changed files with 120 additions and 9 deletions

View File

@ -157,9 +157,10 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
By default, recovery will recover to the end of the WAL log. The By default, recovery will recover to the end of the WAL log. The
following parameters can be used to specify an earlier stopping point. following parameters can be used to specify an earlier stopping point.
At most one of <varname>recovery_target</>, At most one of <varname>recovery_target</>,
<varname>recovery_target_name</>, <varname>recovery_target_time</>, or <varname>recovery_target_lsn</>, <varname>recovery_target_name</>,
<varname>recovery_target_xid</> can be used; if more than one of these <varname>recovery_target_time</>, or <varname>recovery_target_xid</>
is specified in the configuration file, the last entry will be used. can be used; if more than one of these is specified in the configuration
file, the last entry will be used.
</para> </para>
<variablelist> <variablelist>
@ -232,6 +233,23 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows
</para> </para>
</listitem> </listitem>
</varlistentry> </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> </variablelist>
<para> <para>

View File

@ -67,8 +67,8 @@
# must set a recovery target. # must set a recovery target.
# #
# You may set a recovery target either by transactionId, by name, # You may set a recovery target either by transactionId, by name,
# or by timestamp. Recovery may either include or exclude the # by timestamp or by WAL position (LSN). Recovery may either include or
# transaction(s) with the recovery target value (ie, stop either # exclude the transaction(s) with the recovery target value (ie, stop either
# just after or just before the given target, respectively). # just after or just before the given target, respectively).
# #
# #
@ -78,6 +78,8 @@
# #
#recovery_target_xid = '' #recovery_target_xid = ''
# #
#recovery_target_lsn = '' # e.g. '0/70006B8'
#
#recovery_target_inclusive = true #recovery_target_inclusive = true
# #
# #

View File

@ -67,6 +67,7 @@
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/guc.h" #include "utils/guc.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/pg_lsn.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
#include "utils/relmapper.h" #include "utils/relmapper.h"
#include "utils/snapmgr.h" #include "utils/snapmgr.h"
@ -254,6 +255,7 @@ static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE;
static TransactionId recoveryTargetXid; static TransactionId recoveryTargetXid;
static TimestampTz recoveryTargetTime; static TimestampTz recoveryTargetTime;
static char *recoveryTargetName; static char *recoveryTargetName;
static XLogRecPtr recoveryTargetLSN;
static int recovery_min_apply_delay = 0; static int recovery_min_apply_delay = 0;
static TimestampTz recoveryDelayUntilTime; static TimestampTz recoveryDelayUntilTime;
@ -275,6 +277,7 @@ static bool fast_promote = false;
*/ */
static TransactionId recoveryStopXid; static TransactionId recoveryStopXid;
static TimestampTz recoveryStopTime; static TimestampTz recoveryStopTime;
static XLogRecPtr recoveryStopLSN;
static char recoveryStopName[MAXFNAMELEN]; static char recoveryStopName[MAXFNAMELEN];
static bool recoveryStopAfter; static bool recoveryStopAfter;
@ -5078,6 +5081,23 @@ readRecoveryCommandFile(void)
(errmsg_internal("recovery_target_name = '%s'", (errmsg_internal("recovery_target_name = '%s'",
recoveryTargetName))); 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) else if (strcmp(item->name, "recovery_target") == 0)
{ {
if (strcmp(item->value, "immediate") == 0) if (strcmp(item->value, "immediate") == 0)
@ -5400,11 +5420,29 @@ recoveryStopsBefore(XLogReaderState *record)
recoveryStopAfter = false; recoveryStopAfter = false;
recoveryStopXid = InvalidTransactionId; recoveryStopXid = InvalidTransactionId;
recoveryStopLSN = InvalidXLogRecPtr;
recoveryStopTime = 0; recoveryStopTime = 0;
recoveryStopName[0] = '\0'; recoveryStopName[0] = '\0';
return true; return true;
} }
/* Check if target LSN has been reached */
if (recoveryTarget == RECOVERY_TARGET_LSN &&
!recoveryTargetInclusive &&
record->ReadRecPtr >= recoveryTargetLSN)
{
recoveryStopAfter = false;
recoveryStopXid = InvalidTransactionId;
recoveryStopLSN = record->ReadRecPtr;
recoveryStopTime = 0;
recoveryStopName[0] = '\0';
ereport(LOG,
(errmsg("recovery stopping before WAL position (LSN) \"%X/%X\"",
(uint32) (recoveryStopLSN >> 32),
(uint32) recoveryStopLSN)));
return true;
}
/* Otherwise we only consider stopping before COMMIT or ABORT records. */ /* Otherwise we only consider stopping before COMMIT or ABORT records. */
if (XLogRecGetRmid(record) != RM_XACT_ID) if (XLogRecGetRmid(record) != RM_XACT_ID)
return false; return false;
@ -5479,6 +5517,7 @@ recoveryStopsBefore(XLogReaderState *record)
recoveryStopAfter = false; recoveryStopAfter = false;
recoveryStopXid = recordXid; recoveryStopXid = recordXid;
recoveryStopTime = recordXtime; recoveryStopTime = recordXtime;
recoveryStopLSN = InvalidXLogRecPtr;
recoveryStopName[0] = '\0'; recoveryStopName[0] = '\0';
if (isCommit) if (isCommit)
@ -5532,6 +5571,7 @@ recoveryStopsAfter(XLogReaderState *record)
{ {
recoveryStopAfter = true; recoveryStopAfter = true;
recoveryStopXid = InvalidTransactionId; recoveryStopXid = InvalidTransactionId;
recoveryStopLSN = InvalidXLogRecPtr;
(void) getRecordTimestamp(record, &recoveryStopTime); (void) getRecordTimestamp(record, &recoveryStopTime);
strlcpy(recoveryStopName, recordRestorePointData->rp_name, MAXFNAMELEN); strlcpy(recoveryStopName, recordRestorePointData->rp_name, MAXFNAMELEN);
@ -5543,6 +5583,23 @@ recoveryStopsAfter(XLogReaderState *record)
} }
} }
/* Check if the target LSN has been reached */
if (recoveryTarget == RECOVERY_TARGET_LSN &&
recoveryTargetInclusive &&
record->ReadRecPtr >= recoveryTargetLSN)
{
recoveryStopAfter = true;
recoveryStopXid = InvalidTransactionId;
recoveryStopLSN = record->ReadRecPtr;
recoveryStopTime = 0;
recoveryStopName[0] = '\0';
ereport(LOG,
(errmsg("recovery stopping after WAL position (LSN) \"%X/%X\"",
(uint32) (recoveryStopLSN >> 32),
(uint32) recoveryStopLSN)));
return true;
}
if (rmid != RM_XACT_ID) if (rmid != RM_XACT_ID)
return false; return false;
@ -5598,6 +5655,7 @@ recoveryStopsAfter(XLogReaderState *record)
recoveryStopAfter = true; recoveryStopAfter = true;
recoveryStopXid = recordXid; recoveryStopXid = recordXid;
recoveryStopTime = recordXtime; recoveryStopTime = recordXtime;
recoveryStopLSN = InvalidXLogRecPtr;
recoveryStopName[0] = '\0'; recoveryStopName[0] = '\0';
if (xact_info == XLOG_XACT_COMMIT || if (xact_info == XLOG_XACT_COMMIT ||
@ -5629,6 +5687,7 @@ recoveryStopsAfter(XLogReaderState *record)
recoveryStopAfter = true; recoveryStopAfter = true;
recoveryStopXid = InvalidTransactionId; recoveryStopXid = InvalidTransactionId;
recoveryStopTime = 0; recoveryStopTime = 0;
recoveryStopLSN = InvalidXLogRecPtr;
recoveryStopName[0] = '\0'; recoveryStopName[0] = '\0';
return true; return true;
} }
@ -6055,6 +6114,11 @@ StartupXLOG(void)
ereport(LOG, ereport(LOG,
(errmsg("starting point-in-time recovery to \"%s\"", (errmsg("starting point-in-time recovery to \"%s\"",
recoveryTargetName))); recoveryTargetName)));
else if (recoveryTarget == 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) else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
ereport(LOG, ereport(LOG,
(errmsg("starting point-in-time recovery to earliest consistent point"))); (errmsg("starting point-in-time recovery to earliest consistent point")));
@ -7124,6 +7188,12 @@ StartupXLOG(void)
"%s %s\n", "%s %s\n",
recoveryStopAfter ? "after" : "before", recoveryStopAfter ? "after" : "before",
timestamptz_to_str(recoveryStopTime)); timestamptz_to_str(recoveryStopTime));
else if (recoveryTarget == 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 (recoveryTarget == RECOVERY_TARGET_NAME)
snprintf(reason, sizeof(reason), snprintf(reason, sizeof(reason),
"at restore point \"%s\"", "at restore point \"%s\"",

View File

@ -83,6 +83,7 @@ typedef enum
RECOVERY_TARGET_XID, RECOVERY_TARGET_XID,
RECOVERY_TARGET_TIME, RECOVERY_TARGET_TIME,
RECOVERY_TARGET_NAME, RECOVERY_TARGET_NAME,
RECOVERY_TARGET_LSN,
RECOVERY_TARGET_IMMEDIATE RECOVERY_TARGET_IMMEDIATE
} RecoveryTargetType; } RecoveryTargetType;

View File

@ -3,7 +3,7 @@ use strict;
use warnings; use warnings;
use PostgresNode; use PostgresNode;
use TestLib; use TestLib;
use Test::More tests => 7; use Test::More tests => 9;
# Create and test a standby from given backup, with a certain # Create and test a standby from given backup, with a certain
# recovery target. # recovery target.
@ -86,6 +86,16 @@ my $lsn4 =
$node_master->safe_psql('postgres', $node_master->safe_psql('postgres',
"SELECT pg_create_restore_point('$recovery_name');"); "SELECT pg_create_restore_point('$recovery_name');");
# And now for a recovery target LSN
$node_master->safe_psql('postgres',
"INSERT INTO tab_int VALUES (generate_series(4001,5000))");
my $recovery_lsn = $node_master->safe_psql('postgres', "SELECT pg_current_xlog_location()");
my $lsn5 =
$node_master->safe_psql('postgres', "SELECT pg_current_xlog_location();");
$node_master->safe_psql('postgres',
"INSERT INTO tab_int VALUES (generate_series(5001,6000))");
# Force archiving of WAL file # Force archiving of WAL file
$node_master->safe_psql('postgres', "SELECT pg_switch_xlog()"); $node_master->safe_psql('postgres', "SELECT pg_switch_xlog()");
@ -102,6 +112,9 @@ test_recovery_standby('time', 'standby_3', $node_master, \@recovery_params,
@recovery_params = ("recovery_target_name = '$recovery_name'"); @recovery_params = ("recovery_target_name = '$recovery_name'");
test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params, test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
"4000", $lsn4); "4000", $lsn4);
@recovery_params = ("recovery_target_lsn = '$recovery_lsn'");
test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
"5000", $lsn5);
# Multiple targets # Multiple targets
# Last entry has priority (note that an array respects the order of items # Last entry has priority (note that an array respects the order of items
@ -111,16 +124,23 @@ test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
"recovery_target_xid = '$recovery_txid'", "recovery_target_xid = '$recovery_txid'",
"recovery_target_time = '$recovery_time'"); "recovery_target_time = '$recovery_time'");
test_recovery_standby('name + XID + time', test_recovery_standby('name + XID + time',
'standby_5', $node_master, \@recovery_params, "3000", $lsn3); 'standby_6', $node_master, \@recovery_params, "3000", $lsn3);
@recovery_params = ( @recovery_params = (
"recovery_target_time = '$recovery_time'", "recovery_target_time = '$recovery_time'",
"recovery_target_name = '$recovery_name'", "recovery_target_name = '$recovery_name'",
"recovery_target_xid = '$recovery_txid'"); "recovery_target_xid = '$recovery_txid'");
test_recovery_standby('time + name + XID', test_recovery_standby('time + name + XID',
'standby_6', $node_master, \@recovery_params, "2000", $lsn2); 'standby_7', $node_master, \@recovery_params, "2000", $lsn2);
@recovery_params = ( @recovery_params = (
"recovery_target_xid = '$recovery_txid'", "recovery_target_xid = '$recovery_txid'",
"recovery_target_time = '$recovery_time'", "recovery_target_time = '$recovery_time'",
"recovery_target_name = '$recovery_name'"); "recovery_target_name = '$recovery_name'");
test_recovery_standby('XID + time + name', test_recovery_standby('XID + time + name',
'standby_7', $node_master, \@recovery_params, "4000", $lsn4); '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);