1
0
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:
Andrey Sokolov 2023-06-28 19:19:20 +03:00 committed by GitHub
parent 5cbef3ade2
commit 0ac09344dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 109 additions and 8 deletions

View File

@ -29,6 +29,18 @@
<p>Skip <file>recovery.signal</file> for <postgres/> &gt;= 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>

View File

@ -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))

View File

@ -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(&timestamp, &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);

View File

@ -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
***********************************************************************************************************************************/

View File

@ -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");