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:
parent
ad33f545d1
commit
1c1a710460
@ -702,6 +702,10 @@ my %hConfigDefine =
|
||||
&CFGDEF_TYPE => CFGDEF_TYPE_STRING,
|
||||
&CFGDEF_COMMAND =>
|
||||
{
|
||||
&CFGCMD_EXPIRE =>
|
||||
{
|
||||
&CFGDEF_REQUIRED => false,
|
||||
},
|
||||
&CFGCMD_INFO =>
|
||||
{
|
||||
&CFGDEF_REQUIRED => false,
|
||||
|
@ -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 &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="">
|
||||
|
@ -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"/>
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -607,7 +607,7 @@ unit:
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: expire
|
||||
total: 7
|
||||
total: 8
|
||||
|
||||
coverage:
|
||||
command/expire/expire: full
|
||||
|
@ -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
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -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()});
|
||||
|
@ -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();
|
||||
|
@ -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()"))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user