1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +02:00

Add --set option to the expire command.

The specified backup set (i.e. the backup label provided and all of its dependent backups, if any) will be expired regardless of backup retention rules except that at least one full backup must remain in the repository.
This commit is contained in:
Cynthia Shang 2020-04-27 14:00:36 -04:00 committed by David Steele
parent ad33f545d1
commit 1c1a710460
11 changed files with 847 additions and 9 deletions

View File

@ -702,6 +702,10 @@ my %hConfigDefine =
&CFGDEF_TYPE => CFGDEF_TYPE_STRING,
&CFGDEF_COMMAND =>
{
&CFGCMD_EXPIRE =>
{
&CFGDEF_REQUIRED => false,
},
&CFGCMD_INFO =>
{
&CFGDEF_REQUIRED => false,

View File

@ -1130,6 +1130,16 @@
<text><backrest/> does backup rotation but is not concerned with when the backups were created. If two full backups are configured for retention, <backrest/> will keep two full backups no matter whether they occur two hours or two weeks apart.</text>
<option-list>
<!-- OPERATION - EXPIRE COMMAND - SET OPTION -->
<option id="set" name="Set">
<summary>Backup set to expire.</summary>
<text>The specified backup set (i.e. the backup label provided and all of its dependent backups, if any) will be expired regardless of backup retention rules except that at least one full backup must remain in the repository. <admonition type="warning">Use this option with extreme caution &amp;mdash; it will permanently remove all backups and archives not required to make a backup consistent from the <backrest/> repository for the specified backup set. This process may negate the ability to perform PITR. If <br-option>--repo-retention-full</br-option> and/or <br-option>--repo-retention-archive</br-option> options are configured, then it is recommended that you override these options by setting their values to the maximum while performing adhoc expiration.</admonition></text>
<example>20150131-153358F_20150131-153401I</example>
</option>
</option-list>
<command-example-list>
<command-example>
<text><code-block title="">

View File

@ -25,6 +25,16 @@
</release-bug-list>
<release-feature-list>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="cynthia.shang"/>
</release-item-contributor-list>
<p>Add <br-option>--set</br-option> option to the <cmd>expire</cmd> command.</p>
<p>Allow the user to remove a specified backup regardless of retention settings.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="stefan.fercot"/>

View File

@ -17,6 +17,7 @@ Expire Command
#include "storage/helper.h"
#include <stdlib.h>
#include <stdio.h>
/***********************************************************************************************************************************
Helper functions and structures
@ -97,6 +98,93 @@ expireBackup(InfoBackup *infoBackup, const String *backupLabel)
FUNCTION_LOG_RETURN(STRING_LIST, result);
}
/***********************************************************************************************************************************
Function to expire a selected backup (and all its dependents) regardless of retention rules.
***********************************************************************************************************************************/
static unsigned int
expireAdhocBackup(InfoBackup *infoBackup, const String *backupLabel)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(INFO_BACKUP, infoBackup);
FUNCTION_LOG_PARAM(STRING, backupLabel);
FUNCTION_LOG_END();
ASSERT(infoBackup != NULL);
ASSERT(backupLabel != NULL);
unsigned int result = 0;
MEM_CONTEXT_TEMP_BEGIN()
{
// If the label format is invalid, then error
if (!regExpMatchOne(backupRegExpP(.full = true, .differential = true, .incremental = true), backupLabel))
{
THROW_FMT(OptionInvalidValueError, "'%s' is not a valid backup label format", strPtr(backupLabel));
}
// If the label is not a current backup then notify user and exit
if (infoBackupDataByLabel(infoBackup, backupLabel) == NULL)
{
LOG_WARN_FMT(
"backup %s does not exist\nHINT: run the info command and confirm the backup is listed", strPtr(backupLabel));
}
else
{
// Get a list of all full backups with most recent in position 0
StringList *fullList = strLstSort(infoBackupDataLabelList(infoBackup, backupRegExpP(.full = true)), sortOrderDesc);
// If the requested backup to expire is the latest full backup
if (strCmp(strLstGet(fullList, 0), backupLabel) == 0)
{
// If the latest full backup requested is the only backup or the prior full backup is not for the same db-id
// then the backup requested cannot be expired
if (strLstSize(fullList) == 1 || infoBackupDataByLabel(infoBackup, backupLabel)->backupPgId !=
infoBackupDataByLabel(infoBackup, strLstGet(fullList, 1))->backupPgId)
{
THROW_FMT(
BackupSetInvalidError, "full backup %s cannot be expired until another full backup has been created",
strPtr(backupLabel));
}
}
// Save off what is currently the latest backup (it may be removed if it is the adhoc backup or is a dependent of the
// adhoc backup
const String *latestBackup = infoBackupData(infoBackup, infoBackupDataTotal(infoBackup) - 1).backupLabel;
// Expire the requested backup and any dependents
StringList *backupExpired = expireBackup(infoBackup, backupLabel);
// If the latest backup was removed, then update the latest link if not a dry-run
if (infoBackupDataByLabel(infoBackup, latestBackup) == NULL)
{
// If retention settings have been configured, then there may be holes in the archives. For example, if the archive
// for db-id=1 has 01,02,03,04,05 and F1 backup has archive start-stop 02-03 and rentention-full=1
// (hence retention-archive=1 and retention-archive-type=full), then when F2 backup is created and assuming its
// archive start-stop=05-06 then archives 01 and 04 will be removed resulting in F1 not being able to play through
// PITR, which is expected. Now adhoc expire is attempted on F2 - it will be allowed but now there will be no
// backups that can be recovered through PITR until the next full backup is created. Same problem for differential
// backups with retention-diff.
LOG_WARN_FMT(
"expiring latest backup %s - the ability to perform point-in-time-recovery (PITR) may be affected\n"
"HINT: non-default settings for '%s'/'%s' (even in prior expires) can cause gaps in the WAL.",
strPtr(latestBackup), cfgOptionName(cfgOptRepoRetentionArchive), cfgOptionName(cfgOptRepoRetentionArchiveType));
// Adhoc expire is never performed through backup command so only check to determine if dry-run has been set or not
if (!cfgOptionBool(cfgOptDryRun))
backupLinkLatest(infoBackupData(infoBackup, infoBackupDataTotal(infoBackup) - 1).backupLabel);
}
result = strLstSize(backupExpired);
// Log the expired backup list (prepend "set:" if there were any dependents that were also expired)
LOG_INFO_FMT("expire adhoc backup %s%s", (result > 1 ? "set: " : ""), strPtr(strLstJoin(backupExpired, ", ")));
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(UINT, result);
}
/***********************************************************************************************************************************
Expire differential backups
***********************************************************************************************************************************/
@ -600,24 +688,59 @@ removeExpiredArchive(InfoBackup *infoBackup)
Remove expired backups from repo
***********************************************************************************************************************************/
static void
removeExpiredBackup(InfoBackup *infoBackup)
removeExpiredBackup(InfoBackup *infoBackup, const String *adhocBackupLabel)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(INFO_BACKUP, infoBackup);
FUNCTION_LOG_PARAM(STRING, adhocBackupLabel);
FUNCTION_LOG_END();
ASSERT(infoBackup != NULL);
// Get all the current backups in backup.info
// Get all the current backups in backup.info - these will not be expired
StringList *currentBackupList = strLstSort(infoBackupDataLabelList(infoBackup, NULL), sortOrderDesc);
// Get all the backups on disk
StringList *backupList = strLstSort(
storageListP(
storageRepo(), STORAGE_REPO_BACKUP_STR,
.expression = backupRegExpP(.full = true, .differential = true, .incremental = true)),
sortOrderDesc);
// Initialize the index to the lastest backup on disk
unsigned int backupIdx = 0;
// Only remove the resumable backup if there is a possibility it is a dependent of the adhoc label being expired
if (adhocBackupLabel != NULL)
{
String *manifestFileName = strNewFmt(
STORAGE_REPO_BACKUP "/%s/" BACKUP_MANIFEST_FILE, strPtr(strLstGet(backupList, backupIdx)));
String *manifestCopyFileName = strNewFmt("%s" INFO_COPY_EXT, strPtr(manifestFileName));
// If the latest backup is resumable (has a backup.manifest.copy but no backup.manifest)
if (!storageExistsP(storageRepo(), manifestFileName) && storageExistsP(storageRepo(), manifestCopyFileName))
{
// If the resumable backup is not related to the expired adhoc backup then don't remove it
if (!strBeginsWith(strLstGet(backupList, backupIdx), strSubN(adhocBackupLabel, 0, 16)))
{
backupIdx = 1;
}
// Else it may be related to the adhoc backup so check if its ancestor still exists
else
{
Manifest *manifestResume = manifestLoadFile(
storageRepo(), manifestFileName, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
infoPgCipherPass(infoBackupPg(infoBackup)));
// If the ancestor of the resumable backup still exists in backup.info then do not remove the resumable backup
if (infoBackupDataByLabel(infoBackup, manifestData(manifestResume)->backupLabelPrior) != NULL)
backupIdx = 1;
}
}
}
// Remove non-current backups from disk
for (unsigned int backupIdx = 0; backupIdx < strLstSize(backupList); backupIdx++)
for (; backupIdx < strLstSize(backupList); backupIdx++)
{
if (!strLstExists(currentBackupList, strLstGet(backupList, backupIdx)))
{
@ -655,8 +778,19 @@ cmdExpire(void)
storageRepo(), INFO_BACKUP_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
expireFullBackup(infoBackup);
expireDiffBackup(infoBackup);
const String *adhocBackupLabel = NULL;
// If the --set option is valid (i.e. expire is called on its own) and is set then attempt to expire the requested backup
if (cfgOptionValid(cfgOptSet) && cfgOptionTest(cfgOptSet))
{
adhocBackupLabel = cfgOptionStr(cfgOptSet);
expireAdhocBackup(infoBackup, adhocBackupLabel);
}
else
{
expireFullBackup(infoBackup);
expireDiffBackup(infoBackup);
}
// Store the new backup info only if the dry-run mode is disabled
if (!cfgOptionValid(cfgOptDryRun) || !cfgOptionBool(cfgOptDryRun))
@ -666,7 +800,8 @@ cmdExpire(void)
cfgOptionStr(cfgOptRepoCipherPass));
}
removeExpiredBackup(infoBackup);
// Remove all files on disk that are now expired
removeExpiredBackup(infoBackup, adhocBackupLabel);
removeExpiredArchive(infoBackup);
}
MEM_CONTEXT_TEMP_END();

View File

@ -4366,12 +4366,33 @@ static ConfigDefineOptionData configDefineOptionData[] = CFGDEFDATA_OPTION_LIST
CFGDEFDATA_OPTION_COMMAND_LIST
(
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdExpire)
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdInfo)
CFGDEFDATA_OPTION_COMMAND(cfgDefCmdRestore)
)
CFGDEFDATA_OPTION_OPTIONAL_LIST
(
CFGDEFDATA_OPTION_OPTIONAL_COMMAND_OVERRIDE
(
CFGDEFDATA_OPTION_OPTIONAL_COMMAND(cfgDefCmdExpire)
CFGDEFDATA_OPTION_OPTIONAL_REQUIRED(false)
CFGDEFDATA_OPTION_OPTIONAL_HELP_SUMMARY("Backup set to expire.")
CFGDEFDATA_OPTION_OPTIONAL_HELP_DESCRIPTION
(
"The specified backup set (i.e. the backup label provided and all of its dependent backups, if any) will be "
"expired regardless of backup retention rules except that at least one full backup must remain in the "
"repository. \n"
"WARNING: Use this option with extreme caution -- it will permanently remove all backups and archives not "
"required to make a backup consistent from the pgBackRest repository for the specified backup set. This "
"process may negate the ability to perform PITR. If --repo-retention-full and/or --repo-retention-archive "
"options are configured, then it is recommended that you override these options by setting their values to "
"the maximum while performing adhoc expiration."
)
)
CFGDEFDATA_OPTION_OPTIONAL_COMMAND_OVERRIDE
(
CFGDEFDATA_OPTION_OPTIONAL_COMMAND(cfgDefCmdInfo)

View File

@ -607,7 +607,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: expire
total: 7
total: 8
coverage:
command/expire/expire: full

View File

@ -100,6 +100,38 @@ spool-path=[TEST_PATH]/db-master/spool
archive-copy=y
start-fast=y
diff backup - backup for adhoc expire (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --type=diff --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/pgbackrest.conf
----------------------------------------------------------
[db]
pg1-path=[TEST_PATH]/db-master/db/base
pg1-port=6543
pg1-socket-path=[TEST_PATH]/db-master/db
[global]
archive-async=y
buffer-size=[BUFFER-SIZE]
compress-level=3
compress-type=none
db-timeout=45
lock-path=[TEST_PATH]/db-master/lock
log-level-console=detail
log-level-file=[LOG-LEVEL-FILE]
log-level-stderr=off
log-path=[TEST_PATH]/db-master/log
log-subprocess=[LOG-SUBPROCESS]
log-timestamp=n
protocol-timeout=60
repo1-path=[TEST_PATH]/db-master/repo
spool-path=[TEST_PATH]/db-master/spool
[global:backup]
archive-copy=y
start-fast=y
stop all stanzas (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf stop
------------------------------------------------------------------------------------------------------------------------------------
@ -116,6 +148,10 @@ incr backup - fail on archive_mode=always (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------
expire --set=[BACKUP-DIFF-1] (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --set=[BACKUP-DIFF-1] --stanza=db expire
------------------------------------------------------------------------------------------------------------------------------------
incr backup - update during backup (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --stop-auto --buffer-size=[BUFFER-SIZE] --delta --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -156,6 +156,70 @@ repo1-path=[TEST_PATH]/backup/repo
archive-copy=y
start-fast=y
diff backup - backup for adhoc expire (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --type=diff --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/pgbackrest.conf
----------------------------------------------------------
[db]
pg1-path=[TEST_PATH]/db-master/db/base
pg1-port=6543
pg1-socket-path=[TEST_PATH]/db-master/db
[global]
buffer-size=[BUFFER-SIZE]
compress-level=3
compress-level-network=1
compress-type=lz4
db-timeout=45
lock-path=[TEST_PATH]/db-master/lock
log-level-console=detail
log-level-file=[LOG-LEVEL-FILE]
log-level-stderr=off
log-path=[TEST_PATH]/db-master/log
log-subprocess=[LOG-SUBPROCESS]
log-timestamp=n
protocol-timeout=60
repo1-host=backup
repo1-host-cmd=[BACKREST-BIN]
repo1-host-config=[TEST_PATH]/backup/pgbackrest.conf
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-master/spool
+ supplemental file: [TEST_PATH]/backup/pgbackrest.conf
-------------------------------------------------------
[db]
pg1-host=db-master
pg1-host-cmd=[BACKREST-BIN]
pg1-host-config=[TEST_PATH]/db-master/pgbackrest.conf
pg1-host-user=[USER-1]
pg1-path=[TEST_PATH]/db-master/db/base
pg1-port=6543
[global]
archive-async=y
buffer-size=[BUFFER-SIZE]
compress-level=3
compress-level-network=1
compress-type=lz4
db-timeout=45
lock-path=[TEST_PATH]/backup/lock
log-level-console=detail
log-level-file=[LOG-LEVEL-FILE]
log-level-stderr=off
log-path=[TEST_PATH]/backup/log
log-subprocess=[LOG-SUBPROCESS]
log-timestamp=n
protocol-timeout=60
repo1-cipher-pass=x
repo1-cipher-type=aes-256-cbc
repo1-path=[TEST_PATH]/backup/repo
[global:backup]
archive-copy=y
start-fast=y
stop all stanzas (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf stop
------------------------------------------------------------------------------------------------------------------------------------
@ -168,6 +232,10 @@ start all stanzas (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf start
------------------------------------------------------------------------------------------------------------------------------------
expire --set=[BACKUP-DIFF-1] (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --set=[BACKUP-DIFF-1] --stanza=db expire
------------------------------------------------------------------------------------------------------------------------------------
incr backup - update during backup (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --stop-auto --buffer-size=[BUFFER-SIZE] --delta --stanza=db backup
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -817,6 +817,7 @@ sub expire
'expire' .
(defined($$oParam{iRetentionFull}) ? " full=$$oParam{iRetentionFull}" : '') .
(defined($$oParam{iRetentionDiff}) ? " diff=$$oParam{iRetentionDiff}" : '') .
(defined($$oParam{strOptionalParam}) ? " $$oParam{strOptionalParam}" : '') .
' (' . $self->nameGet() . ' host)';
&log(INFO, " ${strComment}");
@ -828,6 +829,7 @@ sub expire
' --config=' . $self->backrestConfig() .
(defined($$oParam{iRetentionFull}) ? " --repo1-retention-full=$$oParam{iRetentionFull}" : '') .
(defined($$oParam{iRetentionDiff}) ? " --repo1-retention-diff=$$oParam{iRetentionDiff}" : '') .
(defined($$oParam{strOptionalParam}) ? " $$oParam{strOptionalParam}" : '') .
' --stanza=' . $self->stanza() . ' expire',
{strComment => $strComment, iExpectedExitStatus => $$oParam{iExpectedExitStatus}, oLogTest => $self->{oLogTest},
bLogOutput => $self->synthetic()});

View File

@ -530,6 +530,8 @@ sub run
$oHostDbStandby->clusterStop({bIgnoreLogError => true});
}
my $strAdhocBackup;
# Execute stop and make sure the backup fails
#---------------------------------------------------------------------------------------------------------------------------
# Restart the cluster to check for any errors before continuing since the stop tests will definitely create errors and
@ -538,6 +540,9 @@ sub run
{
$oHostDbMaster->clusterRestart();
# Add backup for adhoc expire
$strAdhocBackup = $oHostBackup->backup(CFGOPTVAL_BACKUP_TYPE_DIFF, 'backup for adhoc expire');
$oHostDbMaster->stop();
$oHostBackup->backup(
@ -630,6 +635,12 @@ sub run
$oHostDbMaster->sqlSelectOne("select pg_start_backup('test backup that will be restarted', true)");
}
if (defined($strAdhocBackup))
{
# Adhoc expire the latest backup - no other tests should be affected
$oHostBackup->expire({strOptionalParam => '--set=' . $strAdhocBackup});
}
# Drop a table
$oHostDbMaster->sqlExecute('drop table test_remove');
$oHostDbMaster->sqlWalRotate();

View File

@ -1,6 +1,8 @@
/***********************************************************************************************************************************
Test Expire Command
***********************************************************************************************************************************/
#include <unistd.h>
#include "common/io/bufferRead.h"
#include "storage/posix/storage.h"
@ -435,7 +437,7 @@ testRun(void)
strLstAddZ(argList, "--repo1-retention-full=1");
harnessCfgLoad(cfgCmdExpire, argList);
TEST_RESULT_VOID(removeExpiredBackup(infoBackup), "remove backups not in backup.info current");
TEST_RESULT_VOID(removeExpiredBackup(infoBackup, NULL), "remove backups not in backup.info current");
harnessLogResult(
"P00 INFO: remove expired backup 20181119-152100F_20181119-152152D\n"
@ -466,7 +468,7 @@ testRun(void)
TEST_ASSIGN(infoBackup, infoBackupNewLoad(ioBufferReadNew(backupInfoContent)), "get backup.info");
TEST_RESULT_VOID(removeExpiredBackup(infoBackup), "remove backups - backup.info current empty");
TEST_RESULT_VOID(removeExpiredBackup(infoBackup, NULL), "remove backups - backup.info current empty");
harnessLogResult("P00 INFO: remove expired backup 20181119-152138F");
TEST_RESULT_STR_Z(
@ -1279,6 +1281,545 @@ testRun(void)
"all prior to 000000010000000000000006 removed from 10-2/0000000100000000");
}
// *****************************************************************************************************************************
if (testBegin("expireAdhocBackup()"))
{
// Create backup.info
storagePutP(storageNewWriteP(storageTest, backupInfoFileName),
harnessInfoChecksumZ(
"[backup:current]\n"
"20181119-152138F={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\","
"\"backup-archive-start\":\"000000020000000000000001\",\"backup-archive-stop\":\"000000020000000000000001\","
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186,"
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900,"
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\","
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152800F={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\","
"\"backup-archive-start\":\"000000020000000000000002\",\"backup-archive-stop\":\"000000020000000000000002\","
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186,"
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900,"
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\","
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152800F_20181119-152152D={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\",\"backup-archive-start\":\"000000020000000000000004\","
"\"backup-archive-stop\":\"000000020000000000000005\",\"backup-info-repo-size\":2369186,"
"\"backup-info-repo-size-delta\":346,\"backup-info-size\":20162900,\"backup-info-size-delta\":8428,"
"\"backup-prior\":\"20181119-152800F\",\"backup-reference\":[\"20181119-152800F\"],"
"\"backup-timestamp-start\":1542640912,\"backup-timestamp-stop\":1542640915,\"backup-type\":\"diff\","
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152800F_20181119-152155I={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\",\"backup-archive-start\":\"000000020000000000000007\","
"\"backup-archive-stop\":\"000000020000000000000007\",\"backup-info-repo-size\":2369186,"
"\"backup-info-repo-size-delta\":346,\"backup-info-size\":20162900,\"backup-info-size-delta\":8428,"
"\"backup-prior\":\"20181119-152800F_20181119-152152D\","
"\"backup-reference\":[\"20181119-152800F\",\"20181119-152800F_20181119-152152D\"],"
"\"backup-timestamp-start\":1542640912,\"backup-timestamp-stop\":1542640915,\"backup-type\":\"incr\","
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152800F_20181119-152252D={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\",\"backup-archive-start\":\"000000020000000000000009\","
"\"backup-archive-stop\":\"000000020000000000000009\",\"backup-info-repo-size\":2369186,"
"\"backup-info-repo-size-delta\":346,\"backup-info-size\":20162900,\"backup-info-size-delta\":8428,"
"\"backup-prior\":\"20181119-152800F\",\"backup-reference\":[\"20181119-152800F\"],"
"\"backup-timestamp-start\":1542640912,\"backup-timestamp-stop\":1542640915,\"backup-type\":\"diff\","
"\"db-id\":1,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152850F={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\","
"\"backup-archive-start\":\"000000010000000000000002\",\"backup-archive-stop\":\"000000010000000000000004\","
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186,"
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900,"
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\","
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152900F={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\","
"\"backup-archive-start\":\"000000010000000000000006\",\"backup-archive-stop\":\"000000010000000000000007\","
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186,"
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900,"
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\","
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"\n"
"[db]\n"
"db-catalog-version=201909212\n"
"db-control-version=1201\n"
"db-id=2\n"
"db-system-id=6626363367545678089\n"
"db-version=\"12\"\n"
"\n"
"[db:history]\n"
"1={\"db-catalog-version\":201409291,\"db-control-version\":942,\"db-system-id\":6625592122879095702,"
"\"db-version\":\"9.4\"}\n"
"2={\"db-catalog-version\":201909212,\"db-control-version\":1201,\"db-system-id\":6626363367545678089,"
"\"db-version\":\"12\"}\n"));
// Add backup directories with manifest file including a resumable backup dependent on last backup
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152138F/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152800F/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152800F_20181119-152152D/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152800F_20181119-152155I/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152800F_20181119-152252D/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152850F/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152900F/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
// Resumable backup
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152900F_20181119-153000I/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
strPtr(backupStanzaPath))),
harnessInfoChecksumZ(
"[backup]\n"
"backup-archive-start=\"000000010000000000000008\"\n"
"backup-label=null\n"
"backup-prior=\"20181119-152900F\"\n"
"backup-timestamp-copy-start=0\n"
"backup-timestamp-start=0\n"
"backup-timestamp-stop=0\n"
"backup-type=\"incr\"\n"
"\n"
"[backup:db]\n"
"db-catalog-version=201909212\n"
"db-control-version=1201\n"
"db-id=2\n"
"db-system-id=6626363367545678089\n"
"db-version=\"12\"\n"
"\n"
"[backup:option]\n"
"option-archive-check=false\n"
"option-archive-copy=false\n"
"option-checksum-page=false\n"
"option-compress=false\n"
"option-compress-type=\"none\"\n"
"option-hardlink=false\n"
"option-online=false\n"
"\n"
"[backup:target]\n"
"pg_data={\"path\":\"{[path]}/pg\",\"type\":\"path\"}\n"
"\n"
"[db]\n"
"postgres={\"db-id\":12980,\"db-last-system-id\":12979}\n"
"\n"
"[target:file]\n"
"pg_data/PG_VERSION={\"size\":3,\"timestamp\":1565282100}\n"
"\n"
"[target:file:default]\n"
"group=\"postgres\"\n"
"master=false\n"
"mode=\"0600\"\n"
"user=\"postgres\"\n"
"\n"
"[target:path]\n"
"pg_data={}\n"
"\n"
"[target:path:default]\n"
"group=\"postgres\"\n"
"mode=\"0700\"\n"
"user=\"postgres\"\n"));
InfoBackup *infoBackup = NULL;
TEST_ASSIGN(infoBackup, infoBackupLoadFile(storageTest, backupInfoFileName, cipherTypeNone, NULL), "get backup.info");
// Create "latest" symlink
const String *latestLink = storagePathP(storageTest, strNewFmt("%s/latest", strPtr(backupStanzaPath)));
THROW_ON_SYS_ERROR_FMT(
symlink(strPtr(infoBackupData(infoBackup, infoBackupDataTotal(infoBackup) - 1).backupLabel), strPtr(latestLink)) == -1,
FileOpenError, "unable to create symlink '%s' to '%s'", strPtr(latestLink),
strPtr(infoBackupData(infoBackup, infoBackupDataTotal(infoBackup) - 1).backupLabel));
// Create archive info
storagePutP(
storageNewWriteP(storageTest, archiveInfoFileName),
harnessInfoChecksumZ(
"[db]\n"
"db-id=2\n"
"db-system-id=6626363367545678089\n"
"db-version=\"12\"\n"
"\n"
"[db:history]\n"
"1={\"db-id\":6625592122879095702,\"db-version\":\"9.4\"}\n"
"2={\"db-id\":6626363367545678089,\"db-version\":\"12\"}"));
// Create archive directories and generate archive
archiveGenerate(storageTest, archiveStanzaPath, 1, 10, "9.4-1", "0000000200000000");
archiveGenerate(storageTest, archiveStanzaPath, 1, 10, "12-2", "0000000100000000");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("invalid backup label");
TEST_RESULT_UINT(
expireAdhocBackup(infoBackup, STRDEF("20201119-123456F_20201119-234567I")), 0,
"label format OK but backup does not exist");
harnessLogResult(
"P00 WARN: backup 20201119-123456F_20201119-234567I does not exist\n"
" HINT: run the info command and confirm the backup is listed");
TEST_ERROR(
expireAdhocBackup(infoBackup, STRDEF(BOGUS_STR)), OptionInvalidValueError,
"'" BOGUS_STR "' is not a valid backup label format");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("expire backup and dependent");
StringList *argList = strLstDup(argListBase);
strLstAddZ(argList, "--repo1-retention-full=1");
strLstAddZ(argList, "--set=20181119-152800F_20181119-152152D");
harnessCfgLoad(cfgCmdExpire, argList);
// Set the log level to detail so archive expiration messages are seen
harnessLogLevelSet(logLevelDetail);
TEST_RESULT_VOID(cmdExpire(), "adhoc expire only backup and dependent");
TEST_RESULT_BOOL(
(storageExistsP(storageTest, strNewFmt("%s/20181119-152138F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(storageTest, strNewFmt("%s/20181119-152800F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152800F_20181119-152252D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152850F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152900F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152900F_20181119-153000I/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
strPtr(backupStanzaPath))) &&
!storageExistsP(
storageTest, strNewFmt("%s/20181119-152800F_20181119-152152D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
!storageExistsP(
storageTest, strNewFmt("%s/20181119-152800F_20181119-152155I/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath)))),
true, "only adhoc and dependents removed - resumable and all other backups remain");
TEST_RESULT_STR(storageInfoP(storageRepo(), STRDEF(STORAGE_REPO_BACKUP "/latest")).linkDestination,
STRDEF("20181119-152900F"), "latest link not updated");
harnessLogResult(
"P00 INFO: expire adhoc backup set: 20181119-152800F_20181119-152152D, 20181119-152800F_20181119-152155I\n"
"P00 INFO: remove expired backup 20181119-152800F_20181119-152155I\n"
"P00 INFO: remove expired backup 20181119-152800F_20181119-152152D\n"
"P00 DETAIL: archive retention on backup 20181119-152138F, archiveId = 9.4-1, start = 000000020000000000000001,"
" stop = 000000020000000000000001\n"
"P00 DETAIL: archive retention on backup 20181119-152800F, archiveId = 9.4-1, start = 000000020000000000000002\n"
"P00 DETAIL: no archive to remove, archiveId = 9.4-1\n"
"P00 DETAIL: archive retention on backup 20181119-152850F, archiveId = 12-2, start = 000000010000000000000002,"
" stop = 000000010000000000000004\n"
"P00 DETAIL: archive retention on backup 20181119-152900F, archiveId = 12-2, start = 000000010000000000000006\n"
"P00 DETAIL: remove archive: archiveId = 12-2, start = 000000010000000000000001, stop = 000000010000000000000001\n"
"P00 DETAIL: remove archive: archiveId = 12-2, start = 000000010000000000000005, stop = 000000010000000000000005");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("expire full and archive (no dependents)");
argList = strLstDup(argListBase);
strLstAddZ(argList, "--repo1-retention-full=1");
strLstAddZ(argList, "--set=20181119-152138F");
harnessCfgLoad(cfgCmdExpire, argList);
TEST_RESULT_VOID(cmdExpire(), "adhoc expire full backup");
TEST_RESULT_BOOL(
(storageExistsP(storageTest, strNewFmt("%s/20181119-152800F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152800F_20181119-152252D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152850F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152900F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152900F_20181119-153000I/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
strPtr(backupStanzaPath))) &&
!storageExistsP(
storageTest, strNewFmt("%s/20181119-152138F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath)))),
true, "only adhoc full removed");
harnessLogResult(
"P00 INFO: expire adhoc backup 20181119-152138F\n"
"P00 INFO: remove expired backup 20181119-152138F\n"
"P00 DETAIL: archive retention on backup 20181119-152800F, archiveId = 9.4-1, start = 000000020000000000000002\n"
"P00 DETAIL: remove archive: archiveId = 9.4-1, start = 000000020000000000000001, stop = 000000020000000000000001\n"
"P00 DETAIL: archive retention on backup 20181119-152850F, archiveId = 12-2, start = 000000010000000000000002,"
" stop = 000000010000000000000004\n"
"P00 DETAIL: archive retention on backup 20181119-152900F, archiveId = 12-2, start = 000000010000000000000006\n"
"P00 DETAIL: no archive to remove, archiveId = 12-2");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("expire latest and resumable");
argList = strLstDup(argListBase);
strLstAddZ(argList, "--repo1-retention-full=1");
strLstAddZ(argList, "--set=20181119-152900F");
harnessCfgLoad(cfgCmdExpire, argList);
String *archiveRemaining = strNew("");
strCatFmt(
archiveRemaining, "%s, %s",
strPtr(archiveExpectList(2, 4, "0000000100000000")),
strPtr(archiveExpectList(6, 10, "0000000100000000")));
TEST_RESULT_VOID(cmdExpire(), "adhoc expire latest backup");
TEST_RESULT_BOOL(
(storageExistsP(storageTest, strNewFmt("%s/20181119-152800F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
storageExistsP(
storageTest, strNewFmt("%s/20181119-152800F_20181119-152252D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
!storageExistsP(
storageTest, strNewFmt("%s/20181119-152900F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
!storageExistsP(
storageTest, strNewFmt("%s/20181119-152900F_20181119-153000I/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
strPtr(backupStanzaPath)))),
true, "latest and resumable removed");
harnessLogResult(
"P00 WARN: expiring latest backup 20181119-152900F - the ability to perform point-in-time-recovery (PITR) may be"
" affected\n"
" HINT: non-default settings for 'repo1-retention-archive'/'repo1-retention-archive-type'"
" (even in prior expires) can cause gaps in the WAL.\n"
"P00 INFO: expire adhoc backup 20181119-152900F\n"
"P00 INFO: remove expired backup 20181119-152900F_20181119-153000I\n"
"P00 INFO: remove expired backup 20181119-152900F\n"
"P00 DETAIL: archive retention on backup 20181119-152800F, archiveId = 9.4-1, start = 000000020000000000000002\n"
"P00 DETAIL: no archive to remove, archiveId = 9.4-1\n"
"P00 DETAIL: archive retention on backup 20181119-152850F, archiveId = 12-2, start = 000000010000000000000002\n"
"P00 DETAIL: no archive to remove, archiveId = 12-2");
TEST_RESULT_STR(storageInfoP(storageRepo(), STRDEF(STORAGE_REPO_BACKUP "/latest")).linkDestination,
STRDEF("20181119-152850F"), "latest link updated");
TEST_RESULT_STR(
strLstJoin(strLstSort(storageListP(
storageTest, strNewFmt("%s/%s/%s", strPtr(archiveStanzaPath), "12-2", "0000000100000000")), sortOrderAsc), ", "),
archiveRemaining,
"no archives removed from latest except what was already removed");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on expire last full backup in current db-id");
argList = strLstDup(argListAvoidWarn);
strLstAddZ(argList, "--set=20181119-152850F");
harnessCfgLoad(cfgCmdExpire, argList);
TEST_ERROR(
cmdExpire(), BackupSetInvalidError,
"full backup 20181119-152850F cannot be expired until another full backup has been created");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("allow adhoc expire on last full backup in prior db-id");
argList = strLstDup(argListAvoidWarn);
strLstAddZ(argList, "--set=20181119-152800F");
harnessCfgLoad(cfgCmdExpire, argList);
TEST_RESULT_VOID(cmdExpire(), "adhoc expire last prior db-id backup");
TEST_RESULT_BOOL(
(storageExistsP(storageTest, strNewFmt("%s/20181119-152850F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
!storageExistsP(
storageTest, strNewFmt("%s/20181119-152800F/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath))) &&
!storageExistsP(
storageTest, strNewFmt("%s/20181119-152800F_20181119-152252D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath)))),
true, "only last prior backup removed");
harnessLogResult(
strPtr(strNewFmt(
"P00 INFO: expire adhoc backup set: 20181119-152800F, 20181119-152800F_20181119-152252D\n"
"P00 INFO: remove expired backup 20181119-152800F_20181119-152252D\n"
"P00 INFO: remove expired backup 20181119-152800F\n"
"P00 INFO: remove archive path: %s/repo/archive/db/9.4-1\n"
"P00 DETAIL: archive retention on backup 20181119-152850F, archiveId = 12-2, start = 000000010000000000000002\n"
"P00 DETAIL: no archive to remove, archiveId = 12-2", testPath())));
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("error on expire last full backup on disk");
argList = strLstDup(argListAvoidWarn);
strLstAddZ(argList, "--set=20181119-152850F");
harnessCfgLoad(cfgCmdExpire, argList);
TEST_ERROR(
cmdExpire(), BackupSetInvalidError,
"full backup 20181119-152850F cannot be expired until another full backup has been created");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("adhoc dry-run");
// Create backup.info
storagePutP(storageNewWriteP(storageTest, backupInfoFileName),
harnessInfoChecksumZ(
"[backup:current]\n"
"20181119-152850F={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\","
"\"backup-archive-start\":\"000000010000000000000002\",\"backup-archive-stop\":\"000000010000000000000004\","
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186,"
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900,"
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\","
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152850F_20181119-152252D={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\",\"backup-archive-start\":\"000000010000000000000006\","
"\"backup-archive-stop\":\"000000010000000000000007\",\"backup-info-repo-size\":2369186,"
"\"backup-info-repo-size-delta\":346,\"backup-info-size\":20162900,\"backup-info-size-delta\":8428,"
"\"backup-prior\":\"20181119-152850F\",\"backup-reference\":[\"20181119-152850F\"],"
"\"backup-timestamp-start\":1542640912,\"backup-timestamp-stop\":1542640915,\"backup-type\":\"diff\","
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"\n"
"[db]\n"
"db-catalog-version=201909212\n"
"db-control-version=1201\n"
"db-id=2\n"
"db-system-id=6626363367545678089\n"
"db-version=\"12\"\n"
"\n"
"[db:history]\n"
"1={\"db-catalog-version\":201409291,\"db-control-version\":942,\"db-system-id\":6625592122879095702,"
"\"db-version\":\"9.4\"}\n"
"2={\"db-catalog-version\":201909212,\"db-control-version\":1201,\"db-system-id\":6626363367545678089,"
"\"db-version\":\"12\"}\n"));
argList = strLstDup(argListAvoidWarn);
strLstAddZ(argList, "--set=20181119-152850F_20181119-152252D");
strLstAddZ(argList, "--dry-run");
harnessCfgLoad(cfgCmdExpire, argList);
// Load the backup info. Do not store a manifest file for the adhoc backup for code coverage
TEST_ASSIGN(infoBackup, infoBackupLoadFile(storageTest, backupInfoFileName, cipherTypeNone, NULL), "get backup.info");
// Create the manifest file to create the directory then remove the file for code coverage
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152850F_20181119-152252D/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storageRemoveP(
storageTest, strNewFmt("%s/20181119-152850F_20181119-152252D/" BACKUP_MANIFEST_FILE, strPtr(backupStanzaPath)));
String *adhocBackupLabel = strNew("20181119-152850F_20181119-152252D");
TEST_RESULT_UINT(expireAdhocBackup(infoBackup, adhocBackupLabel), 1, "adhoc expire last dependent backup");
TEST_RESULT_VOID(removeExpiredBackup(infoBackup, adhocBackupLabel), "code coverage: removeExpireBackup with no manifests");
harnessLogResult(
"P00 WARN: [DRY-RUN] expiring latest backup 20181119-152850F_20181119-152252D - the ability to perform"
" point-in-time-recovery (PITR) may be affected\n"
" HINT: non-default settings for 'repo1-retention-archive'/'repo1-retention-archive-type'"
" (even in prior expires) can cause gaps in the WAL.\n"
"P00 INFO: [DRY-RUN] expire adhoc backup 20181119-152850F_20181119-152252D\n"
"P00 INFO: [DRY-RUN] remove expired backup 20181119-152850F_20181119-152252D");
//--------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("resumable possibly based on adhoc expire backup");
argList = strLstDup(argListAvoidWarn);
strLstAddZ(argList, "--set=20181119-152850F_20181119-152252D");
harnessCfgLoad(cfgCmdExpire, argList);
// Create backup.info
storagePutP(storageNewWriteP(storageTest, backupInfoFileName),
harnessInfoChecksumZ(
"[backup:current]\n"
"20181119-152850F={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\","
"\"backup-archive-start\":\"000000010000000000000002\",\"backup-archive-stop\":\"000000010000000000000004\","
"\"backup-info-repo-size\":2369186,\"backup-info-repo-size-delta\":2369186,"
"\"backup-info-size\":20162900,\"backup-info-size-delta\":20162900,"
"\"backup-timestamp-start\":1542640898,\"backup-timestamp-stop\":1542640911,\"backup-type\":\"full\","
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"20181119-152850F_20181119-152252D={"
"\"backrest-format\":5,\"backrest-version\":\"2.08dev\",\"backup-archive-start\":\"000000010000000000000006\","
"\"backup-archive-stop\":\"000000010000000000000007\",\"backup-info-repo-size\":2369186,"
"\"backup-info-repo-size-delta\":346,\"backup-info-size\":20162900,\"backup-info-size-delta\":8428,"
"\"backup-prior\":\"20181119-152850F\",\"backup-reference\":[\"20181119-152850F\"],"
"\"backup-timestamp-start\":1542640912,\"backup-timestamp-stop\":1542640915,\"backup-type\":\"diff\","
"\"db-id\":2,\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false,"
"\"option-checksum-page\":true,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
"\n"
"[db]\n"
"db-catalog-version=201909212\n"
"db-control-version=1201\n"
"db-id=2\n"
"db-system-id=6626363367545678089\n"
"db-version=\"12\"\n"
"\n"
"[db:history]\n"
"1={\"db-catalog-version\":201409291,\"db-control-version\":942,\"db-system-id\":6625592122879095702,"
"\"db-version\":\"9.4\"}\n"
"2={\"db-catalog-version\":201909212,\"db-control-version\":1201,\"db-system-id\":6626363367545678089,"
"\"db-version\":\"12\"}\n"));
// Adhoc backup and resumable backup manifests
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152850F_20181119-152252D/" BACKUP_MANIFEST_FILE,
strPtr(backupStanzaPath))), BUFSTRDEF("tmp"));
storagePutP(
storageNewWriteP(storageTest, strNewFmt("%s/20181119-152850F_20181200-152252D/" BACKUP_MANIFEST_FILE INFO_COPY_EXT,
strPtr(backupStanzaPath))),
harnessInfoChecksumZ(
"[backup]\n"
"backup-archive-start=\"000000010000000000000009\"\n"
"backup-label=null\n"
"backup-prior=\"20181119-152850F\"\n"
"backup-timestamp-copy-start=0\n"
"backup-timestamp-start=0\n"
"backup-timestamp-stop=0\n"
"backup-type=\"incr\"\n"
"\n"
"[backup:db]\n"
"db-catalog-version=201909212\n"
"db-control-version=1201\n"
"db-id=2\n"
"db-system-id=6626363367545678089\n"
"db-version=\"12\"\n"
"\n"
"[backup:option]\n"
"option-archive-check=false\n"
"option-archive-copy=false\n"
"option-checksum-page=false\n"
"option-compress=false\n"
"option-compress-type=\"none\"\n"
"option-hardlink=false\n"
"option-online=false\n"
"\n"
"[backup:target]\n"
"pg_data={\"path\":\"{[path]}/pg\",\"type\":\"path\"}\n"
"\n"
"[db]\n"
"postgres={\"db-id\":12980,\"db-last-system-id\":12979}\n"
"\n"
"[target:file]\n"
"pg_data/PG_VERSION={\"size\":3,\"timestamp\":1565282100}\n"
"\n"
"[target:file:default]\n"
"group=\"postgres\"\n"
"master=false\n"
"mode=\"0600\"\n"
"user=\"postgres\"\n"
"\n"
"[target:path]\n"
"pg_data={}\n"
"\n"
"[target:path:default]\n"
"group=\"postgres\"\n"
"mode=\"0700\"\n"
"user=\"postgres\"\n"));
archiveGenerate(storageTest, archiveStanzaPath, 2, 10, "12-2", "0000000100000000");
TEST_RESULT_VOID(cmdExpire(), "adhoc expire latest with resumable possibly based on it");
harnessLogResult(
"P00 WARN: expiring latest backup 20181119-152850F_20181119-152252D - the ability to perform point-in-time-recovery"
" (PITR) may be affected\n"
" HINT: non-default settings for 'repo1-retention-archive'/'repo1-retention-archive-type'"
" (even in prior expires) can cause gaps in the WAL.\n"
"P00 INFO: expire adhoc backup 20181119-152850F_20181119-152252D\n"
"P00 INFO: remove expired backup 20181119-152850F_20181119-152252D\n"
"P00 DETAIL: archive retention on backup 20181119-152850F, archiveId = 12-2, start = 000000010000000000000002\n"
"P00 DETAIL: no archive to remove, archiveId = 12-2");
harnessLogLevelReset();
}
// *****************************************************************************************************************************
if (testBegin("archiveIdComparator()"))
{