1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-11-28 20:04:05 +03:00

Retry reads of pg_control until checksum is valid.

On certain file systems (e.g. ext4) pg_control may appear torn if there is a concurrent write while reading the file. To prevent an invalid read, retry until the checksum matches the control data.

Special handling is required for the pg-version-force feature since the offset of the checksum is not known. In this case, scan from the default position to the end of the data looking for a checksum match. This is a bit imprecise, but better than nothing, and the chance of a random collision in the control data seems very remote considering the ratio of data size (< 512 bytes) to checksum size (4 bytes).

This was discovered and a possible solution proposed for PostgreSQL in [1]. The proposed solution may work for backup, but pgBackRest needs to be able to read pg_control reliably outside of backup. So no matter what fix is adopted for PostgreSQL, pgBackRest need retries. Further adjustment may be required as the PostgreSQL fix evolves.

[1] https://www.postgresql.org/message-id/20221123014224.xisi44byq3cf5psi%40awork3.anarazel.de
This commit is contained in:
David Steele
2023-09-10 09:47:49 -04:00
committed by GitHub
parent c1805134b3
commit f42d927d2d
21 changed files with 473 additions and 124 deletions

View File

@@ -74,6 +74,33 @@ testRun(void)
"unexpected control version = 1501 and catalog version = 202211111\n"
"HINT: is this version of PostgreSQL supported?");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("invalid CRC");
HRN_PG_CONTROL_OVERRIDE_CRC_PUT(storageTest, PG_VERSION_11, 0xFADEFADE);
TEST_ERROR_FMT(
pgControlFromFile(storageTest, NULL), ChecksumError,
"calculated pg_control checksum does not match expected value\n"
"HINT: calculated 0x%x but expected value is 0xfadefade\n"
"HINT: is pg_control corrupt?\n"
"HINT: does pg_control have a different layout than expected?",
(uint32_t)(TEST_BIG_ENDIAN() ? 0x4e206eeb : (TEST_64BIT() ? 0x4ad387b2 : 0x3ca3a1ec)));
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("invalid CRC on force control version");
HRN_PG_CONTROL_OVERRIDE_CRC_PUT(storageTest, PG_VERSION_13, 0xFADEFADE);
TEST_ERROR_FMT(
pgControlFromFile(storageTest, STRDEF(PG_VERSION_14_Z)), ChecksumError,
"calculated pg_control checksum does not match expected value\n"
"HINT: calculated 0x%x but expected value is 0x0\n"
"HINT: checksum values may be misleading due to forced version scan\n"
"HINT: is pg_control corrupt?\n"
"HINT: does pg_control have a different layout than expected?",
(uint32_t)(TEST_BIG_ENDIAN() ? 0x27dc2b85 : (TEST_64BIT() ? 0xa9264d94 : 0xb371302a)));
// -------------------------------------------------------------------------------------------------------------------------
HRN_PG_CONTROL_PUT(
storageTest, PG_VERSION_11, .systemId = 0xFACEFACE, .checkpoint = 0xEEFFEEFFAABBAABB, .timeline = 47,
@@ -133,6 +160,21 @@ testRun(void)
TEST_RESULT_UINT(info.catalogVersion, 202211111, "check catalog version");
TEST_RESULT_UINT(info.checkpoint, 0xAABBAABBEEFFEEFF, "check checkpoint");
TEST_RESULT_UINT(info.timeline, 88, "check timeline");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("CRC at greater offset on force control version");
// Start with an invalid crc but write a valid one at a greater offset to test the forced scan
Buffer *control = hrnPgControlToBuffer(0, 0xFADEFADE, (PgControl){.version = PG_VERSION_13, .systemId = 0xAAAA0AAAA});
size_t crcOffset = pgInterfaceVersion(PG_VERSION_13)->controlCrcOffset() + sizeof(uint32_t) * 4;
*((uint32_t *)(bufPtr(control) + crcOffset)) = crc32cOne(bufPtrConst(control), crcOffset);
HRN_STORAGE_PUT(storageTest, PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL, control);
TEST_ASSIGN(info, pgControlFromFile(storageTest, STRDEF(PG_VERSION_14_Z)), "get control info force v14");
TEST_RESULT_UINT(info.systemId, 0xAAAA0AAAA, "check system id");
TEST_RESULT_UINT(info.version, PG_VERSION_14, "check version");
TEST_RESULT_UINT(info.catalogVersion, 202007201, "check catalog version");
}
// *****************************************************************************************************************************