mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2024-12-12 10:04:14 +02:00
Fix spurious automatic delta backup on backup from standby.
When performing backup from standby the file sizes on the standby may not be equal to file sizes on the primary. This is because replication continues during the backup and by the time the file is copied from the standby it may have changed. Since we cap the size of all files copied from the standby this practically applies to truncation and in particular truncation of free space maps (at least, every case we have seen so far is an fsm). Free space maps are especially vulnerable since they are only partially replicated, which amplifies the difference between the primary and standby. On an incremental backup it may look like the size has changed on the primary (because of the final size recorded by the standby in the prior backup) but the timestamp may not have changed on the primary and this will trigger a checksum delta for safety. While this has no impact on backup integrity, checksum delta incrementals can run much longer than regular incrementals and backup schedules may be impacted. The solution is to preserve the original size in the manifest and use it to do the time/size check. In the case of backup from standby the original size will always be the size on the primary, which makes comparisons against subsequent file sizes on the primary consistent. Original size is only stored in the manifest when it differs from final size, so there should not be any noticeable manifest bloat.
This commit is contained in:
parent
4c27d74bbd
commit
5ed6f8df14
@ -46,6 +46,20 @@
|
||||
<p>Fix block incremental file names in <cmd>verify</cmd> command.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-issue id="2020"/>
|
||||
<github-pull-request id="2124"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-ideator id="krmozejko"/>
|
||||
<release-item-ideator id="don.seiler"/>
|
||||
<release-item-contributor id="david.steele"/>
|
||||
<release-item-reviewer id="stephen.frost"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p>Fix spurious automatic delta backup on backup from standby.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-issue id="2062"/>
|
||||
<github-pull-request id="2070"/>
|
||||
@ -12775,6 +12789,11 @@
|
||||
<contributor-id type="github">kaceymiri</contributor-id>
|
||||
</contributor>
|
||||
|
||||
<contributor id="krmozejko">
|
||||
<contributor-name-display>krmozejko</contributor-name-display>
|
||||
<contributor-id type="github">krmozejko</contributor-id>
|
||||
</contributor>
|
||||
|
||||
<contributor id="kyle.nevins">
|
||||
<contributor-name-display>Kyle Nevins</contributor-name-display>
|
||||
<contributor-id type="github">kyle-nevins</contributor-id>
|
||||
|
@ -1209,6 +1209,7 @@ backupFilePut(
|
||||
.user = basePath->user,
|
||||
.group = basePath->group,
|
||||
.size = strSize(content),
|
||||
.sizeOriginal = strSize(content),
|
||||
.sizeRepo = pckReadU64P(ioFilterGroupResultP(filterGroup, SIZE_FILTER_TYPE)),
|
||||
.timestamp = timestamp,
|
||||
.checksumSha1 = bufPtr(pckReadBinP(ioFilterGroupResultP(filterGroup, CRYPTO_HASH_FILTER_TYPE, .idx = 0))),
|
||||
@ -2403,6 +2404,7 @@ backupArchiveCheckCopy(const BackupData *const backupData, Manifest *const manif
|
||||
.user = basePath->user,
|
||||
.group = basePath->group,
|
||||
.size = backupData->walSegmentSize,
|
||||
.sizeOriginal = backupData->walSegmentSize,
|
||||
.sizeRepo = pckReadU64P(ioFilterGroupResultP(filterGroup, SIZE_FILTER_TYPE)),
|
||||
.timestamp = manifestData(manifest)->backupTimestampStop,
|
||||
.checksumSha1 = bufPtr(bufNewDecode(encodingHex, strSubN(archiveFile, 25, 40))),
|
||||
|
@ -108,6 +108,7 @@ typedef enum
|
||||
manifestFilePackFlagChecksumPage,
|
||||
manifestFilePackFlagChecksumPageError,
|
||||
manifestFilePackFlagChecksumPageErrorList,
|
||||
manifestFilePackFlagSizeOriginal,
|
||||
manifestFilePackFlagMode,
|
||||
manifestFilePackFlagUser,
|
||||
manifestFilePackFlagUserNull,
|
||||
@ -166,6 +167,9 @@ manifestFilePack(const Manifest *const manifest, const ManifestFile *const file)
|
||||
if (file->blockIncrSize != 0)
|
||||
flag |= 1 << manifestFilePackFlagBlockIncr;
|
||||
|
||||
if (file->sizeOriginal != file->size)
|
||||
flag |= 1 << manifestFilePackFlagSizeOriginal;
|
||||
|
||||
if (file->mode != manifest->fileModeDefault)
|
||||
flag |= 1 << manifestFilePackFlagMode;
|
||||
|
||||
@ -184,6 +188,10 @@ manifestFilePack(const Manifest *const manifest, const ManifestFile *const file)
|
||||
// Size
|
||||
cvtUInt64ToVarInt128(file->size, buffer, &bufferPos, sizeof(buffer));
|
||||
|
||||
// Original size
|
||||
if (flag & (1 << manifestFilePackFlagSizeOriginal))
|
||||
cvtUInt64ToVarInt128(file->sizeOriginal, buffer, &bufferPos, sizeof(buffer));
|
||||
|
||||
// Use the first timestamp that appears as the base for all other timestamps. Ideally we would like a timestamp as close to the
|
||||
// middle as possible but it doesn't seem worth doing the calculation.
|
||||
if (manifestPackBaseTime == -1)
|
||||
@ -308,6 +316,12 @@ manifestFileUnpack(const Manifest *const manifest, const ManifestFilePack *const
|
||||
// Size
|
||||
result.size = cvtUInt64FromVarInt128((const uint8_t *)filePack, &bufferPos, UINT_MAX);
|
||||
|
||||
// Original size
|
||||
if (flag & (1 << manifestFilePackFlagSizeOriginal))
|
||||
result.sizeOriginal = cvtUInt64FromVarInt128((const uint8_t *)filePack, &bufferPos, UINT_MAX);
|
||||
else
|
||||
result.sizeOriginal = result.size;
|
||||
|
||||
// Timestamp
|
||||
result.timestamp =
|
||||
manifestPackBaseTime - (time_t)cvtInt64FromZigZag(cvtUInt64FromVarInt128((const uint8_t *)filePack, &bufferPos, UINT_MAX));
|
||||
@ -1052,6 +1066,7 @@ manifestBuildInfo(
|
||||
.user = info->user,
|
||||
.group = info->group,
|
||||
.size = info->size,
|
||||
.sizeOriginal = info->size,
|
||||
.sizeRepo = info->size,
|
||||
.timestamp = info->timeModified,
|
||||
};
|
||||
@ -1634,12 +1649,12 @@ manifestBuildIncr(Manifest *this, const Manifest *manifestPrior, BackupType type
|
||||
}
|
||||
|
||||
// Check for size change with no timestamp change
|
||||
if (file.size != filePrior.size && file.timestamp == filePrior.timestamp)
|
||||
if (file.sizeOriginal != filePrior.sizeOriginal && file.timestamp == filePrior.timestamp)
|
||||
{
|
||||
LOG_WARN_FMT(
|
||||
"file '%s' has same timestamp (%" PRId64 ") as prior but different size (prior %" PRIu64 ", current"
|
||||
" %" PRIu64 "), enabling delta checksum",
|
||||
strZ(manifestPathPg(file.name)), (int64_t)file.timestamp, filePrior.size, file.size);
|
||||
strZ(manifestPathPg(file.name)), (int64_t)file.timestamp, filePrior.sizeOriginal, file.sizeOriginal);
|
||||
|
||||
this->pub.data.backupOptionDelta = BOOL_TRUE_VAR;
|
||||
break;
|
||||
@ -1850,6 +1865,7 @@ manifestBuildComplete(
|
||||
#define MANIFEST_KEY_PATH STRID5("path", 0x450300)
|
||||
#define MANIFEST_KEY_REFERENCE STRID5("reference", 0x51b8b2298b20)
|
||||
#define MANIFEST_KEY_SIZE STRID5("size", 0x2e9330)
|
||||
#define MANIFEST_KEY_SIZE_ORIGINAL STRID5("szo", 0x3f530)
|
||||
#define MANIFEST_KEY_SIZE_REPO STRID5("repo-size", 0x5d267b7c0b20)
|
||||
#define MANIFEST_KEY_TABLESPACE_ID "tablespace-id"
|
||||
#define MANIFEST_KEY_TABLESPACE_NAME "tablespace-name"
|
||||
@ -2053,6 +2069,13 @@ manifestLoadCallback(void *callbackData, const String *const section, const Stri
|
||||
if (file.size == 0)
|
||||
file.checksumSha1 = bufPtrConst(HASH_TYPE_SHA1_ZERO_BUF);
|
||||
|
||||
// If original is not present in the manifest file then it is the same as size (i.e. the file did not change size during
|
||||
// copy) -- to save space the original size is only stored in the manifest file if it is different than size.
|
||||
if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_SIZE_ORIGINAL))
|
||||
file.sizeOriginal = jsonReadUInt64(json);
|
||||
else
|
||||
file.sizeOriginal = file.size;
|
||||
|
||||
// Timestamp is required so error if it is not present
|
||||
if (jsonReadKeyExpectStrId(json, MANIFEST_KEY_TIMESTAMP))
|
||||
file.timestamp = (time_t)jsonReadInt64(json);
|
||||
@ -2766,6 +2789,10 @@ manifestSaveCallback(void *const callbackData, const String *const sectionNext,
|
||||
jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE_REPO), file.sizeRepo);
|
||||
|
||||
jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE), file.size);
|
||||
|
||||
if (file.sizeOriginal != file.size)
|
||||
jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_SIZE_ORIGINAL), file.sizeOriginal);
|
||||
|
||||
jsonWriteUInt64(jsonWriteKeyStrId(json, MANIFEST_KEY_TIMESTAMP), (uint64_t)file.timestamp);
|
||||
|
||||
if (!varEq(manifestOwnerVar(file.user), saveData->userDefault))
|
||||
|
@ -157,7 +157,8 @@ typedef struct ManifestFile
|
||||
size_t blockIncrSize; // Size of incremental blocks
|
||||
size_t blockIncrChecksumSize; // Size of incremental block checksum
|
||||
uint64_t blockIncrMapSize; // Block incremental map size
|
||||
uint64_t size; // Original size
|
||||
uint64_t size; // Final size (after copy)
|
||||
uint64_t sizeOriginal; // Original size (from manifest build)
|
||||
uint64_t sizeRepo; // Size in repo
|
||||
time_t timestamp; // Original timestamp
|
||||
} ManifestFile;
|
||||
|
@ -64,6 +64,7 @@ hrnManifestFileAdd(Manifest *const manifest, const HrnManifestFile hrnManifestFi
|
||||
FUNCTION_HARNESS_PARAM(UINT64, hrnManifestFile.blockIncrSize);
|
||||
FUNCTION_HARNESS_PARAM(UINT64, hrnManifestFile.blockIncrMapSize);
|
||||
FUNCTION_HARNESS_PARAM(UINT64, hrnManifestFile.size);
|
||||
FUNCTION_HARNESS_PARAM(UINT64, hrnManifestFile.sizeOriginal);
|
||||
FUNCTION_HARNESS_PARAM(UINT64, hrnManifestFile.sizeRepo);
|
||||
FUNCTION_HARNESS_PARAM(TIME, hrnManifestFile.timestamp);
|
||||
FUNCTION_HARNESS_END();
|
||||
@ -87,10 +88,14 @@ hrnManifestFileAdd(Manifest *const manifest, const HrnManifestFile hrnManifestFi
|
||||
.blockIncrChecksumSize = hrnManifestFile.blockIncrChecksumSize,
|
||||
.blockIncrMapSize = hrnManifestFile.blockIncrMapSize,
|
||||
.size = hrnManifestFile.size,
|
||||
.sizeOriginal = hrnManifestFile.sizeOriginal,
|
||||
.sizeRepo = hrnManifestFile.sizeRepo,
|
||||
.timestamp = hrnManifestFile.timestamp,
|
||||
};
|
||||
|
||||
if (manifestFile.sizeOriginal == 0)
|
||||
manifestFile.sizeOriginal = manifestFile.size;
|
||||
|
||||
if (hrnManifestFile.mode == 0)
|
||||
manifestFile.mode = 0600;
|
||||
else
|
||||
|
@ -46,6 +46,7 @@ typedef struct HrnManifestFile
|
||||
size_t blockIncrChecksumSize;
|
||||
uint64_t blockIncrMapSize;
|
||||
uint64_t size;
|
||||
uint64_t sizeOriginal;
|
||||
uint64_t sizeRepo;
|
||||
time_t timestamp;
|
||||
} HrnManifestFile;
|
||||
|
@ -3148,7 +3148,8 @@ testRun(void)
|
||||
",\"timestamp\":1571200002}\n"
|
||||
"pg_data/base/1/1={\"size\":0,\"timestamp\":1571200000}\n"
|
||||
"pg_data/base/1/2={\"checksum\":\"54ceb91256e8190e474aa752a6e0650a2df5ba37\",\"size\":2,\"timestamp\":1571200000}\n"
|
||||
"pg_data/base/1/3={\"checksum\":\"3c01bdbb26f358bab27f267924aa2c9a03fcfdb8\",\"size\":3,\"timestamp\":1571200000}\n"
|
||||
"pg_data/base/1/3={\"checksum\":\"3c01bdbb26f358bab27f267924aa2c9a03fcfdb8\",\"size\":3,\"szo\":4"
|
||||
",\"timestamp\":1571200000}\n"
|
||||
"pg_data/global/pg_control={\"size\":8192,\"timestamp\":1571200000}\n"
|
||||
"pg_data/pg_xlog/0000000105DA69C000000000={\"size\":16777216,\"timestamp\":1571200002}\n"
|
||||
"pg_data/postgresql.conf={\"checksum\":\"e3db315c260e79211b7b52587123b7aa060f30ab\",\"size\":11"
|
||||
|
@ -1245,8 +1245,9 @@ testRun(void)
|
||||
.group = "test", .user = "test");
|
||||
|
||||
HRN_MANIFEST_FILE_ADD(
|
||||
manifestPrior, .name = MANIFEST_TARGET_PGDATA "/FILE2", .copy = true, .size = 4, .sizeRepo = 4, .timestamp = 1482182860,
|
||||
.reference = "20190101-010101F_20190202-010101D", .checksumSha1 = "ddddddddddbbbbbbbbbbccccccccccaaaaaaaaaa");
|
||||
manifestPrior, .name = MANIFEST_TARGET_PGDATA "/FILE2", .copy = true, .size = 6, .sizeOriginal = 4, .sizeRepo = 4,
|
||||
.timestamp = 1482182860, .reference = "20190101-010101F_20190202-010101D",
|
||||
.checksumSha1 = "ddddddddddbbbbbbbbbbccccccccccaaaaaaaaaa");
|
||||
|
||||
TEST_RESULT_VOID(
|
||||
manifestBuildIncr(manifest, manifestPrior, backupTypeIncr, STRDEF("000000040000000400000004")),
|
||||
@ -1273,7 +1274,8 @@ testRun(void)
|
||||
"\n"
|
||||
"[target:file]\n"
|
||||
"pg_data/FILE1={\"size\":6,\"timestamp\":1482182861}\n"
|
||||
"pg_data/FILE2={\"size\":6,\"timestamp\":1482182860}\n"
|
||||
"pg_data/FILE2={\"checksum\":\"ddddddddddbbbbbbbbbbccccccccccaaaaaaaaaa\""
|
||||
",\"reference\":\"20190101-010101F_20190202-010101D\",\"repo-size\":4,\"size\":6,\"timestamp\":1482182860}\n"
|
||||
TEST_MANIFEST_FILE_DEFAULT
|
||||
"\n"
|
||||
"[target:path]\n"
|
||||
@ -1576,7 +1578,7 @@ testRun(void)
|
||||
",\"rck\":\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"reference\":\"20190818-084502F_20190819-084506D\"" \
|
||||
",\"size\":4,\"timestamp\":1565282114}\n" \
|
||||
"pg_data/base/16384/17000={\"bi\":4,\"bni\":1,\"checksum\":\"e0101dd8ffb910c9c202ca35b5f828bcb9697bed\"" \
|
||||
",\"checksum-page\":false,\"checksum-page-error\":[1],\"repo-size\":4096,\"size\":8192" \
|
||||
",\"checksum-page\":false,\"checksum-page-error\":[1],\"repo-size\":4096,\"size\":8192,\"szo\":16384" \
|
||||
",\"timestamp\":1565282114}\n" \
|
||||
"pg_data/base/16384/PG_VERSION={\"bni\":1,\"bno\":1,\"checksum\":\"184473f470864e067ee3a22e64b47b0a1c356f29\"" \
|
||||
",\"group\":\"group2\",\"size\":4,\"timestamp\":1565282115,\"user\":false}\n" \
|
||||
|
Loading…
Reference in New Issue
Block a user