You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-09-16 09:06:18 +02:00
Automatically enable backup checksum delta when anomalies (e.g. timeline switch) are detected.
There are a number of cases where a checksum delta is more appropriate than the default time-based delta: * Timeline has switched since the prior backup * File timestamp is older than recorded in the prior backup * File size changed but timestamp did not * File timestamp is in the future compared to the start of the backup * Online option has changed since the prior backup A practical example is that checksum delta will be enabled after a failover to standby due to the timeline switch. In this case, timestamps can't be trusted and our recommendation has been to run a full backup, which can impact the retention schedule and requires manual intervention. Now, a checksum delta will be performed if the backup type is incr/diff. This means more CPU will be used during the backup but the backup size will be smaller and the retention schedule will not be impacted. Contributed by Cynthia Shang.
This commit is contained in:
committed by
David Steele
parent
cca7a4ffd4
commit
34c63276cd
@@ -72,7 +72,11 @@ sub resumeClean
|
||||
$oStorageRepo,
|
||||
$strBackupLabel,
|
||||
$oManifest,
|
||||
$oAbortedManifest
|
||||
$oAbortedManifest,
|
||||
$bOnline,
|
||||
$bDelta,
|
||||
$strTimelineCurrent,
|
||||
$strTimelineLast,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
@@ -80,7 +84,11 @@ sub resumeClean
|
||||
{name => 'oStorageRepo'},
|
||||
{name => 'strBackupLabel'},
|
||||
{name => 'oManifest'},
|
||||
{name => 'oAbortedManifest'}
|
||||
{name => 'oAbortedManifest'},
|
||||
{name => 'bOnline'},
|
||||
{name => 'bDelta'},
|
||||
{name => 'strTimelineCurrent', required => false},
|
||||
{name => 'strTimelineLast', required => false},
|
||||
);
|
||||
|
||||
&log(DETAIL, 'clean resumed backup path: ' . $oStorageRepo->pathGet(STORAGE_REPO_BACKUP . "/${strBackupLabel}"));
|
||||
@@ -91,6 +99,56 @@ sub resumeClean
|
||||
# Get compress flag
|
||||
my $bCompressed = $oAbortedManifest->boolGet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_COMPRESS);
|
||||
|
||||
if (!$bDelta)
|
||||
{
|
||||
# Check to see if delta checksum should be enabled
|
||||
$bDelta = $oAbortedManifest->checkDelta(
|
||||
'resumed', $oAbortedManifest->boolTest(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_ONLINE, undef, $bOnline),
|
||||
$strTimelineCurrent, $strTimelineLast);
|
||||
|
||||
# If delta is still false, check the files for anomalies
|
||||
if (!$bDelta)
|
||||
{
|
||||
my @stryFileList = ();
|
||||
|
||||
foreach my $strName (sort(keys(%{$hFile})))
|
||||
{
|
||||
# Ignore files that will never be in the manifest but should be preserved
|
||||
if ($strName eq FILE_MANIFEST_COPY ||
|
||||
$strName eq '.')
|
||||
{
|
||||
next;
|
||||
}
|
||||
|
||||
if ($hFile->{$strName}{type} eq 'f')
|
||||
{
|
||||
# If the original backup was compressed then remove the extension before checking the manifest
|
||||
my $strFile = $strName;
|
||||
|
||||
if ($bCompressed)
|
||||
{
|
||||
$strFile = substr($strFile, 0, length($strFile) - 3);
|
||||
}
|
||||
|
||||
# To be preserved the file must exist in the new manifest and not be a reference to a previous backup and must
|
||||
# have a checksum
|
||||
if ($oManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile) &&
|
||||
!$oManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_REFERENCE) &&
|
||||
$oAbortedManifest->test(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_CHECKSUM))
|
||||
{
|
||||
push(@stryFileList, $strFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# If there are files in the list then check if delta should be enabled
|
||||
if (@stryFileList)
|
||||
{
|
||||
$bDelta = $oManifest->checkDeltaFile(\@stryFileList, $oAbortedManifest, undef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Find paths and files to delete
|
||||
my @stryFile;
|
||||
|
||||
@@ -139,7 +197,7 @@ sub resumeClean
|
||||
if (defined($strChecksum) &&
|
||||
$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_SIZE) ==
|
||||
$oAbortedManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_SIZE) &&
|
||||
(cfgOption(CFGOPT_DELTA) ||
|
||||
($bDelta ||
|
||||
$oManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_TIMESTAMP) ==
|
||||
$oAbortedManifest->get(MANIFEST_SECTION_TARGET_FILE, $strFile, MANIFEST_SUBKEY_TIMESTAMP)))
|
||||
{
|
||||
@@ -189,7 +247,11 @@ sub resumeClean
|
||||
}
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn($strOperation);
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'bDelta', value => $bDelta, trace => true},
|
||||
);
|
||||
}
|
||||
|
||||
####################################################################################################################################
|
||||
@@ -504,6 +566,7 @@ sub process
|
||||
# Find the previous backup based on the type
|
||||
my $oLastManifest;
|
||||
my $strBackupLastPath;
|
||||
my $strTimelineLast;
|
||||
|
||||
if ($strType ne CFGOPTVAL_BACKUP_TYPE_FULL)
|
||||
{
|
||||
@@ -520,6 +583,12 @@ sub process
|
||||
# If the repo is encrypted then use the passphrase in this manifest for the backup set
|
||||
$strCipherPassBackupSet = $oLastManifest->cipherPassSub();
|
||||
|
||||
# Get archive segment timeline for determining if a timeline switch has occurred. Only defined for prior online backup.
|
||||
if ($oLastManifest->test(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP))
|
||||
{
|
||||
$strTimelineLast = substr($oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP), 0, 8);
|
||||
}
|
||||
|
||||
&log(INFO, 'last backup label = ' . $oLastManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LABEL) .
|
||||
', version = ' . $oLastManifest->get(INI_SECTION_BACKREST, INI_KEY_VERSION));
|
||||
|
||||
@@ -554,6 +623,7 @@ sub process
|
||||
my $strBackupLabel;
|
||||
my $oAbortedManifest;
|
||||
my $strBackupPath;
|
||||
my $strTimelineAborted;
|
||||
|
||||
foreach my $strAbortedBackup ($oStorageRepo->list(
|
||||
STORAGE_REPO_BACKUP, {strExpression => backupRegExpGet(true, true, true), strSortOrder => 'reverse'}))
|
||||
@@ -659,6 +729,17 @@ sub process
|
||||
{
|
||||
$strCipherPassBackupSet = $oAbortedManifest->cipherPassSub();
|
||||
}
|
||||
|
||||
# Get the archive segment timeline for determining if a timeline switch has occurred. Only defined for prior online
|
||||
# backup.
|
||||
if ($oAbortedManifest->test(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP))
|
||||
{
|
||||
$strTimelineAborted = substr($oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_STOP), 0, 8);
|
||||
}
|
||||
elsif ($oAbortedManifest->test(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START))
|
||||
{
|
||||
$strTimelineAborted = substr($oAbortedManifest->get(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_ARCHIVE_START), 0, 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -725,7 +806,8 @@ sub process
|
||||
my $strLsnStart = undef;
|
||||
my $iWalSegmentSize = undef;
|
||||
my $hTablespaceMap = undef;
|
||||
my $hDatabaseMap = undef;
|
||||
my $hDatabaseMap = undef;
|
||||
my $strTimelineCurrent = undef;
|
||||
|
||||
# If this is an offline backup
|
||||
if (!cfgOption(CFGOPT_ONLINE))
|
||||
@@ -767,6 +849,9 @@ sub process
|
||||
$oBackupManifest->set(MANIFEST_SECTION_BACKUP, MANIFEST_KEY_LSN_START, undef, $strLsnStart);
|
||||
&log(INFO, "backup start archive = ${strArchiveStart}, lsn = ${strLsnStart}");
|
||||
|
||||
# Get the timeline from the archive
|
||||
$strTimelineCurrent = substr($strArchiveStart, 0, 8);
|
||||
|
||||
# Get tablespace map
|
||||
$hTablespaceMap = $oDbMaster->tablespaceMapGet();
|
||||
|
||||
@@ -825,12 +910,10 @@ sub process
|
||||
# Record checksum-page option in the manifest
|
||||
$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_CHECKSUM_PAGE, undef, cfgOption(CFGOPT_CHECKSUM_PAGE));
|
||||
|
||||
# Build the manifest
|
||||
$oBackupManifest->build($oStorageDbMaster, $strDbMasterPath, $oLastManifest, cfgOption(CFGOPT_ONLINE),
|
||||
cfgOption(CFGOPT_DELTA), $hTablespaceMap, $hDatabaseMap, cfgOption(CFGOPT_EXCLUDE, false));
|
||||
|
||||
# Set the delta option.
|
||||
$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_DELTA, undef, cfgOption(CFGOPT_DELTA));
|
||||
# Build the manifest. The delta option may have changed from false to true during the manifest build so set it to the result.
|
||||
cfgOptionSet(CFGOPT_DELTA, $oBackupManifest->build(
|
||||
$oStorageDbMaster, $strDbMasterPath, $oLastManifest, cfgOption(CFGOPT_ONLINE), cfgOption(CFGOPT_DELTA), $hTablespaceMap,
|
||||
$hDatabaseMap, cfgOption(CFGOPT_EXCLUDE, false), $strTimelineCurrent, $strTimelineLast));
|
||||
|
||||
&log(TEST, TEST_MANIFEST_BUILD);
|
||||
|
||||
@@ -840,8 +923,10 @@ sub process
|
||||
&log(WARN, "aborted backup ${strBackupLabel} of same type exists, will be cleaned to remove invalid files and resumed");
|
||||
&log(TEST, TEST_BACKUP_RESUME);
|
||||
|
||||
# Clean the backup path before resuming
|
||||
$self->resumeClean($oStorageRepo, $strBackupLabel, $oBackupManifest, $oAbortedManifest);
|
||||
# Clean the backup path before resuming. The delta option may have changed from false to true during the reseume clean
|
||||
# so set it to the result.
|
||||
cfgOptionSet(CFGOPT_DELTA, $self->resumeClean($oStorageRepo, $strBackupLabel, $oBackupManifest, $oAbortedManifest,
|
||||
cfgOption(CFGOPT_ONLINE), cfgOption(CFGOPT_DELTA), $strTimelineCurrent, $strTimelineAborted));
|
||||
}
|
||||
# Else create the backup path
|
||||
else
|
||||
@@ -850,6 +935,9 @@ sub process
|
||||
$oStorageRepo->pathCreate(STORAGE_REPO_BACKUP . "/${strBackupLabel}");
|
||||
}
|
||||
|
||||
# Set the delta option in the manifest
|
||||
$oBackupManifest->boolSet(MANIFEST_SECTION_BACKUP_OPTION, MANIFEST_KEY_DELTA, undef, cfgOption(CFGOPT_DELTA));
|
||||
|
||||
# Save the backup manifest
|
||||
$oBackupManifest->saveCopy();
|
||||
|
||||
|
Reference in New Issue
Block a user