mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-01-30 05:39:12 +02:00
Fix unique label generation for diff/incr backup.
If there were at least two full backups and the last one was expired, it was impossible to make either a differential or incremental backup without first making a new full backup. The backupLabelCreate() function identified this situation as clock skew because the new backup label was compared with label of the expired full backup. If the new backup is differential or incremental, then its label is now compared with the labels of differential or incremental backups related to the same full backup. Also convert a hard-coded date length to a macro.
This commit is contained in:
parent
5cbef3ade2
commit
0ac09344dc
@ -29,6 +29,18 @@
|
||||
|
||||
<p>Skip <file>recovery.signal</file> for <postgres/> >= 12 when recovery <br-option>type=none</br-option>.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-issue id="1873"/>
|
||||
<github-pull-request id="2104"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="andrey.sokolov"/>
|
||||
<release-item-reviewer id="david.steele"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Fix unique label generation for diff/incr backup.</p>
|
||||
</release-item>
|
||||
</release-bug-list>
|
||||
|
||||
<release-improvement-list>
|
||||
|
@ -78,14 +78,19 @@ backupLabelCreate(const BackupType type, const String *const backupLabelPrior, c
|
||||
|
||||
if (!strLstEmpty(historyYearList))
|
||||
{
|
||||
// For full backup compare against all backups in the history. For other backup types find backups whose name begins
|
||||
// with full backup part of backupLabelLatest. This prevents label generation from failing when the last full backup is
|
||||
// removed and then an diff/incr is generated from the last remaining full backup.
|
||||
const String *const fileNameRegExp =
|
||||
(type == backupTypeFull) ?
|
||||
backupRegExpP(.full = true, .differential = true, .incremental = true, .noAnchorEnd = true) :
|
||||
strNewFmt("^%.*sF\\_" DATE_TIME_REGEX "(D|I)", DATE_TIME_LEN, strZ(backupLabelLatest));
|
||||
|
||||
const StringList *historyList = strLstSort(
|
||||
storageListP(
|
||||
storageRepo(),
|
||||
strNewFmt(STORAGE_REPO_BACKUP "/" BACKUP_PATH_HISTORY "/%s", strZ(strLstGet(historyYearList, 0))),
|
||||
.expression = strNewFmt(
|
||||
"%s\\.manifest\\.%s$",
|
||||
strZ(backupRegExpP(.full = true, .differential = true, .incremental = true, .noAnchorEnd = true)),
|
||||
strZ(compressTypeStr(compressTypeGz)))),
|
||||
.expression = strNewFmt("%s\\.manifest\\.%s$", strZ(fileNameRegExp), strZ(compressTypeStr(compressTypeGz)))),
|
||||
sortOrderDesc);
|
||||
|
||||
if (!strLstEmpty(historyList))
|
||||
|
@ -13,7 +13,6 @@ Common Functions and Definitions for Backup and Expire Commands
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
#define DATE_TIME_REGEX "[0-9]{8}\\-[0-9]{6}"
|
||||
#define BACKUP_LINK_LATEST "latest"
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
@ -60,7 +59,7 @@ backupLabelFormat(const BackupType type, const String *const backupLabelPrior, c
|
||||
|
||||
// Format the timestamp
|
||||
struct tm timePart;
|
||||
char buffer[16];
|
||||
char buffer[DATE_TIME_LEN + 1];
|
||||
|
||||
THROW_ON_SYS_ERROR(
|
||||
strftime(buffer, sizeof(buffer), "%Y%m%d-%H%M%S", localtime_r(×tamp, &timePart)) == 0, AssertError,
|
||||
@ -77,7 +76,7 @@ backupLabelFormat(const BackupType type, const String *const backupLabelPrior, c
|
||||
else
|
||||
{
|
||||
// Get the full backup portion of the prior backup label and append the diff/incr timestamp
|
||||
result = strNewFmt("%.16s_%s%s", strZ(backupLabelPrior), buffer, type == backupTypeDiff ? "D" : "I");
|
||||
result = strNewFmt("%.*s_%s%s", DATE_TIME_LEN + 1, strZ(backupLabelPrior), buffer, type == backupTypeDiff ? "D" : "I");
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(STRING, result);
|
||||
|
@ -17,6 +17,10 @@ Backup constants
|
||||
#define BACKUP_PATH_HISTORY "backup.history"
|
||||
#define BACKUP_BLOCK_INCR_EXT ".pgbi"
|
||||
|
||||
// Date and time must be in %Y%m%d-%H%M%S format, for example 20220901-193409
|
||||
#define DATE_TIME_REGEX "[0-9]{8}\\-[0-9]{6}"
|
||||
#define DATE_TIME_LEN (8 + 1 + 6)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
|
@ -1655,6 +1655,7 @@ testRun(void)
|
||||
|
||||
time_t timestamp = 1575401652;
|
||||
String *backupLabel = backupLabelFormat(backupTypeFull, NULL, timestamp);
|
||||
const String *const olderBackupLabel = backupLabelFormat(backupTypeFull, NULL, timestamp - 3);
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("assign label when no history");
|
||||
@ -1678,10 +1679,90 @@ testRun(void)
|
||||
TEST_TITLE("assign label when backup is older");
|
||||
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageRepoWrite(), zNewFmt(STORAGE_REPO_BACKUP "/%s", strZ(backupLabelFormat(backupTypeFull, NULL, timestamp - 2))));
|
||||
storageRepoWrite(), zNewFmt(STORAGE_REPO_BACKUP "/%s", strZ(olderBackupLabel)));
|
||||
|
||||
TEST_RESULT_STR(backupLabelCreate(backupTypeFull, NULL, timestamp), backupLabel, "create label");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("differential and incremental backups read history related to full backup they are created on (full only)");
|
||||
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageRepoWrite(),
|
||||
zNewFmt(
|
||||
STORAGE_REPO_BACKUP "/backup.history/2019/%s.manifest.gz",
|
||||
strZ(backupLabelFormat(backupTypeFull, NULL, timestamp - 1))));
|
||||
|
||||
TEST_RESULT_STR(
|
||||
backupLabelCreate(backupTypeDiff, olderBackupLabel, timestamp),
|
||||
backupLabelFormat(backupTypeDiff, olderBackupLabel, timestamp),
|
||||
"create label");
|
||||
|
||||
TEST_RESULT_STR(
|
||||
backupLabelCreate(backupTypeIncr, olderBackupLabel, timestamp),
|
||||
backupLabelFormat(backupTypeIncr, olderBackupLabel, timestamp),
|
||||
"create label");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("differential and incremental backups read history related to full backup they are created on (diff exists)");
|
||||
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageRepoWrite(),
|
||||
zNewFmt(
|
||||
STORAGE_REPO_BACKUP "/backup.history/2019/%s.manifest.gz",
|
||||
strZ(backupLabelFormat(backupTypeDiff, olderBackupLabel, timestamp - 2))));
|
||||
|
||||
TEST_RESULT_STR(
|
||||
backupLabelCreate(backupTypeDiff, olderBackupLabel, timestamp),
|
||||
backupLabelFormat(backupTypeDiff, olderBackupLabel, timestamp),
|
||||
"create label");
|
||||
|
||||
TEST_RESULT_STR(
|
||||
backupLabelCreate(backupTypeIncr, olderBackupLabel, timestamp),
|
||||
backupLabelFormat(backupTypeIncr, olderBackupLabel, timestamp),
|
||||
"create label");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("differential and incremental backups read history related to full backup they are created on (diff and incr)");
|
||||
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageRepoWrite(),
|
||||
zNewFmt(
|
||||
STORAGE_REPO_BACKUP "/backup.history/2019/%s.manifest.gz",
|
||||
strZ(backupLabelFormat(backupTypeIncr, olderBackupLabel, timestamp - 1))));
|
||||
|
||||
TEST_RESULT_STR(
|
||||
backupLabelCreate(backupTypeDiff, olderBackupLabel, timestamp),
|
||||
backupLabelFormat(backupTypeDiff, olderBackupLabel, timestamp),
|
||||
"create label");
|
||||
|
||||
TEST_RESULT_STR(
|
||||
backupLabelCreate(backupTypeIncr, olderBackupLabel, timestamp),
|
||||
backupLabelFormat(backupTypeIncr, olderBackupLabel, timestamp),
|
||||
"create label");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("differential and incremental backups read history related to full backup they are created on (skew)");
|
||||
|
||||
HRN_STORAGE_PUT_EMPTY(
|
||||
storageRepoWrite(),
|
||||
zNewFmt(
|
||||
STORAGE_REPO_BACKUP "/backup.history/2019/%s.manifest.gz",
|
||||
strZ(backupLabelFormat(backupTypeIncr, olderBackupLabel, timestamp + 3))));
|
||||
|
||||
TEST_ERROR(
|
||||
backupLabelCreate(backupTypeDiff, olderBackupLabel, timestamp), ClockError,
|
||||
"new backup label '20191203-193409F_20191203-193413D' is not later "
|
||||
"than latest backup label '20191203-193409F_20191203-193415I'\n"
|
||||
"HINT: has the timezone changed?\n"
|
||||
"HINT: is there clock skew?");
|
||||
|
||||
TEST_ERROR(
|
||||
backupLabelCreate(backupTypeIncr, olderBackupLabel, timestamp), ClockError,
|
||||
"new backup label '20191203-193409F_20191203-193413I' is not later "
|
||||
"than latest backup label '20191203-193409F_20191203-193415I'\n"
|
||||
"HINT: has the timezone changed?\n"
|
||||
"HINT: is there clock skew?");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("advance time when backup is same");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user