You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-17 01:12:23 +02:00
Auto-select backup set on restore when time target is specified.
Auto-selection is performed only when --set is not specified. If a backup set for the given target time cannot not be found, the latest (default) backup set will be used. Currently a limited number of date formats are recognized and timezone names are not allowed, only timezone offsets.
This commit is contained in:
committed by
David Steele
parent
f46d1fa74c
commit
856980ae99
@ -14,6 +14,18 @@
|
||||
<release-list>
|
||||
<release date="XXXX-XX-XX" version="2.24dev" title="UNDER DEVELOPMENT">
|
||||
<release-core-list>
|
||||
<release-feature-list>
|
||||
<release-item>
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="cynthia.shang"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Auto-select backup set on restore when time target is specified.</p>
|
||||
|
||||
<p>Auto-selection is performed only when <br-option>--set</br-option> is not specified. If a backup set for the given target time cannot not be found, the latest (default) backup set will be used.</p>
|
||||
</release-item>
|
||||
</release-feature-list>
|
||||
|
||||
<release-improvement-list>
|
||||
<release-item>
|
||||
<release-item-contributor-list>
|
||||
|
@ -1944,18 +1944,27 @@
|
||||
</execute>
|
||||
</execute-list>
|
||||
|
||||
<p>Now take a new backup and attempt recovery from the new backup.</p>
|
||||
<p>Now take a new backup and attempt recovery from the new backup by specifying the <br-option>{[dash]}-set</br-option> option. The <cmd>info</cmd> command can be used to find the new backup label.</p>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Perform a backup then attempt recovery from that backup</title>
|
||||
<title>Perform a backup and get backup info</title>
|
||||
|
||||
<execute user="postgres">
|
||||
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-type=incr backup</exe-cmd>
|
||||
</execute>
|
||||
|
||||
<execute user="postgres" show="n" variable-key="backup-last">
|
||||
<exe-cmd>{[cmd-backup-last]}</exe-cmd>
|
||||
</execute>
|
||||
|
||||
<execute user="postgres">
|
||||
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-type=incr backup</exe-cmd>
|
||||
<execute user="postgres" filter="n" output="y">
|
||||
<exe-cmd>{[project-exe]} info</exe-cmd>
|
||||
<exe-highlight>{[backup-last]}</exe-highlight>
|
||||
</execute>
|
||||
</execute-list>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Attempt recovery from the specified backup</title>
|
||||
|
||||
<execute user="root">
|
||||
<exe-cmd>{[pg-cluster-stop]}</exe-cmd>
|
||||
@ -1963,7 +1972,8 @@
|
||||
|
||||
<execute user="postgres">
|
||||
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-delta
|
||||
{[dash]}-type=time "{[dash]}-target={[time-recovery-timestamp]}" --target-action=promote restore</exe-cmd>
|
||||
{[dash]}-set={[backup-last]}
|
||||
{[dash]}-type=time "{[dash]}-target={[time-recovery-timestamp]}" {[dash]}-target-action=promote restore</exe-cmd>
|
||||
</execute>
|
||||
|
||||
<execute user="root" show="n">
|
||||
@ -1995,21 +2005,10 @@
|
||||
</execute>
|
||||
</execute-list>
|
||||
|
||||
<p>Using an earlier backup will allow <postgres/> to play forward to the correct time. The <cmd>info</cmd> command can be used to find the next to last backup.</p>
|
||||
<p>The default behavior for time-based restore, if the <br-option>{[dash]}-set</br-option> option is not specified, is to attempt to discover an earlier backup to play forward from. If a backup set cannot be found, then restore will default to the latest backup which, as shown earlier, may not give the desired result.</p>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Get backup info for the {[postgres-cluster-demo]} cluster</title>
|
||||
|
||||
<execute user="postgres" filter="n" output="y">
|
||||
<exe-cmd>{[project-exe]} info</exe-cmd>
|
||||
<exe-highlight>{[backup-last]}</exe-highlight>
|
||||
</execute>
|
||||
</execute-list>
|
||||
|
||||
<p>The default behavior for restore is to use the last backup but an earlier backup can be specified with the <br-option>{[dash]}-set</br-option> option.</p>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Stop <postgres/>, restore from the selected backup, and start <postgres/></title>
|
||||
<title>Stop <postgres/>, restore from auto-selected backup, and start <postgres/></title>
|
||||
|
||||
<execute user="root">
|
||||
<exe-cmd>{[pg-cluster-stop]}</exe-cmd>
|
||||
@ -2019,7 +2018,7 @@
|
||||
<exe-cmd>
|
||||
{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-delta
|
||||
{[dash]}-type=time "{[dash]}-target={[time-recovery-timestamp]}"
|
||||
{[dash]}-set={[backup-last]} --target-action=promote restore
|
||||
{[dash]}-target-action=promote restore
|
||||
</exe-cmd>
|
||||
</execute>
|
||||
|
||||
|
@ -59,6 +59,8 @@ Recovery constants
|
||||
STRING_STATIC(RECOVERY_TYPE_PRESERVE_STR, RECOVERY_TYPE_PRESERVE);
|
||||
#define RECOVERY_TYPE_STANDBY "standby"
|
||||
STRING_STATIC(RECOVERY_TYPE_STANDBY_STR, RECOVERY_TYPE_STANDBY);
|
||||
#define RECOVERY_TYPE_TIME "time"
|
||||
STRING_STATIC(RECOVERY_TYPE_TIME_STR, RECOVERY_TYPE_TIME);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Validate restore path
|
||||
@ -106,6 +108,95 @@ restorePathValidate(void)
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get epoch time from formatted string
|
||||
***********************************************************************************************************************************/
|
||||
static time_t
|
||||
getEpoch(const String *targetTime)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STRING, targetTime);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(targetTime != NULL);
|
||||
|
||||
time_t result = 0;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Build the regex to accept formats: YYYY-MM-DD HH:MM:SS with optional msec (up to 6 digits and separated from minutes by
|
||||
// a comma or period), optional timezone offset +/- HH or HHMM or HH:MM, where offset boundaries are UTC-12 to UTC+14
|
||||
const String *expression = STRDEF(
|
||||
"^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}((\\,|\\.)[0-9]{1,6})?((\\+|\\-)[0-9]{2}(:?)([0-9]{2})?)?$");
|
||||
|
||||
RegExp *regExp = regExpNew(expression);
|
||||
|
||||
// If the target-recovery time matches the regular expression then validate it
|
||||
if (regExpMatch(regExp, targetTime))
|
||||
{
|
||||
// Strip off the date and time and put the remainder into another string
|
||||
String *datetime = strSubN(targetTime, 0, 19);
|
||||
|
||||
int dtYear = cvtZToInt(strPtr(strSubN(datetime, 0, 4)));
|
||||
int dtMonth = cvtZToInt(strPtr(strSubN(datetime, 5, 2)));
|
||||
int dtDay = cvtZToInt(strPtr(strSubN(datetime, 8, 2)));
|
||||
int dtHour = cvtZToInt(strPtr(strSubN(datetime, 11, 2)));
|
||||
int dtMinute = cvtZToInt(strPtr(strSubN(datetime, 14, 2)));
|
||||
int dtSecond = cvtZToInt(strPtr(strSubN(datetime, 17, 2)));
|
||||
|
||||
// Confirm date and time parts are valid
|
||||
datePartsValid(dtYear, dtMonth, dtDay);
|
||||
timePartsValid(dtHour, dtMinute, dtSecond);
|
||||
|
||||
String *timeTargetZone = strSub(targetTime, 19);
|
||||
|
||||
// Find the + or - indicating a timezone offset was provided (there may be milliseconds before the timezone, so need to
|
||||
// skip). If a timezone offset was not provided, then local time is assumed.
|
||||
int idxSign = strChr(timeTargetZone, '+');
|
||||
|
||||
if (idxSign == -1)
|
||||
idxSign = strChr(timeTargetZone, '-');
|
||||
|
||||
if (idxSign != -1)
|
||||
{
|
||||
String *timezoneOffset = strSub(timeTargetZone, (size_t)idxSign);
|
||||
|
||||
// Include the sign with the hour
|
||||
int tzHour = cvtZToInt(strPtr(strSubN(timezoneOffset, 0, 3)));
|
||||
int tzMinute = 0;
|
||||
|
||||
// If minutes are included in timezone offset then extract the minutes based on whether a colon separates them from
|
||||
// the hour
|
||||
if (strSize(timezoneOffset) > 3)
|
||||
tzMinute = cvtZToInt(strPtr(strSubN(timezoneOffset, 3 + (strChr(timezoneOffset, ':') == -1 ? 0 : 1), 2)));
|
||||
|
||||
result = epochFromParts(dtYear, dtMonth, dtDay, dtHour, dtMinute, dtSecond, tzOffsetSeconds(tzHour, tzMinute));
|
||||
}
|
||||
// If there is no timezone offset, then assume it is local time
|
||||
else
|
||||
{
|
||||
// Set tm_isdst to -1 to force mktime to consider if DST. For example, if system time is America/New_York then
|
||||
// 2019-09-14 20:02:49 was a time in DST so the Epoch value should be 1568505769 (and not 1568509369 which would be
|
||||
// 2019-09-14 21:02:49 - an hour too late)
|
||||
result = mktime(
|
||||
&(struct tm){.tm_sec = dtSecond, .tm_min = dtMinute, .tm_hour = dtHour, .tm_mday = dtDay, .tm_mon = dtMonth - 1,
|
||||
.tm_year = dtYear - 1900, .tm_isdst = -1});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARN_FMT(
|
||||
"automatic backup set selection cannot be performed with provided time '%s', latest backup set will be used"
|
||||
"\nHINT: time format must be YYYY-MM-DD HH:MM:SS with optional msec and optional timezone (+/- HH or HHMM or HH:MM)"
|
||||
" - if timezone is ommitted, local time is assumed (for UTC use +00)",
|
||||
strPtr(targetTime));
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(TIME, result);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Get the backup set to restore
|
||||
***********************************************************************************************************************************/
|
||||
@ -130,6 +221,41 @@ restoreBackupSet(InfoBackup *infoBackup)
|
||||
if (infoBackupDataTotal(infoBackup) == 0)
|
||||
THROW(BackupSetInvalidError, "no backup sets to restore");
|
||||
|
||||
time_t timeTargetEpoch = 0;
|
||||
|
||||
// If the recovery type is time, attempt to determine the backup set
|
||||
if (strEq(cfgOptionStr(cfgOptType), RECOVERY_TYPE_TIME_STR))
|
||||
{
|
||||
timeTargetEpoch = getEpoch(cfgOptionStr(cfgOptTarget));
|
||||
|
||||
// Try to find the newest backup set with a stop time before the target recovery time
|
||||
if (timeTargetEpoch != 0)
|
||||
{
|
||||
// Search current backups from newest to oldest
|
||||
for (unsigned int keyIdx = infoBackupDataTotal(infoBackup) - 1; (int)keyIdx >= 0; keyIdx--)
|
||||
{
|
||||
// Get the backup data
|
||||
InfoBackupData backupData = infoBackupData(infoBackup, keyIdx);
|
||||
|
||||
// If the end of the backup is before the target time, then select this backup
|
||||
if (backupData.backupTimestampStop < timeTargetEpoch)
|
||||
{
|
||||
backupSet = backupData.backupLabel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (backupSet == NULL)
|
||||
{
|
||||
LOG_WARN_FMT(
|
||||
"unable to find backup set with stop time less than '%s', latest backup set will be used",
|
||||
strPtr(cfgOptionStr(cfgOptTarget)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If a backup set was not found or the recovery type was not time, then use the latest backup
|
||||
if (backupSet == NULL)
|
||||
backupSet = infoBackupData(infoBackup, infoBackupDataTotal(infoBackup) - 1).backupLabel;
|
||||
}
|
||||
// Otherwise check to make sure the specified backup set is valid
|
||||
|
@ -623,7 +623,7 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: restore
|
||||
total: 11
|
||||
total: 12
|
||||
containerReq: true
|
||||
binReq: true
|
||||
|
||||
|
@ -202,8 +202,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
|
||||
recovery_target_xid = '[XID-TARGET-1]'
|
||||
recovery_target_action = 'promote'
|
||||
|
||||
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf
|
||||
|
@ -251,8 +251,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
|
||||
recovery_target = 'immediate'
|
||||
recovery_target_action = 'promote'
|
||||
|
||||
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf
|
||||
|
@ -253,8 +253,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
|
||||
recovery_target = 'immediate'
|
||||
recovery_target_action = 'promote'
|
||||
|
||||
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf
|
||||
|
@ -241,8 +241,8 @@ restore, type 'default' (db-master host)
|
||||
# Recovery settings generated by pgBackRest restore on [TIMESTAMP]
|
||||
restore_command = '[BACKREST-BIN] --buffer-size=[BUFFER-SIZE] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get %f "%p"'
|
||||
|
||||
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf
|
||||
|
@ -347,8 +347,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
|
||||
recovery_target = 'immediate'
|
||||
recovery_target_action = 'promote'
|
||||
|
||||
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf
|
||||
|
@ -243,8 +243,8 @@ restore, type 'default' (db-master host)
|
||||
# Recovery settings generated by pgBackRest restore on [TIMESTAMP]
|
||||
restore_command = '[BACKREST-BIN] --buffer-size=[BUFFER-SIZE] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get %f "%p"'
|
||||
|
||||
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
|
||||
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf
|
||||
|
@ -464,6 +464,10 @@ sub regExpReplaceAll
|
||||
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', " \\-\\-target\\=\\\".*UTC", "[^\\\"]+UTC\$");
|
||||
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', "^recovery_target_time \\= \\'.*UTC", "[^\\']+UTC\$");
|
||||
|
||||
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', "\\, target \\'.*\\+00", "[^\\']+\\+00\$");
|
||||
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', " \\-\\-target\\=\\\".*\\+00", "[^\\\"]+\\+00\$");
|
||||
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', "^recovery_target_time \\= \\'.*\\+00", "[^\\']+\\+00\$");
|
||||
|
||||
# Full test xid-based recovery (this expressions only work when time-based expressions above have already been applied
|
||||
$strLine = $self->regExpReplace($strLine, 'XID-TARGET', "\\, target \\'[0-9]+", "[0-9]+\$");
|
||||
$strLine = $self->regExpReplace($strLine, 'XID-TARGET', " \\-\\-target\\=\\\"[0-9]+", "[0-9]+\$");
|
||||
|
@ -1537,6 +1537,7 @@ sub restore
|
||||
$strOptionalParam,
|
||||
$bTablespace,
|
||||
$strUser,
|
||||
$strBackupExpected,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
@ -1557,6 +1558,7 @@ sub restore
|
||||
{name => 'strOptionalParam', optional => true},
|
||||
{name => 'bTablespace', optional => true},
|
||||
{name => 'strUser', optional => true},
|
||||
{name => 'strBackupExpected', optional => true},
|
||||
);
|
||||
|
||||
# Build link map options
|
||||
@ -1593,10 +1595,15 @@ sub restore
|
||||
|
||||
if (!defined($rhExpectedManifest))
|
||||
{
|
||||
if (!defined($strBackupExpected))
|
||||
{
|
||||
$strBackupExpected = $strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup;
|
||||
}
|
||||
|
||||
# Load the manifest
|
||||
my $oExpectedManifest = new pgBackRest::Manifest(
|
||||
storageRepo()->pathGet(
|
||||
STORAGE_REPO_BACKUP . qw{/} . ($strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup) . qw{/} .
|
||||
STORAGE_REPO_BACKUP . qw{/} . $strBackupExpected. qw{/} .
|
||||
FILE_MANIFEST),
|
||||
{strCipherPass => $oHostBackup->cipherPassManifest()});
|
||||
|
||||
|
@ -543,11 +543,11 @@ sub run
|
||||
$oHostDbMaster->start();
|
||||
}
|
||||
|
||||
# Setup the time target
|
||||
# Setup the time targets
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
$oHostDbMaster->sqlExecute("update test set message = '$strTimeMessage'");
|
||||
$oHostDbMaster->sqlWalRotate();
|
||||
my $strTimeTarget = $oHostDbMaster->sqlSelectOne("select to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS.US TZ')");
|
||||
my $strTimeTarget = $oHostDbMaster->sqlSelectOne("select current_timestamp");
|
||||
&log(INFO, " time target is ${strTimeTarget}");
|
||||
|
||||
# Incr backup - fail on archive_mode=always when version >= 9.5
|
||||
@ -904,18 +904,19 @@ sub run
|
||||
$oHostDbMaster->sqlExecute("update test set message = '$strTimelineMessage'");
|
||||
}
|
||||
|
||||
# Restore (restore type = time, inclusive) - there is no exclusive time test because I can't find a way to find the
|
||||
# exact commit time of a transaction.
|
||||
# Restore (restore type = time, inclusive, automatically select backup) - there is no exclusive time test because I can't
|
||||
# find a way to find the exact commit time of a transaction.
|
||||
#---------------------------------------------------------------------------------------------------------------------------
|
||||
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_TIME);
|
||||
|
||||
$oHostDbMaster->clusterStop();
|
||||
|
||||
$oHostDbMaster->restore(
|
||||
undef, $strFullBackup,
|
||||
undef, cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
|
||||
{bDelta => true, strType => CFGOPTVAL_RESTORE_TYPE_TIME, strTarget => $strTimeTarget,
|
||||
strTargetAction => $oHostDbMaster->pgVersion() >= PG_VERSION_91 ? 'promote' : undef,
|
||||
strTargetTimeline => $oHostDbMaster->pgVersion() >= PG_VERSION_12 ? 'current' : undef});
|
||||
strTargetTimeline => $oHostDbMaster->pgVersion() >= PG_VERSION_12 ? 'current' : undef,
|
||||
strBackupExpected => $strFullBackup});
|
||||
|
||||
$oHostDbMaster->clusterStart();
|
||||
$oHostDbMaster->sqlSelectOneTest('select message from test', $strTimeMessage);
|
||||
|
@ -55,7 +55,7 @@ Test data for backup.info
|
||||
"\"backup-info-repo-size\":3159811,\"backup-info-repo-size-delta\":15765,\"backup-info-size\":26897030," \
|
||||
"\"backup-info-size-delta\":163866,\"backup-prior\":\"20161219-212741F\",\"backup-reference\":[\"20161219-212741F\"," \
|
||||
"\"20161219-212741F_20161219-212803D\"]," \
|
||||
"\"backup-timestamp-start\":1482182877,\"backup-timestamp-stop\":1482182883,\"backup-type\":\"incr\",\"db-id\":1," \
|
||||
"\"backup-timestamp-start\":1482182884,\"backup-timestamp-stop\":1482182985,\"backup-type\":\"incr\",\"db-id\":1," \
|
||||
"\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
|
||||
"\"option-checksum-page\":false,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
|
||||
|
||||
@ -446,6 +446,44 @@ testRun(void)
|
||||
storageRemoveP(storagePgWrite(), strNew(PG_FILE_PGVERSION), .errorOnMissing = true);
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("getEpoch()"))
|
||||
{
|
||||
TEST_TITLE("system time UTC");
|
||||
|
||||
setenv("TZ", "UTC", true);
|
||||
TEST_RESULT_UINT(getEpoch(strNew("2020-01-08 09:18:15-0700")), 1578500295, "epoch with timezone");
|
||||
TEST_RESULT_UINT(getEpoch(strNew("2020-01-08 16:18:15.0000")), 1578500295, "same epoch no timezone");
|
||||
TEST_RESULT_UINT(getEpoch(strNew("2020-01-08 16:18:15.0000+00")), 1578500295, "same epoch timezone 0");
|
||||
TEST_ERROR_FMT(getEpoch(strNew("2020-13-08 16:18:15")), FormatError, "invalid date 2020-13-08");
|
||||
TEST_ERROR_FMT(getEpoch(strNew("2020-01-08 16:68:15")), FormatError, "invalid time 16:68:15");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("system time America/New_York");
|
||||
|
||||
setenv("TZ", "America/New_York", true);
|
||||
time_t testTime = 1573754569;
|
||||
char timeBuffer[20];
|
||||
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %H:%M:%S", localtime(&testTime));
|
||||
TEST_RESULT_Z(timeBuffer, "2019-11-14 13:02:49", "check timezone set");
|
||||
TEST_RESULT_UINT(getEpoch(strNew("2019-11-14 13:02:49-0500")), 1573754569, "offset same as local");
|
||||
TEST_RESULT_UINT(getEpoch(strNew("2019-11-14 13:02:49")), 1573754569, "GMT-0500 (EST)");
|
||||
TEST_RESULT_UINT(getEpoch(strNew("2019-09-14 20:02:49")), 1568505769, "GMT-0400 (EDT)");
|
||||
TEST_RESULT_UINT(getEpoch(strNew("2018-04-27 04:29:00+04:30")), 1524787140, "GMT+0430");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("invalid target time format");
|
||||
|
||||
TEST_RESULT_UINT(getEpoch(strNew("Tue, 15 Nov 1994 12:45:26")), 0, "invalid date time format");
|
||||
TEST_RESULT_LOG(
|
||||
"P00 WARN: automatic backup set selection cannot be performed with provided time 'Tue, 15 Nov 1994 12:45:26',"
|
||||
" latest backup set will be used\n"
|
||||
" HINT: time format must be YYYY-MM-DD HH:MM:SS with optional msec and optional timezone"
|
||||
" (+/- HH or HHMM or HH:MM) - if timezone is ommitted, local time is assumed (for UTC use +00)");
|
||||
|
||||
setenv("TZ", "UTC", true);
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
if (testBegin("restoreBackupSet()"))
|
||||
{
|
||||
@ -478,6 +516,54 @@ testRun(void)
|
||||
harnessCfgLoad(cfgCmdRestore, argList);
|
||||
|
||||
TEST_ERROR(restoreBackupSet(infoBackup), BackupSetInvalidError, "backup set BOGUS is not valid");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("target time");
|
||||
setenv("TZ", "UTC", true);
|
||||
|
||||
infoBackup = infoBackupNewLoad(
|
||||
ioBufferReadNew(harnessInfoChecksumZ(TEST_RESTORE_BACKUP_INFO "\n" TEST_RESTORE_BACKUP_INFO_DB)));
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAdd(argList, strNewFmt("--repo1-path=%s", strPtr(repoPath)));
|
||||
strLstAdd(argList, strNewFmt("--pg1-path=%s", strPtr(pgPath)));
|
||||
strLstAddZ(argList, "--type=time");
|
||||
strLstAddZ(argList, "--target=2016-12-19 16:28:04-0500");
|
||||
|
||||
harnessCfgLoad(cfgCmdRestore, argList);
|
||||
|
||||
TEST_RESULT_STR_Z(restoreBackupSet(infoBackup), "20161219-212741F_20161219-212803D", "backup set found");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAdd(argList, strNewFmt("--repo1-path=%s", strPtr(repoPath)));
|
||||
strLstAdd(argList, strNewFmt("--pg1-path=%s", strPtr(pgPath)));
|
||||
strLstAddZ(argList, "--type=time");
|
||||
strLstAddZ(argList, "--target=2016-12-19 16:27:30-0500");
|
||||
|
||||
harnessCfgLoad(cfgCmdRestore, argList);
|
||||
|
||||
TEST_RESULT_STR_Z(restoreBackupSet(infoBackup), "20161219-212741F_20161219-212918I", "default to latest backup set");
|
||||
TEST_RESULT_LOG(
|
||||
"P00 WARN: unable to find backup set with stop time less than '2016-12-19 16:27:30-0500', latest backup set will be"
|
||||
" used");
|
||||
|
||||
argList = strLstNew();
|
||||
strLstAddZ(argList, "--stanza=test1");
|
||||
strLstAdd(argList, strNewFmt("--repo1-path=%s", strPtr(repoPath)));
|
||||
strLstAdd(argList, strNewFmt("--pg1-path=%s", strPtr(pgPath)));
|
||||
strLstAddZ(argList, "--type=time");
|
||||
strLstAddZ(argList, "--target=Tue, 15 Nov 1994 12:45:26");
|
||||
|
||||
harnessCfgLoad(cfgCmdRestore, argList);
|
||||
|
||||
TEST_RESULT_STR_Z(restoreBackupSet(infoBackup), "20161219-212741F_20161219-212918I", "time invalid format, default latest");
|
||||
TEST_RESULT_LOG(
|
||||
"P00 WARN: automatic backup set selection cannot be performed with provided time 'Tue, 15 Nov 1994 12:45:26',"
|
||||
" latest backup set will be used\n"
|
||||
" HINT: time format must be YYYY-MM-DD HH:MM:SS with optional msec and optional timezone"
|
||||
" (+/- HH or HHMM or HH:MM) - if timezone is ommitted, local time is assumed (for UTC use +00)");
|
||||
}
|
||||
|
||||
// *****************************************************************************************************************************
|
||||
|
Reference in New Issue
Block a user