From 2f55219552f30c2cc5a97b15f855fa402d99a1fd Mon Sep 17 00:00:00 2001 From: Jeevan Chalke Date: Fri, 16 Aug 2019 14:10:16 +0530 Subject: [PATCH 2/4] Refactor code in basebackup.c - Refactor full backup code to the separate function. - Refactor checksum varifying logic to the separate function. --- src/backend/replication/basebackup.c | 308 ++++++++++++++++++++--------------- 1 file changed, 176 insertions(+), 132 deletions(-) diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index 74c954b..18e992c 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -75,6 +75,13 @@ static void SendXlogRecPtrResult(XLogRecPtr ptr, TimeLineID tli); static int compareWalFileNames(const ListCell *a, const ListCell *b); static void throttle(size_t increment); static bool is_checksummed_file(const char *fullpath, const char *filename); +static void verify_page_checksum(const char *readfilename, FILE *fp, char *buf, + off_t cnt, int blkindex, BlockNumber blkno, int segmentno, + int *checksum_failures); +static pgoff_t do_full_backup(const char *readfilename, + const char *tarfilename, FILE *fp, + struct stat *statbuf, int segmentno, + bool verify_checksum, int *checksum_failures); /* Was the backup currently in-progress initiated in recovery mode? */ static bool backup_started_in_recovery = false; @@ -1377,17 +1384,11 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf bool missing_ok, Oid dboid) { FILE *fp; - BlockNumber blkno = 0; - bool block_retry = false; char buf[TAR_SEND_SIZE]; - uint16 checksum; int checksum_failures = 0; off_t cnt; - int i; pgoff_t len = 0; - char *page; size_t pad; - PageHeader phdr; int segmentno = 0; char *segmentpath; bool verify_checksum = false; @@ -1402,8 +1403,6 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf errmsg("could not open file \"%s\": %m", readfilename))); } - _tarWriteHeader(tarfilename, NULL, statbuf, false); - if (!noverify_checksums && DataChecksumsEnabled()) { char *filename; @@ -1435,130 +1434,9 @@ sendFile(const char *readfilename, const char *tarfilename, struct stat *statbuf } } - while ((cnt = fread(buf, 1, Min(sizeof(buf), statbuf->st_size - len), fp)) > 0) - { - /* - * The checksums are verified at block level, so we iterate over the - * buffer in chunks of BLCKSZ, after making sure that - * TAR_SEND_SIZE/buf is divisible by BLCKSZ and we read a multiple of - * BLCKSZ bytes. - */ - Assert(TAR_SEND_SIZE % BLCKSZ == 0); - - if (verify_checksum && (cnt % BLCKSZ != 0)) - { - ereport(WARNING, - (errmsg("cannot verify checksum in file \"%s\", block " - "%d: read buffer size %d and page size %d " - "differ", - readfilename, blkno, (int) cnt, BLCKSZ))); - verify_checksum = false; - } - - if (verify_checksum) - { - for (i = 0; i < cnt / BLCKSZ; i++) - { - page = buf + BLCKSZ * i; - - /* - * Only check pages which have not been modified since the - * start of the base backup. Otherwise, they might have been - * written only halfway and the checksum would not be valid. - * However, replaying WAL would reinstate the correct page in - * this case. We also skip completely new pages, since they - * don't have a checksum yet. - */ - if (!PageIsNew(page) && PageGetLSN(page) < startptr) - { - checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE); - phdr = (PageHeader) page; - if (phdr->pd_checksum != checksum) - { - /* - * Retry the block on the first failure. It's - * possible that we read the first 4K page of the - * block just before postgres updated the entire block - * so it ends up looking torn to us. We only need to - * retry once because the LSN should be updated to - * something we can ignore on the next pass. If the - * error happens again then it is a true validation - * failure. - */ - if (block_retry == false) - { - /* Reread the failed block */ - if (fseek(fp, -(cnt - BLCKSZ * i), SEEK_CUR) == -1) - { - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not fseek in file \"%s\": %m", - readfilename))); - } - - if (fread(buf + BLCKSZ * i, 1, BLCKSZ, fp) != BLCKSZ) - { - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not reread block %d of file \"%s\": %m", - blkno, readfilename))); - } - - if (fseek(fp, cnt - BLCKSZ * i - BLCKSZ, SEEK_CUR) == -1) - { - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not fseek in file \"%s\": %m", - readfilename))); - } - - /* Set flag so we know a retry was attempted */ - block_retry = true; - - /* Reset loop to validate the block again */ - i--; - continue; - } - - checksum_failures++; - - if (checksum_failures <= 5) - ereport(WARNING, - (errmsg("checksum verification failed in " - "file \"%s\", block %d: calculated " - "%X but expected %X", - readfilename, blkno, checksum, - phdr->pd_checksum))); - if (checksum_failures == 5) - ereport(WARNING, - (errmsg("further checksum verification " - "failures in file \"%s\" will not " - "be reported", readfilename))); - } - } - block_retry = false; - blkno++; - } - } - - /* Send the chunk as a CopyData message */ - if (pq_putmessage('d', buf, cnt)) - ereport(ERROR, - (errmsg("base backup could not send data, aborting backup"))); - - len += cnt; - throttle(cnt); - - if (len >= statbuf->st_size) - { - /* - * Reached end of file. The file could be longer, if it was - * extended while we were sending it, but for a base backup we can - * ignore such extended data. It will be restored from WAL. - */ - break; - } - } + /* Perform full backup */ + len = do_full_backup(readfilename, tarfilename, fp, statbuf, segmentno, + verify_checksum, &checksum_failures); /* If the file was truncated while we were sending it, pad it with zeros */ if (len < statbuf->st_size) @@ -1731,3 +1609,169 @@ throttle(size_t increment) */ throttled_last = GetCurrentTimestamp(); } + +/* + * verify_page_checksum + * + * Verifies checksum for one page. + */ +static void +verify_page_checksum(const char *readfilename, FILE *fp, char *buf, + off_t cnt, int blkindex, BlockNumber blkno, int segmentno, + int *checksum_failures) +{ + char *page; + uint16 checksum; + bool block_retry = false; + + while (1) + { + page = buf + BLCKSZ * blkindex; + + /* + * Only check pages which have not been modified since the start of the + * base backup. Otherwise, they might have been written only halfway + * and the checksum would not be valid. However, replaying WAL would + * reinstate the correct page in this case. We also skip completely + * new pages, since they don't have a checksum yet. + */ + if (!PageIsNew(page) && PageGetLSN(page) < startptr) + { + PageHeader phdr; + + checksum = pg_checksum_page((char *) page, blkno + segmentno * RELSEG_SIZE); + phdr = (PageHeader) page; + if (phdr->pd_checksum != checksum) + { + /* + * Retry the block on the first failure. It's possible that we + * read the first 4K page of the block just before postgres + * updated the entire block so it ends up looking torn to us. + * We only need to retry once because the LSN should be updated + * to something we can ignore on the next pass. If the error + * happens again then it is a true validation failure. + */ + if (block_retry == false) + { + /* Reread the failed block */ + if (fseek(fp, -(cnt - BLCKSZ * blkindex), SEEK_CUR) == -1) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not fseek in file \"%s\": %m", + readfilename))); + } + + if (fread(buf + BLCKSZ * blkindex, 1, BLCKSZ, fp) != BLCKSZ) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not reread block %d of file \"%s\": %m", + blkno, readfilename))); + } + + if (fseek(fp, cnt - BLCKSZ * blkindex - BLCKSZ, SEEK_CUR) == -1) + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not fseek in file \"%s\": %m", + readfilename))); + } + + /* Set flag so we know a retry was attempted */ + block_retry = true; + + /* Re-validate the block again */ + continue; + } + + (*checksum_failures)++; + + if (*checksum_failures <= 5) + ereport(WARNING, + (errmsg("checksum verification failed in " + "file \"%s\", block %d: calculated " + "%X but expected %X", + readfilename, blkno, checksum, + phdr->pd_checksum))); + if (*checksum_failures == 5) + ereport(WARNING, + (errmsg("further checksum verification " + "failures in file \"%s\" will not " + "be reported", readfilename))); + } + } + + break; + } +} + +/* + * do_full_backup + * + * Perform full backup. + */ +static pgoff_t +do_full_backup(const char *readfilename, const char *tarfilename, FILE *fp, + struct stat *statbuf, int segmentno, bool verify_checksum, + int *checksum_failures) +{ + char buf[TAR_SEND_SIZE]; + off_t cnt; + pgoff_t len = 0; + BlockNumber blkno = 0; + int i; + + _tarWriteHeader(tarfilename, NULL, statbuf, false); + + while ((cnt = fread(buf, 1, Min(sizeof(buf), statbuf->st_size - len), fp)) > 0) + { + /* + * The checksums are verified at block level, so we iterate over the + * buffer in chunks of BLCKSZ, after making sure that + * TAR_SEND_SIZE/buf is divisible by BLCKSZ and we read a multiple of + * BLCKSZ bytes. + */ + Assert(TAR_SEND_SIZE % BLCKSZ == 0); + + if (verify_checksum && (cnt % BLCKSZ != 0)) + { + ereport(WARNING, + (errmsg("cannot verify checksum in file \"%s\", block " + "%d: read buffer size %d and page size %d " + "differ", + readfilename, blkno, (int) cnt, BLCKSZ))); + verify_checksum = false; + } + + if (verify_checksum) + { + for (i = 0; i < cnt / BLCKSZ; i++) + { + verify_page_checksum(readfilename, fp, buf, cnt, i, blkno, + segmentno, checksum_failures); + blkno++; + } + } + + /* Send the chunk as a CopyData message */ + if (pq_putmessage('d', buf, cnt)) + ereport(ERROR, + (errmsg("base backup could not send data, aborting backup"))); + + len += cnt; + throttle(cnt); + + if (len >= statbuf->st_size) + { + /* + * Reached end of file. The file could be longer, if it was + * extended while we were sending it, but for a base backup we can + * ignore such extended data. It will be restored from WAL. + */ + break; + } + } + + return len; +} -- 1.8.3.1