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

Allow page header checks to be skipped.

These checks cause false negatives for page checksum verification when the page is encrypted because pd_upper might end up as 0 in the encrypted data. This issue is rare but reproducible given a large enough cluster.

Make these checks optional, but leave them enabled by default.
This commit is contained in:
David Steele 2023-04-20 13:24:12 +03:00 committed by GitHub
parent 8240eb5da5
commit f5e6bc2698
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 173 additions and 82 deletions

View File

@ -17,6 +17,16 @@
<release date="XXXX-XX-XX" version="2.46dev" title="UNDER DEVELOPMENT">
<release-core-list>
<release-improvement-list>
<release-item>
<release-item-contributor-list>
<release-item-ideator id="david.christensen"/>
<release-item-contributor id="david.steele"/>
<release-item-reviewer id="david.christensen"/>
</release-item-contributor-list>
<p>Allow page header checks to be skipped.</p>
</release-item>
<release-item>
<commit subject="Move error modules to common/error directory."/>
<commit subject="Improve retry error messages."/>

View File

@ -1210,6 +1210,15 @@ option:
command-role:
main: {}
page-header-check:
section: global
type: boolean
default: true
command:
backup: {}
command-role:
main: {}
exclude:
section: global
type: list

View File

@ -1249,6 +1249,18 @@
<example>y</example>
</config-key>
<config-key id="page-header-check" name="Page Header Check">
<summary>Check PostgreSQL page headers.</summary>
<text>
<p>Enabled by default, this option adds page header checks.</p>
<p>Disabling this option should be avoided except when necessary, e.g. if pages are encrypted.</p>
</text>
<example>n</example>
</config-key>
<config-key id="resume" name="Resume">
<summary>Allow resume of failed backup.</summary>

View File

@ -1987,6 +1987,7 @@ backupJobCallback(void *const data, const unsigned int clientIdx)
pckWriteBoolP(param, !backupProcessFilePrimary(jobData->standbyExp, file.name));
pckWriteBinP(param, file.checksumSha1 != NULL ? BUF(file.checksumSha1, HASH_TYPE_SHA1_SIZE) : NULL);
pckWriteBoolP(param, file.checksumPage);
pckWriteBoolP(param, cfgOptionBool(cfgOptPageHeaderCheck));
// If block incremental then provide the location of the prior map when available
if (blockIncr)

View File

@ -208,7 +208,8 @@ backupFile(
ioFilterGroupAdd(
ioReadFilterGroup(storageReadIo(read)),
pageChecksumNew(
segmentNumber(file->pgFile), PG_SEGMENT_PAGE_DEFAULT, storagePathP(storagePg(), file->pgFile)));
segmentNumber(file->pgFile), PG_SEGMENT_PAGE_DEFAULT, file->pgFilePageHeaderCheck,
storagePathP(storagePg(), file->pgFile)));
}
// Compress filter

View File

@ -33,6 +33,7 @@ typedef struct BackupFile
bool pgFileCopyExactSize; // Copy only pg expected size
const Buffer *pgFileChecksum; // Expected pg file checksum
bool pgFileChecksumPage; // Validate page checksums?
bool pgFilePageHeaderCheck; // Validate page headers?
size_t blockIncrSize; // Perform block incremental on this file?
size_t blockIncrChecksumSize; // Block checksum size
uint64_t blockIncrSuperSize; // Size of the super block

View File

@ -21,6 +21,7 @@ typedef struct PageChecksum
{
unsigned int segmentPageTotal; // Total pages in a segment
unsigned int pageNoOffset; // Page number offset for subsequent segments
bool headerCheck; // Perform additional header checks?
const String *fileName; // Used to load the file to retry pages
unsigned char *pageBuffer; // Buffer to hold a page while verifying the checksum
@ -99,28 +100,29 @@ pageChecksumProcess(THIS_VOID, const Buffer *const input)
// Only validate page checksum if the page is complete
if (this->align || pageIdx < pageTotal - 1)
{
// Skip new pages indicated by pd_upper == 0
bool pdUpperValid = true;
bool pageValid = true;
if (pageHeader->pd_upper == 0)
// Skip new all-zero pages. To speed this up first check pd_upper when header check is enabled or pd_checksum when
// header check is disabled. The latter is required when the page is encrypted.
if ((this->headerCheck && pageHeader->pd_upper == 0) || (!this->headerCheck && pageHeader->pd_checksum == 0))
{
// Check that the entire page is zero in case pd_upper is corrupted
// Check that the entire page is zero
for (unsigned int pageIdx = 0; pageIdx < PG_PAGE_SIZE_DEFAULT / sizeof(size_t); pageIdx++)
{
if (((size_t *)pageHeader)[pageIdx] != 0)
{
pdUpperValid = false;
pageValid = false;
break;
}
}
// If the entire page is zero it is valid
if (pdUpperValid)
if (pageValid)
continue;
}
// Only validate the checksum if pd_upper is non-zero to avoid an assertion from pg_checksum_page()
if (pdUpperValid)
// Only validate the checksum if the page is valid
if (pageValid)
{
// Make a copy of the page since it will be modified by the page checksum function
memcpy(this->pageBuffer, pageHeader, PG_PAGE_SIZE_DEFAULT);
@ -223,11 +225,13 @@ pageChecksumResult(THIS_VOID)
/**********************************************************************************************************************************/
FN_EXTERN IoFilter *
pageChecksumNew(const unsigned int segmentNo, const unsigned int segmentPageTotal, const String *const fileName)
pageChecksumNew(
const unsigned int segmentNo, const unsigned int segmentPageTotal, const bool headerCheck, const String *const fileName)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(UINT, segmentNo);
FUNCTION_LOG_PARAM(UINT, segmentPageTotal);
FUNCTION_LOG_PARAM(BOOL, headerCheck);
FUNCTION_LOG_PARAM(STRING, fileName);
FUNCTION_LOG_END();
@ -237,6 +241,7 @@ pageChecksumNew(const unsigned int segmentNo, const unsigned int segmentPageTota
{
.segmentPageTotal = segmentPageTotal,
.pageNoOffset = segmentNo * segmentPageTotal,
.headerCheck = headerCheck,
.fileName = strDup(fileName),
.pageBuffer = bufPtr(bufNew(PG_PAGE_SIZE_DEFAULT)),
.valid = true,
@ -254,6 +259,7 @@ pageChecksumNew(const unsigned int segmentNo, const unsigned int segmentPageTota
pckWriteU32P(packWrite, segmentNo);
pckWriteU32P(packWrite, segmentPageTotal);
pckWriteBoolP(packWrite, headerCheck);
pckWriteStrP(packWrite, fileName);
pckWriteEndP(packWrite);
@ -276,9 +282,10 @@ pageChecksumNewPack(const Pack *const paramList)
PackRead *const paramListPack = pckReadNew(paramList);
const unsigned int segmentNo = pckReadU32P(paramListPack);
const unsigned int segmentPageTotal = pckReadU32P(paramListPack);
const bool headerCheck = pckReadBoolP(paramListPack);
const String *const fileName = pckReadStrP(paramListPack);
result = ioFilterMove(pageChecksumNew(segmentNo, segmentPageTotal, fileName), memContextPrior());
result = ioFilterMove(pageChecksumNew(segmentNo, segmentPageTotal, headerCheck, fileName), memContextPrior());
}
MEM_CONTEXT_TEMP_END();

View File

@ -16,7 +16,8 @@ Filter type constant
/***********************************************************************************************************************************
Constructors
***********************************************************************************************************************************/
FN_EXTERN IoFilter *pageChecksumNew(unsigned int segmentNo, unsigned int segmentPageTotal, const String *fileName);
FN_EXTERN IoFilter *pageChecksumNew(
unsigned int segmentNo, unsigned int segmentPageTotal, bool headerCheck, const String *fileName);
FN_EXTERN IoFilter *pageChecksumNewPack(const Pack *paramList);
#endif

View File

@ -49,6 +49,7 @@ backupFileProtocol(PackRead *const param, ProtocolServer *const server)
file.pgFileCopyExactSize = pckReadBoolP(param);
file.pgFileChecksum = pckReadBinP(param);
file.pgFileChecksumPage = pckReadBoolP(param);
file.pgFilePageHeaderCheck = pckReadBoolP(param);
file.blockIncrSize = (size_t)pckReadU64P(param);
if (file.blockIncrSize > 0)

View File

@ -96,6 +96,7 @@ Option constants
#define CFGOPT_NEUTRAL_UMASK "neutral-umask"
#define CFGOPT_ONLINE "online"
#define CFGOPT_OUTPUT "output"
#define CFGOPT_PAGE_HEADER_CHECK "page-header-check"
#define CFGOPT_PG "pg"
#define CFGOPT_PG_VERSION_FORCE "pg-version-force"
#define CFGOPT_PROCESS "process"
@ -134,7 +135,7 @@ Option constants
#define CFGOPT_TYPE "type"
#define CFGOPT_VERBOSE "verbose"
#define CFG_OPTION_TOTAL 166
#define CFG_OPTION_TOTAL 167
/***********************************************************************************************************************************
Option value constants
@ -416,6 +417,7 @@ typedef enum
cfgOptNeutralUmask,
cfgOptOnline,
cfgOptOutput,
cfgOptPageHeaderCheck,
cfgOptPg,
cfgOptPgDatabase,
cfgOptPgHost,

View File

@ -3225,6 +3225,32 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
), // opt/output
), // opt/output
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/page-header-check
( // opt/page-header-check
PARSE_RULE_OPTION_NAME("page-header-check"), // opt/page-header-check
PARSE_RULE_OPTION_TYPE(cfgOptTypeBoolean), // opt/page-header-check
PARSE_RULE_OPTION_NEGATE(true), // opt/page-header-check
PARSE_RULE_OPTION_RESET(true), // opt/page-header-check
PARSE_RULE_OPTION_REQUIRED(true), // opt/page-header-check
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/page-header-check
// opt/page-header-check
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/page-header-check
( // opt/page-header-check
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/page-header-check
), // opt/page-header-check
// opt/page-header-check
PARSE_RULE_OPTIONAL // opt/page-header-check
( // opt/page-header-check
PARSE_RULE_OPTIONAL_GROUP // opt/page-header-check
( // opt/page-header-check
PARSE_RULE_OPTIONAL_DEFAULT // opt/page-header-check
( // opt/page-header-check
PARSE_RULE_VAL_BOOL_TRUE, // opt/page-header-check
), // opt/page-header-check
), // opt/page-header-check
), // opt/page-header-check
), // opt/page-header-check
// -----------------------------------------------------------------------------------------------------------------------------
PARSE_RULE_OPTION // opt/pg
( // opt/pg
PARSE_RULE_OPTION_NAME("pg"), // opt/pg
@ -9875,6 +9901,7 @@ static const uint8_t optionResolveOrder[] =
cfgOptNeutralUmask, // opt-resolve-order
cfgOptOnline, // opt-resolve-order
cfgOptOutput, // opt-resolve-order
cfgOptPageHeaderCheck, // opt-resolve-order
cfgOptPg, // opt-resolve-order
cfgOptPgLocal, // opt-resolve-order
cfgOptPgPath, // opt-resolve-order

View File

@ -12,6 +12,7 @@ Modifications need to be made after copying:
1) Remove `#include "storage/bufpage.h"`.
2) Make pg_checksum_page() static.
3) Remove Assert(!PageIsNew(&cpage->phdr)).
***********************************************************************************************************************************/
/*-------------------------------------------------------------------------
@ -204,9 +205,6 @@ pg_checksum_page(char *page, BlockNumber blkno)
uint16 save_checksum;
uint32 checksum;
/* We only calculate the checksum for properly-initialized pages */
Assert(!PageIsNew(&cpage->phdr));
/*
* Save pd_checksum and temporarily set it to zero, so that the checksum
* calculation isn't affected by the old checksum stored on the page.

View File

@ -205,14 +205,6 @@ typedef struct PageHeaderData
// ---------------------------------------------------------------------------------------------------------------------------------
typedef PageHeaderData *PageHeader;
// PageIsNew macro
// ---------------------------------------------------------------------------------------------------------------------------------
/*
* PageIsNew
* returns true iff page has not been initialized (by PageInit)
*/
#define PageIsNew(page) (((PageHeader) (page))->pd_upper == 0)
/***********************************************************************************************************************************
Types from src/include/access/transam.h
***********************************************************************************************************************************/

View File

@ -626,7 +626,7 @@ testRun(void)
IoWrite *write = ioBufferWriteNew(bufferOut);
ioFilterGroupAdd(
ioWriteFilterGroup(write),
pageChecksumNewPack(ioFilterParamList(pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, STRDEF(BOGUS_STR)))));
pageChecksumNewPack(ioFilterParamList(pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, true, STRDEF(BOGUS_STR)))));
ioWriteOpen(write);
ioWrite(write, buffer);
TEST_ERROR(ioWrite(write, buffer), AssertError, "should not be possible to see two misaligned pages in a row");
@ -656,7 +656,8 @@ testRun(void)
write = ioBufferWriteNew(bufferOut);
ioFilterGroupAdd(
ioWriteFilterGroup(write), pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, storagePathP(storageTest, STRDEF("relation"))));
ioWriteFilterGroup(write),
pageChecksumNew(0, PG_SEGMENT_PAGE_DEFAULT, true, storagePathP(storageTest, STRDEF("relation"))));
ioWriteOpen(write);
ioWrite(write, buffer);
ioWriteClose(write);
@ -3294,9 +3295,27 @@ testRun(void)
hrnCfgArgRawZ(argList, cfgOptRepoRetentionFull, "1");
hrnCfgArgRawStrId(argList, cfgOptType, backupTypeIncr);
hrnCfgArgRawBool(argList, cfgOptDelta, true);
hrnCfgArgRawBool(argList, cfgOptPageHeaderCheck, false);
hrnCfgArgRawBool(argList, cfgOptRepoHardlink, true);
HRN_CFG_LOAD(cfgCmdBackup, argList);
// File with bad page checksum and header errors that will be ignored
Buffer *relation = bufNew(PG_PAGE_SIZE_DEFAULT * 4);
memset(bufPtr(relation), 0, bufSize(relation));
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x00)) = (PageHeaderData){.pd_upper = 0xFF};
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x01)) = (PageHeaderData){.pd_upper = 0x00};
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02)) = (PageHeaderData){.pd_upper = 0x00};
(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02))[PG_PAGE_SIZE_DEFAULT - 1] = 0xFF;
((PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02)))->pd_checksum = pgPageChecksum(
bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x02), 2);
*(PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x03)) = (PageHeaderData){.pd_upper = 0x00};
(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x03))[PG_PAGE_SIZE_DEFAULT - 1] = 0xEE;
((PageHeaderData *)(bufPtr(relation) + (PG_PAGE_SIZE_DEFAULT * 0x03)))->pd_checksum = 1;
bufUsedSet(relation, bufSize(relation));
HRN_STORAGE_PUT(storagePgWrite(), PG_PATH_BASE "/1/3", relation, .timeModified = backupTimeStart);
const char *rel1_3Sha1 = strZ(strNewEncode(encodingHex, cryptoHashOne(hashTypeSha1, relation)));
// Run backup. Make sure that the timeline selected converts to hexdecimal that can't be interpreted as decimal.
hrnBackupPqScriptP(PG_VERSION_11, backupTimeStart, .timeline = 0x2C, .walTotal = 2);
TEST_RESULT_VOID(hrnCmdBackup(), "backup");
@ -3308,6 +3327,8 @@ testRun(void)
"P00 INFO: check archive for segment 0000002C05DB8EB000000000\n"
"P00 WARN: a timeline switch has occurred since the 20191027-181320F backup, enabling delta checksum\n"
" HINT: this is normal after restoring from backup or promoting a standby.\n"
"P01 DETAIL: backup file " TEST_PATH "/pg1/base/1/3 (32KB, [PCT]) checksum [SHA1]\n"
"P00 WARN: invalid page checksums found in file " TEST_PATH "/pg1/base/1/3 at pages 0, 3\n"
"P01 DETAIL: backup file " TEST_PATH "/pg1/global/pg_control (8KB, [PCT]) checksum [SHA1]\n"
"P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/base/1/1 (8KB, [PCT]) checksum [SHA1]\n"
"P01 DETAIL: match file from prior backup " TEST_PATH "/pg1/postgresql.conf (11B, [PCT]) checksum [SHA1]\n"
@ -3322,65 +3343,73 @@ testRun(void)
"P00 DETAIL: wrote 'tablespace_map' file returned from backup stop function\n"
"P00 INFO: check archive for segment(s) 0000002C05DB8EB000000000:0000002C05DB8EB000000001\n"
"P00 INFO: new backup label = 20191027-181320F_20191030-014640I\n"
"P00 INFO: incr backup size = [SIZE], file total = 7");
"P00 INFO: incr backup size = [SIZE], file total = 8");
TEST_RESULT_STR_Z(
TEST_RESULT_STR(
testBackupValidateP(storageRepo(), STRDEF(STORAGE_REPO_BACKUP "/latest")),
". {link, d=20191027-181320F_20191030-014640I}\n"
"pg_data {path}\n"
"pg_data/PG_VERSION.gz {file, s=2}\n"
"pg_data/backup_label.gz {file, s=17}\n"
"pg_data/base {path}\n"
"pg_data/base/1 {path}\n"
"pg_data/base/1/1.gz {file, s=8192}\n"
"pg_data/global {path}\n"
"pg_data/global/pg_control.gz {file, s=8192}\n"
"pg_data/pg_tblspc {path}\n"
"pg_data/pg_tblspc/32768 {link, d=../../pg_tblspc/32768}\n"
"pg_data/pg_wal {path}\n"
"pg_data/postgresql.conf.gz {file, s=11}\n"
"pg_data/tablespace_map.gz {file, s=19}\n"
"pg_tblspc {path}\n"
"pg_tblspc/32768 {path}\n"
"pg_tblspc/32768/PG_11_201809051 {path}\n"
"pg_tblspc/32768/PG_11_201809051/1 {path}\n"
"pg_tblspc/32768/PG_11_201809051/1/5.gz {file, s=0}\n"
"--------\n"
"[backup:target]\n"
"pg_data={\"path\":\"" TEST_PATH "/pg1\",\"type\":\"path\"}\n"
"pg_tblspc/32768={\"path\":\"../../pg1-tblspc/32768\",\"tablespace-id\":\"32768\""
",\"tablespace-name\":\"tblspc32768\",\"type\":\"link\"}\n"
"\n"
"[target:file]\n"
"pg_data/PG_VERSION={\"checksum\":\"17ba0791499db908433b80f37c5fbc89b870084b\",\"reference\":\"20191027-181320F\""
",\"size\":2,\"timestamp\":1572200000}\n"
"pg_data/backup_label={\"checksum\":\"8e6f41ac87a7514be96260d65bacbffb11be77dc\",\"size\":17"
",\"timestamp\":1572400002}\n"
"pg_data/base/1/1={\"checksum\":\"0631457264ff7f8d5fb1edc2c0211992a67c73e6\",\"checksum-page\":true"
",\"reference\":\"20191027-181320F\",\"size\":8192,\"timestamp\":1572200000}\n"
"pg_data/global/pg_control={\"size\":8192,\"timestamp\":1572400000}\n"
"pg_data/postgresql.conf={\"checksum\":\"e3db315c260e79211b7b52587123b7aa060f30ab\""
",\"reference\":\"20191027-181320F\",\"size\":11,\"timestamp\":1570000000}\n"
"pg_data/tablespace_map={\"checksum\":\"87fe624d7976c2144e10afcb7a9a49b071f35e9c\",\"size\":19"
",\"timestamp\":1572400002}\n"
"pg_tblspc/32768/PG_11_201809051/1/5={\"checksum-page\":true,\"reference\":\"20191027-181320F\",\"size\":0"
",\"timestamp\":1572200000}\n"
"\n"
"[target:link]\n"
"pg_data/pg_tblspc/32768={\"destination\":\"../../pg1-tblspc/32768\"}\n"
"\n"
"[target:path]\n"
"pg_data={}\n"
"pg_data/base={}\n"
"pg_data/base/1={}\n"
"pg_data/global={}\n"
"pg_data/pg_tblspc={}\n"
"pg_data/pg_wal={}\n"
"pg_tblspc={}\n"
"pg_tblspc/32768={}\n"
"pg_tblspc/32768/PG_11_201809051={}\n"
"pg_tblspc/32768/PG_11_201809051/1={}\n",
strNewFmt(
". {link, d=20191027-181320F_20191030-014640I}\n"
"pg_data {path}\n"
"pg_data/PG_VERSION.gz {file, s=2}\n"
"pg_data/backup_label.gz {file, s=17}\n"
"pg_data/base {path}\n"
"pg_data/base/1 {path}\n"
"pg_data/base/1/1.gz {file, s=8192}\n"
"pg_data/base/1/3.gz {file, s=32768}\n"
"pg_data/global {path}\n"
"pg_data/global/pg_control.gz {file, s=8192}\n"
"pg_data/pg_tblspc {path}\n"
"pg_data/pg_tblspc/32768 {link, d=../../pg_tblspc/32768}\n"
"pg_data/pg_wal {path}\n"
"pg_data/postgresql.conf.gz {file, s=11}\n"
"pg_data/tablespace_map.gz {file, s=19}\n"
"pg_tblspc {path}\n"
"pg_tblspc/32768 {path}\n"
"pg_tblspc/32768/PG_11_201809051 {path}\n"
"pg_tblspc/32768/PG_11_201809051/1 {path}\n"
"pg_tblspc/32768/PG_11_201809051/1/5.gz {file, s=0}\n"
"--------\n"
"[backup:target]\n"
"pg_data={\"path\":\"" TEST_PATH "/pg1\",\"type\":\"path\"}\n"
"pg_tblspc/32768={\"path\":\"../../pg1-tblspc/32768\",\"tablespace-id\":\"32768\""
",\"tablespace-name\":\"tblspc32768\",\"type\":\"link\"}\n"
"\n"
"[target:file]\n"
"pg_data/PG_VERSION={\"checksum\":\"17ba0791499db908433b80f37c5fbc89b870084b\""
",\"reference\":\"20191027-181320F\",\"size\":2,\"timestamp\":1572200000}\n"
"pg_data/backup_label={\"checksum\":\"8e6f41ac87a7514be96260d65bacbffb11be77dc\",\"size\":17"
",\"timestamp\":1572400002}\n"
"pg_data/base/1/1={\"checksum\":\"0631457264ff7f8d5fb1edc2c0211992a67c73e6\",\"checksum-page\":true"
",\"reference\":\"20191027-181320F\",\"size\":8192,\"timestamp\":1572200000}\n"
"pg_data/base/1/3={\"checksum\":\"%s\",\"checksum-page\":false,\"checksum-page-error\":[0,3],\"size\":32768"
",\"timestamp\":1572400000}\n"
"pg_data/global/pg_control={\"size\":8192,\"timestamp\":1572400000}\n"
"pg_data/postgresql.conf={\"checksum\":\"e3db315c260e79211b7b52587123b7aa060f30ab\""
",\"reference\":\"20191027-181320F\",\"size\":11,\"timestamp\":1570000000}\n"
"pg_data/tablespace_map={\"checksum\":\"87fe624d7976c2144e10afcb7a9a49b071f35e9c\",\"size\":19"
",\"timestamp\":1572400002}\n"
"pg_tblspc/32768/PG_11_201809051/1/5={\"checksum-page\":true,\"reference\":\"20191027-181320F\",\"size\":0"
",\"timestamp\":1572200000}\n"
"\n"
"[target:link]\n"
"pg_data/pg_tblspc/32768={\"destination\":\"../../pg1-tblspc/32768\"}\n"
"\n"
"[target:path]\n"
"pg_data={}\n"
"pg_data/base={}\n"
"pg_data/base/1={}\n"
"pg_data/global={}\n"
"pg_data/pg_tblspc={}\n"
"pg_data/pg_wal={}\n"
"pg_tblspc={}\n"
"pg_tblspc/32768={}\n"
"pg_tblspc/32768/PG_11_201809051={}\n"
"pg_tblspc/32768/PG_11_201809051/1={}\n",
rel1_3Sha1),
"compare file list");
// Remove test files
HRN_STORAGE_REMOVE(storagePgWrite(), "base/1/3", .errorOnMissing = true);
}
// -------------------------------------------------------------------------------------------------------------------------