openGauss-server/src/gausskernel/storage/access/transam/cbmparsexlog.cpp

3579 lines
143 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* cbmparsexlog.cpp
* Functions for parsing Write-Ahead-Log and write cbm files
*
* IDENTIFICATION
* src/gausskernel/storage/access/transam/cbmparsexlog.cpp
*
* -------------------------------------------------------------------------
*/
#include <sys/time.h>
#include "postgres.h"
#include "knl/knl_variable.h"
#include "access/cbmparsexlog.h"
#include "access/ustore/undo/knl_uundoapi.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xlog_internal.h"
#include "access/xlogproc.h"
#include "access/xlogreader.h"
#include "access/visibilitymap.h"
#include "catalog/pg_control.h"
#include "catalog/storage_xlog.h"
#include "commands/dbcommands.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "port.h"
#include "postmaster/cbmwriter.h"
#include "storage/copydir.h"
#include "storage/smgr/fd.h"
#include "storage/freespace.h"
#include "storage/lock/lwlock.h"
#include "storage/proc.h"
#include "utils/memutils.h"
#include "utils/relmapper.h"
/* we can put the following globals into XlogCbmSys */
static XLogRecPtr tmpTargetLSN = InvalidXLogRecPtr;
static XLogRecPtr latestCompTargetLSN = InvalidXLogRecPtr;
static bool tmpLSNIsRecEnd = true;
/* File name stem for bitmap files. */
static const char *const cbmFileNameStem = "pg_xlog_";
static const int cbmFileNameStemLen = strlen(cbmFileNameStem);
static const char *const mergedCbmFileNameStem = "pg_merged_xlog_";
/* File name template for bitmap files. The 1st format tag is a directory
name, the 2nd tag is the stem, the 3rd tag is a file sequence number, the 4th
tag is the start LSN for the file, the 5th tag is the end LSN of the file. */
static const char *const bmp_file_name_template = "%s%s%lu_%08X%08X_%08X%08X.cbm";
static const char *const merged_bmp_file_name_template = "%s%s%08X%08X_%08X%08X_%ld-%d.cbm";
static void CBMFileHomeInitialize(void);
static void ResetXlogCbmSys(void);
static bool IsCBMFile(const char *fileName, uint64 *seqNum, XLogRecPtr *startLSN, XLogRecPtr *endLSN);
static void ValidateCBMFile(const char *filename, XLogRecPtr *trackedLSN, uint64 *lastfileSize, bool truncErrPage);
static bool ReadCBMPage(BitmapFile *cbmFile, char *page, bool *checksum_ok);
static pg_crc32c CBMPageCalcCRC(const char *page);
static XLogRecPtr InitCBMTrackStartLSN(bool startupXlog, bool fromScratch, XLogRecPtr lastTrackedLSN,
XLogRecPtr startupCPRedo);
static void InitCBMStartFileAndTrack(bool fromScratch, XLogRecPtr trackStartLSN, const char *lastfileName,
XLogRecPtr lastfileStartLSN, XLogRecPtr lastfileTrackedLSN, uint64 lastfileSize,
uint64 lastfileSeqNum);
static void SetCBMFileName(char *cbmFileNameBuf, uint64 seqNum, XLogRecPtr startLSN, XLogRecPtr endLSN);
static void SetNewCBMFileName(XLogRecPtr startLSN);
static void StartNextCBMFile(XLogRecPtr startLSN);
static void StartExistCBMFile(uint64 lastfileSize);
static HTAB *CBMPageHashInitialize(MemoryContext memoryContext);
static bool ParseXlogIntoCBMPages(TimeLineID timeLine, bool isRecEnd);
static int CBMXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetRecPtr,
char *readBuf, TimeLineID *pageTLI, char* xlog_path = NULL);
static void TrackChangeBlock(XLogReaderState *record);
static void TrackRelPageModification(XLogReaderState *record);
static void TrackCuBlockModification(XLogReaderState *record);
static void TrackRelStorageDrop(XLogReaderState *record);
static void TrackRelStorageCreate(XLogReaderState *record);
static void TrackRelStorageTruncate(XLogReaderState *record);
static void TrackVMPageModification(XLogReaderState *record);
static void TrackDbStorageChange(XLogReaderState *record);
static void TrackTblspcStorageChange(XLogReaderState *record);
static void TrackRelmapChange(XLogReaderState *record);
static void TrackSegmentPageChange(XLogReaderState *record);
static void TrackUheapInsert(XLogReaderState *record);
static void TrackUheapDelete(XLogReaderState *record);
static void TrackUheapUpdate(XLogReaderState *record);
static void TrackUheapMultiInsert(XLogReaderState *record);
static void TrackUndoPageModification(XLogReaderState *record);
static void TrackUndoStorageModification(XLogReaderState *record);
static void TrackTransSlotModification(XLogReaderState *record);
static void RegisterBlockChange(const RelFileNode &rNode, ForkNumber forkNum, BlockNumber blkNo);
static void RegisterBlockChangeExtended(const RelFileNode &rNode, ForkNumber forkNum, BlockNumber blkNo, uint8 pageType,
BlockNumber truncBlkNo);
static void CBMPageEtySetBitmap(CbmHashEntry *cbmPageEntry, BlockNumber blkNo, uint8 pageType, BlockNumber truncBlkNo);
static void CBMPageSetBitmap(char *page, BlockNumber blkNo, uint8 pageType, BlockNumber truncBlkNo);
static void CreateNewCBMPageAndInsert(CbmHashEntry *cbmPageEntry, BlockNumber blkNo, uint8 pageType,
BlockNumber truncBlkNo);
static void CreateDummyCBMEtyPageAndInsert(void);
static void FlushCBMPagesToDisk(XlogBitmap *xlogCbmSys, bool isCBMWriter);
static int CBMPageSeqCmp(const void *a, const void *b);
static void FlushOneCBMPage(const char *page, XlogBitmap *xlogCbmSys);
static void RotateCBMFile(void);
static void RemoveAllCBMFiles(int elevel);
static void PrintCBMHashTab(HTAB *cbmPageHash);
static void CBMGetMergedHash(XLogRecPtr startLSN, XLogRecPtr endLSN, HTAB *cbmPageHash, XLogRecPtr *mergeStartLSN,
XLogRecPtr *mergeEndLSN);
static CbmFileName **GetAndValidateCBMFileArray(XLogRecPtr startLSN, XLogRecPtr endLSN, int *fileNum);
static CbmFileName **GetCBMFileArray(XLogRecPtr startLSN, XLogRecPtr endLSN, int *fileNum, bool missingOk);
static CbmFileName **SortCBMFilesList(Dllist *cbmSegPageList, int cbmFileNum);
static int CBMFileNameSeqCmp(const void *a, const void *b);
static void PrintCBMFileArray(CbmFileName **cbmFileNameArray, int cbmFileNum, XLogRecPtr startLSN, XLogRecPtr endLSN);
static void ValidateCBMFileArray(CbmFileName **cbmFileNameArray, int cbmFileNum, XLogRecPtr startLSN,
XLogRecPtr endLSN);
static void MergeCBMFileArrayIntoHash(CbmFileName **cbmFileNameArray, int cbmFileNum, XLogRecPtr startLSN,
XLogRecPtr endLSN, HTAB *cbmPageHash, XLogRecPtr *mergeStartLSN,
XLogRecPtr *mergeEndLSN);
static void CBMPageIterBegin(cbmPageIterator *pageIteratorPtr, CbmFileName *cbmFile);
static bool CBMPageIterNext(cbmPageIterator *pageIteratorPtr, CbmFileName *cbmFile);
static void CBMPageIterEnd(cbmPageIterator *pageIteratorPtr, CbmFileName *cbmFile);
static void MergeCBMPageIntoHash(const char *page, HTAB *cbmPageHash);
static void ValidateCBMPageHeader(cbmpageheader *cbmPageHeader);
static void CBMHashRemove(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter);
static void CBMHashRemoveDb(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter);
static void CBMHashRemoveTblspc(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter);
static void CBMPageEtyRemove(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter, bool removeEntry);
static void CBMPageEtyRemoveRestFork(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter);
static void CBMPageEtyTruncate(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, BlockNumber truncBlkNo,
bool isCBMWriter);
static void CBMPageEtyTruncateBefore(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, BlockNumber truncBlkNo,
bool isCBMWriter);
static void CBMPageEtyMergePage(CbmHashEntry *cbmPageEntry, const char *page);
static void CBMPageMergeBitmap(char *cbmHashPage, const char *newPage);
static void CopyCBMPageAndInsert(CbmHashEntry *cbmPageEntry, const char *page);
static uint64 GetCBMHashTotalPageNum(HTAB *cbmPageHash);
static FILE *MergedXlogCBMSysInitFile(XlogBitmap *mergedXlogCbmSys);
static void FreeCBMFileArray(CbmFileName **cbmFileNameArray, int cbmFileNum);
static CBMArrayEntry *ConvertCBMHashIntoArray(HTAB *cbmPageHash, long *arrayLength, bool destroyHashEntry);
static void MergeOneCBMPageIntoArrayEntry(char *page, CBMArrayEntry *cbmArrayEntry);
static bool CBMBitmap_next(CBMBitmapIterator *iter, BlockNumber *blkno);
static void InitCBMArrayEntryBlockArray(CBMArrayEntry *cbmArrayEntry);
static void CBMArrayEntryRigsterBlock(CBMArrayEntry *cbmArrayEntry, BlockNumber blkNo);
static void UnlinkCBMFile(const char *cbmFileName);
static XLogRecPtr GetTmpTargetLSN(bool *isRecEnd);
static void SetTmpTargetLSN(XLogRecPtr targetLSN, bool isRecEnd);
static XLogRecPtr GetLatestCompTargetLSN(void);
static void SetLatestCompTargetLSN(XLogRecPtr targetLSN);
static Dlelem *FindPageElemFromCbmSegList(CbmSegPageList *cbmSegPageList, BlockNumber pageFirstBlock);
static Dlelem *FindCbmSegPageListemFromEntry(CbmHashEntry *cbmPageEntry, const int list_num);
static Dlelem *InsertCbmSegPageListToEntry(CbmHashEntry *cbmPageEntry, const int list_num);
static void InsertCbmPageElemToEntry(CbmHashEntry *cbmPageEntry, Dlelem *elt, BlockNumber pageFirstBlock);
static bool ForeachEntryForPage(CbmHashEntry *cbmPageEntry, Dlelem **eltCbmSegPageList, Dlelem **eltPagelist);
static void DestoryCbmHashEntry(CbmHashEntry *cbmPageEntry, bool reuse, XlogBitmap *xlogCbmSys, bool changeTotalNum,
StringInfo log);
static Dlelem *FindPageElemFromEntry(CbmHashEntry *cbmPageEntry, BlockNumber pageFirstBlock);
static bool checkUserRequstAndRotateCbm();
extern void *palloc_extended(Size size, int flags);
extern void InitXlogCbmSys(void)
{
CBMFileHomeInitialize();
t_thrd.cbm_cxt.XlogCbmSys->out.fd = -1;
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd = -1;
ResetXlogCbmSys();
t_thrd.cbm_cxt.XlogCbmSys->xlogParseFailed = false;
t_thrd.cbm_cxt.XlogCbmSys->firstCPCreated = false;
}
static void CBMFileHomeInitialize(void)
{
Assert(t_thrd.proc_cxt.DataDir);
int rc = 0;
int pathlen = strlen(t_thrd.proc_cxt.DataDir) + 8;
if (pathlen >= MAXPGPATH)
ereport(FATAL, (errmsg("Length of CBM file home path exceeds MAXPGPATH!")));
pathlen = strlen(t_thrd.proc_cxt.DataDir);
rc = strncpy_s(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, MAXPGPATH, t_thrd.proc_cxt.DataDir, pathlen);
securec_check(rc, "\0", "\0");
if (t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome[pathlen - 1] != '/') {
t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome[pathlen] = '/';
t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome[pathlen + 1] = '\0';
}
rc = strncat_s(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, MAXPGPATH, CBMDIR, 7);
securec_check(rc, "\0", "\0");
pathlen = strlen(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome) + MAX_CBM_FILENAME_LENGTH;
if (pathlen >= MAXPGPATH)
ereport(FATAL, (errmsg("Length of absolute CBM file path would exceed MAXPGPATH!")));
}
static void ResetXlogCbmSys(void)
{
int rc = 0;
if (t_thrd.cbm_cxt.XlogCbmSys->out.fd >= 0) {
if (pg_fsync(t_thrd.cbm_cxt.XlogCbmSys->out.fd) != 0)
ereport(WARNING, (errcode_for_file_access(), errmsg("fsync pending CBM file \"%s\" failed during reset",
t_thrd.cbm_cxt.XlogCbmSys->out.name)));
if (close(t_thrd.cbm_cxt.XlogCbmSys->out.fd) != 0)
ereport(WARNING, (errcode_for_file_access(), errmsg("close pending CBM file \"%s\" failed during reset",
t_thrd.cbm_cxt.XlogCbmSys->out.name)));
}
rc = memset_s(t_thrd.cbm_cxt.XlogCbmSys->out.name, MAXPGPATH, 0, MAXPGPATH);
securec_check(rc, "\0", "\0");
t_thrd.cbm_cxt.XlogCbmSys->out.fd = -1;
t_thrd.cbm_cxt.XlogCbmSys->out.size = 0;
t_thrd.cbm_cxt.XlogCbmSys->out.offset = (off_t)0;
t_thrd.cbm_cxt.XlogCbmSys->outSeqNum = 0;
t_thrd.cbm_cxt.XlogCbmSys->startLSN = 0;
t_thrd.cbm_cxt.XlogCbmSys->endLSN = 0;
t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash = NULL;
DLInitList(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList);
t_thrd.cbm_cxt.XlogCbmSys->totalPageNum = 0;
if (t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd >= 0) {
if (close(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd) != 0)
ereport(WARNING, (errcode_for_file_access(), errmsg("could not close file \"%s\" during reset",
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath)));
}
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd = -1;
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.logSegNo = 0;
rc = memset_s(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath, MAXPGPATH, 0, MAXPGPATH);
securec_check(rc, "\0", "\0");
}
extern void CBMTrackInit(bool startupXlog, XLogRecPtr startupCPRedo)
{
bool fromScratch = false;
DIR *cbmdir = NULL;
struct dirent *cbmde = NULL;
struct stat st;
char lastfileName[MAXPGPATH] = {0};
uint64 lastfileSize = 0;
uint64 lastfileSeqNum = 0;
XLogRecPtr lastfileStartLSN = InvalidXLogRecPtr;
XLogRecPtr lastfileEndLSN = InvalidXLogRecPtr;
XLogRecPtr lastfileTrackedLSN = InvalidXLogRecPtr;
XLogRecPtr trackStartLSN = InvalidXLogRecPtr;
int rc;
cbmdir = AllocateDir(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome);
if (cbmdir == NULL) {
ereport(WARNING, (errcode_for_file_access(), errmsg("could not open CBM file directory \"%s\": %m",
t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome)));
ereport(LOG, (errmsg("This maybe the first time CBM tracking is enabled after "
"db installation or CBM track reset.")));
/* Remove any broken directory with same name. */
if (stat(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, &st) == 0 && S_ISDIR(st.st_mode))
(void)rmtree(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, true, true);
/* Create and fsync CBM file directory. */
if (mkdir(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, S_IRWXU) < 0)
ereport(ERROR, (errcode_for_file_access(),
errmsg("could not create directory \"%s\": %m", t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome)));
fsync_fname(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, true);
fromScratch = true;
} else {
uint64 fileSeqNum = 0;
XLogRecPtr fileStartLSN = InvalidXLogRecPtr;
XLogRecPtr fileEndLSN = InvalidXLogRecPtr;
while ((cbmde = ReadDir(cbmdir, t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome)) != NULL) {
/* Ignore files that are not CBM files. */
if (!IsCBMFile(cbmde->d_name, &fileSeqNum, &fileStartLSN, &fileEndLSN))
continue;
Assert(!XLogRecPtrIsInvalid(fileStartLSN));
Assert(XLByteLT(fileStartLSN, fileEndLSN) || XLogRecPtrIsInvalid(fileEndLSN));
/* Find the last CBM file. */
if (fileSeqNum > lastfileSeqNum) {
lastfileSeqNum = fileSeqNum;
lastfileStartLSN = fileStartLSN;
lastfileEndLSN = fileEndLSN;
rc = strncpy_s(lastfileName, MAXPGPATH, cbmde->d_name, strlen(cbmde->d_name));
securec_check(rc, "\0", "\0");
}
}
(void)FreeDir(cbmdir);
if (lastfileSeqNum == 0)
fromScratch = true;
else
ereport(LOG, (errmsg("last CBM file name \"%s\", seqnum %lu, start LSN %08X/%08X, "
"end LSN %08X/%08X",
lastfileName, lastfileSeqNum, (uint32)(lastfileStartLSN >> 32),
(uint32)lastfileStartLSN, (uint32)(lastfileEndLSN >> 32), (uint32)lastfileEndLSN)));
}
if (!fromScratch) {
ValidateCBMFile(lastfileName, &lastfileTrackedLSN, &lastfileSize, true);
ereport(LOG, (errmsg("last CBM file name \"%s\", lastfileSize %lu, tracked LSN %08X/%08X", lastfileName,
lastfileSize, (uint32)(lastfileTrackedLSN >> 32), (uint32)lastfileTrackedLSN)));
if (XLogRecPtrIsInvalid(lastfileTrackedLSN))
lastfileTrackedLSN = lastfileStartLSN;
else
Assert(XLByteLT(lastfileStartLSN, lastfileTrackedLSN));
}
trackStartLSN = InitCBMTrackStartLSN(startupXlog, fromScratch, lastfileTrackedLSN, startupCPRedo);
InitCBMStartFileAndTrack(fromScratch, trackStartLSN, lastfileName, lastfileStartLSN, lastfileTrackedLSN,
lastfileSize, lastfileSeqNum);
}
static bool IsCBMFile(const char *fileName, uint64 *seqNum, XLogRecPtr *startLSN, XLogRecPtr *endLSN)
{
char stem[MAXPGPATH];
Assert(strlen(fileName) < MAXPGPATH);
int rc = 0;
uint32 startLSN_hi = 0;
uint32 startLSN_lo = 0;
uint32 endLSN_hi = 0;
uint32 endLSN_lo = 0;
rc = sscanf_s(fileName, "%[a-z_]%lu_%8X%8X_%8X%8X.cbm", stem, MAXPGPATH - 1, seqNum, &startLSN_hi, &startLSN_lo,
&endLSN_hi, &endLSN_lo);
*startLSN = (((uint64)startLSN_hi) << 32) | startLSN_lo;
*endLSN = (((uint64)endLSN_hi) << 32) | endLSN_lo;
ereport(DEBUG1, (errmsg("file name \"%s\", rc %d, stem \"%s\", seqnum %lu, "
"startLSN: %08X/%08X, endLSN: %08X/%08X",
fileName, rc, stem, *seqNum, (uint32)(*startLSN >> 32), (uint32)(*startLSN),
(uint32)(*endLSN >> 32), (uint32)(*endLSN))));
return ((rc == 6) && (!strncmp(stem, cbmFileNameStem, cbmFileNameStemLen)));
}
static void ValidateCBMFile(const char *filename, XLogRecPtr *trackedLSN, uint64 *lastfileSize, bool truncErrPage)
{
struct stat st;
char *page = NULL;
char filePath[MAXPGPATH];
bool is_last_page = false;
bool checksum_ok = false;
off_t read_offset = 0;
BitmapFile cbmFile;
int rc;
rc = snprintf_s(filePath, MAXPGPATH, MAXPGPATH - 1, "%s%s", t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, filename);
securec_check_ss(rc, "\0", "\0");
if (stat(filePath, &st) != 0) {
*trackedLSN = InvalidXLogRecPtr;
*lastfileSize = 0;
ereport(LOG, (errmsg("could not stat CBM file \"%s\" ", filePath)));
return;
}
if (st.st_size % (off_t)CBMPAGESIZE)
ereport(WARNING, (errmsg("size(%ld) of CBM file \"%s\" is not a multiple of CBMPAGESIZE, "
"which may imply file corruption.",
st.st_size, filePath)));
cbmFile.fd = BasicOpenFile(filePath, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
if (cbmFile.fd < 0) {
*trackedLSN = InvalidXLogRecPtr;
*lastfileSize = 0;
ereport(LOG,
(errcode_for_file_access(), errmsg("could not open CBM file \"%s\" while invalidation: %m", filePath)));
return;
}
cbmFile.size = read_offset = st.st_size - st.st_size % (off_t)CBMPAGESIZE;
rc = strncpy_s(cbmFile.name, MAXPGPATH, filePath, strlen(filePath));
securec_check(rc, "\0", "\0");
page = (char *)palloc_extended(CBMPAGESIZE, MCXT_ALLOC_NO_OOM);
if (page == NULL) {
if (close(cbmFile.fd))
ereport(WARNING, (errcode_for_file_access(), errmsg("could not close CBM file \"%s\": %m", filePath)));
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("memory is temporarily unavailable while allocate "
"page read buffer during validate CBM file")));
}
while (read_offset > (off_t)0 && (!is_last_page || !checksum_ok)) {
read_offset -= (off_t)CBMPAGESIZE;
cbmFile.offset = read_offset;
if (!ReadCBMPage(&cbmFile, page, &checksum_ok)) {
checksum_ok = false;
break;
}
if (checksum_ok)
is_last_page = ((cbmpageheader *)page)->isLastBlock;
else
ereport(WARNING, (errmsg("Corruption detected in CBM file \"%s\", page offset %ld", filePath,
cbmFile.offset - (off_t)CBMPAGESIZE)));
}
*trackedLSN = (checksum_ok && is_last_page) ? ((cbmpageheader *)page)->pageEndLsn : InvalidXLogRecPtr;
if (XLogRecPtrIsInvalid(*trackedLSN))
*lastfileSize = (off_t)0;
else {
if (cbmFile.offset < st.st_size && truncErrPage) {
if (ftruncate(cbmFile.fd, cbmFile.offset))
ereport(ERROR, (errcode_for_file_access(),
errmsg("Failed to truncate CBM file \"%s\" to length %ld", filePath, cbmFile.offset)));
}
*lastfileSize = cbmFile.offset;
}
if (close(cbmFile.fd))
ereport(WARNING, (errcode_for_file_access(), errmsg("could not close CBM file \"%s\": %m", filePath)));
pfree(page);
}
static bool ReadCBMPage(BitmapFile *cbmFile, char *page, bool *checksum_ok)
{
pg_crc32c checksum;
pg_crc32c actual_checksum;
ssize_t readLen;
Assert(cbmFile->size >= CBMPAGESIZE);
Assert(cbmFile->offset <= (off_t)(cbmFile->size - CBMPAGESIZE));
Assert(cbmFile->offset % (off_t)CBMPAGESIZE == (off_t)0);
PGSTAT_INIT_TIME_RECORD();
PGSTAT_START_TIME_RECORD();
readLen = pread(cbmFile->fd, page, (size_t)CBMPAGESIZE, cbmFile->offset);
PGSTAT_END_TIME_RECORD(DATA_IO_TIME);
if (readLen != (ssize_t)CBMPAGESIZE) {
ereport(WARNING, (errmsg("failed reading CBM file \"%s\", page offset %ld", cbmFile->name, cbmFile->offset)));
return false;
}
cbmFile->offset += (off_t)CBMPAGESIZE;
Assert(cbmFile->offset <= (off_t)cbmFile->size);
checksum = ((cbmpageheader *)page)->pageCrc;
actual_checksum = CBMPageCalcCRC(page);
*checksum_ok = (checksum == actual_checksum);
return true;
}
static pg_crc32c CBMPageCalcCRC(const char *page)
{
pg_crc32c crc;
INIT_CRC32C(crc);
COMP_CRC32C(crc, page + offsetof(CbmPageHeader, isLastBlock), CBMPAGESIZE - offsetof(CbmPageHeader, isLastBlock));
FIN_CRC32C(crc);
return crc;
}
static XLogRecPtr InitCBMTrackStartLSN(bool startupXlog, bool fromScratch, XLogRecPtr lastTrackedLSN,
XLogRecPtr startupCPRedo)
{
XLogRecPtr trackStartLSN = InvalidXLogRecPtr;
if (startupXlog) {
Assert(!t_thrd.cbm_cxt.XlogCbmSys->xlogParseFailed);
if (fromScratch && u_sess->attr.attr_storage.enable_cbm_tracking) {
Assert(XLogRecPtrIsInvalid(lastTrackedLSN));
Assert(!XLogRecPtrIsInvalid(startupCPRedo));
trackStartLSN = startupCPRedo;
} else
trackStartLSN = lastTrackedLSN;
SetCBMTrackedLSN(trackStartLSN);
} else {
(void)LWLockAcquire(ControlFileLock, LW_SHARED);
if (fromScratch) {
Assert(XLogRecPtrIsInvalid(lastTrackedLSN));
Assert(XLogRecPtrIsInvalid(startupCPRedo));
trackStartLSN = t_thrd.shemem_ptr_cxt.ControlFile->checkPointCopy.redo;
} else {
trackStartLSN = (t_thrd.cbm_cxt.XlogCbmSys->xlogParseFailed &&
XLByteLT(lastTrackedLSN, t_thrd.shemem_ptr_cxt.ControlFile->checkPointCopy.redo))
? t_thrd.shemem_ptr_cxt.ControlFile->checkPointCopy.redo
: lastTrackedLSN;
if (XLByteLT(lastTrackedLSN, trackStartLSN))
ereport(WARNING, (errmsg("Last tracked LSN %08X/%08X is smaller than CBM track start "
"LSN %08X/%08X. This may be caused by CBM file or xlog file corruption",
(uint32)(lastTrackedLSN >> 32), (uint32)lastTrackedLSN,
(uint32)(trackStartLSN >> 32), (uint32)trackStartLSN)));
}
SetCBMTrackedLSN(trackStartLSN);
LWLockRelease(ControlFileLock);
}
Assert(!XLogRecPtrIsInvalid(trackStartLSN));
return trackStartLSN;
}
static void InitCBMStartFileAndTrack(bool fromScratch, XLogRecPtr trackStartLSN, const char *lastfileName,
XLogRecPtr lastfileStartLSN, XLogRecPtr lastfileTrackedLSN, uint64 lastfileSize,
uint64 lastfileSeqNum)
{
int rc;
volatile bool switchFile = false;
char lastfilePath[MAXPGPATH];
char filePath[MAXPGPATH];
if (lastfileSize >= MAXCBMFILESIZE || (lastfileSize != 0 && XLByteLT(lastfileTrackedLSN, trackStartLSN)))
switchFile = true;
t_thrd.cbm_cxt.XlogCbmSys->outSeqNum = fromScratch ? 1 : (switchFile ? (lastfileSeqNum + 1) : lastfileSeqNum);
t_thrd.cbm_cxt.XlogCbmSys->startLSN = t_thrd.cbm_cxt.XlogCbmSys->endLSN = trackStartLSN;
/*
* Initialize lastest completed cbm lsn to newly-decided start lsn, so that any later cbm track
* request less than this lsn could be ignored silently.
* We do not use lock to protest latestCompTargetLSN, though startup thread also calls CBMTrackInit,
* because at that point, pm state is still at pm_startup and cbm writer thread has not been started.
*/
SetLatestCompTargetLSN(t_thrd.cbm_cxt.XlogCbmSys->startLSN);
if (!fromScratch) {
rc = snprintf_s(lastfilePath, MAXPGPATH, MAXPGPATH - 1, "%s%s", t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome,
lastfileName);
securec_check_ss(rc, "\0", "\0");
if (switchFile) {
Assert(XLByteLT(lastfileStartLSN, lastfileTrackedLSN));
SetCBMFileName(filePath, lastfileSeqNum, lastfileStartLSN, lastfileTrackedLSN);
if (durable_rename(lastfilePath, filePath, ERROR))
ereport(ERROR, (errcode_for_file_access(),
errmsg("could not rename file \"%s\" to \"%s\": %m", lastfilePath, filePath)));
SetCBMFileStartLsn(lastfileStartLSN);
} else if (lastfileSize == 0) {
switchFile = true;
rc = unlink(lastfilePath);
if (rc < 0 && errno != ENOENT)
ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", lastfilePath)));
} else {
SetNewCBMFileName(lastfileStartLSN);
if (durable_rename(lastfilePath, t_thrd.cbm_cxt.XlogCbmSys->out.name, ERROR))
ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\": %m",
lastfilePath, t_thrd.cbm_cxt.XlogCbmSys->out.name)));
SetCBMFileStartLsn(lastfileStartLSN);
}
}
if (fromScratch || switchFile)
StartNextCBMFile(trackStartLSN);
else
StartExistCBMFile(lastfileSize);
}
static void SetCBMFileName(char *cbmFileNameBuf, uint64 seqNum, XLogRecPtr startLSN, XLogRecPtr endLSN)
{
int rc;
rc = snprintf_s(cbmFileNameBuf, MAXPGPATH, MAXPGPATH - 1, bmp_file_name_template,
t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, cbmFileNameStem, seqNum, (uint32)(startLSN >> 32),
(uint32)startLSN, (uint32)(endLSN >> 32), (uint32)endLSN);
securec_check_ss(rc, "\0", "\0");
}
static void SetNewCBMFileName(XLogRecPtr startLSN)
{
SetCBMFileName(t_thrd.cbm_cxt.XlogCbmSys->out.name, t_thrd.cbm_cxt.XlogCbmSys->outSeqNum, startLSN,
InvalidXLogRecPtr);
}
static void StartNextCBMFile(XLogRecPtr startLSN)
{
SetNewCBMFileName(startLSN);
int fd = BasicOpenFile(t_thrd.cbm_cxt.XlogCbmSys->out.name, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
S_IRUSR | S_IWUSR);
if (fd < 0)
ereport(ERROR, (errcode_for_file_access(),
errmsg("could not create new CBM file \"%s\": %m", t_thrd.cbm_cxt.XlogCbmSys->out.name)));
t_thrd.cbm_cxt.XlogCbmSys->out.fd = fd;
t_thrd.cbm_cxt.XlogCbmSys->out.size = 0;
t_thrd.cbm_cxt.XlogCbmSys->out.offset = (off_t)0;
SetCBMFileStartLsn(startLSN);
ereport(LOG, (errmsg("change to new CBM file \"%s\"", t_thrd.cbm_cxt.XlogCbmSys->out.name)));
}
static void StartExistCBMFile(uint64 lastfileSize)
{
int fd = BasicOpenFile(t_thrd.cbm_cxt.XlogCbmSys->out.name, O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
if (fd < 0)
ereport(ERROR, (errcode_for_file_access(),
errmsg("could not open CBM file \"%s\": %m", t_thrd.cbm_cxt.XlogCbmSys->out.name)));
t_thrd.cbm_cxt.XlogCbmSys->out.fd = fd;
t_thrd.cbm_cxt.XlogCbmSys->out.size = lastfileSize;
t_thrd.cbm_cxt.XlogCbmSys->out.offset = (off_t)lastfileSize;
}
static HTAB *CBMPageHashInitialize(MemoryContext memoryContext)
{
HASHCTL ctl;
HTAB *hTab = NULL;
/*
* create hashtable that indexes the CBM pages
*/
errno_t rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "\0", "\0");
ctl.hcxt = memoryContext;
ctl.keysize = sizeof(CBMPageTag);
ctl.entrysize = sizeof(CbmHashEntry);
ctl.hash = tag_hash;
hTab = hash_create("CBM page hash by relfilenode and forknum", INITCBMPAGEHASHSIZE, &ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
return hTab;
}
extern void CBMFollowXlog(void)
{
(void)LWLockAcquire(CBMParseXlogLock, LW_EXCLUSIVE);
if (t_thrd.cbm_cxt.XlogCbmSys->needReset) {
/* Flush any leaked data in the top-level context */
MemoryContextResetAndDeleteChildren(t_thrd.cbm_cxt.cbmwriter_context);
MemoryContextResetAndDeleteChildren(t_thrd.cbm_cxt.cbmwriter_page_context);
ResetXlogCbmSys();
CBMTrackInit(false, InvalidXLogRecPtr);
t_thrd.cbm_cxt.XlogCbmSys->xlogParseFailed = false;
} else {
t_thrd.cbm_cxt.XlogCbmSys->needReset = true;
struct stat statbuf;
if (lstat(t_thrd.cbm_cxt.XlogCbmSys->out.name, &statbuf) != 0)
ereport(ERROR, (errcode_for_file_access(),
errmsg("failed to stat current cbm file %s :%m", t_thrd.cbm_cxt.XlogCbmSys->out.name)));
}
if (t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash == NULL)
t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash = CBMPageHashInitialize(t_thrd.cbm_cxt.cbmwriter_page_context);
TimeLineID timeLine;
XLogRecPtr checkPointRedo;
XLogRecPtr tmpEndLSN;
XLogRecPtr tmpForceEnd;
bool isRecEnd = false;
(void)LWLockAcquire(ControlFileLock, LW_SHARED);
checkPointRedo = t_thrd.shemem_ptr_cxt.ControlFile->checkPointCopy.redo;
timeLine = t_thrd.shemem_ptr_cxt.ControlFile->checkPointCopy.ThisTimeLineID;
LWLockRelease(ControlFileLock);
tmpEndLSN = checkPointRedo;
tmpForceEnd = GetTmpTargetLSN(&isRecEnd);
if (!XLogRecPtrIsInvalid(tmpForceEnd)) {
if (XLByteLT(t_thrd.cbm_cxt.XlogCbmSys->startLSN, tmpForceEnd))
tmpEndLSN = tmpForceEnd;
else {
tmpForceEnd = InvalidXLogRecPtr;
isRecEnd = true;
SetTmpTargetLSN(InvalidXLogRecPtr, true);
}
} else
isRecEnd = true;
if (XLByteLT(tmpEndLSN, t_thrd.cbm_cxt.XlogCbmSys->startLSN)) {
if (XLByteEQ(t_thrd.cbm_cxt.XlogCbmSys->startLSN, GetLatestCompTargetLSN())) {
Assert(XLByteEQ(tmpEndLSN, checkPointRedo));
ereport(LOG, (errmsg("The xlog LSN to be parsed %08X/%08X is smaller than "
"already tracked xlog LSN %08X/%08X, due to previous force "
"CBM track. Skip CBM track this time",
(uint32)(tmpEndLSN >> 32), (uint32)tmpEndLSN,
(uint32)(t_thrd.cbm_cxt.XlogCbmSys->startLSN >> 32),
(uint32)t_thrd.cbm_cxt.XlogCbmSys->startLSN)));
t_thrd.cbm_cxt.XlogCbmSys->needReset = false;
LWLockRelease(CBMParseXlogLock);
return;
} else if (!t_thrd.cbm_cxt.XlogCbmSys->firstCPCreated) {
ereport(LOG, (errmsg("The xlog LSN to be parsed %08X/%08X is smaller than "
"already tracked xlog LSN %08X/%08X. This may be caused by "
"crush recovery or switchover/failover, before the first checkpoint "
"following recovery has been created. Usually you can ignore this "
"message. But if you have manually modified xlog, to be safe, "
"please check if xlog records are consistent and uncorrupted",
(uint32)(tmpEndLSN >> 32), (uint32)tmpEndLSN,
(uint32)(t_thrd.cbm_cxt.XlogCbmSys->startLSN >> 32),
(uint32)t_thrd.cbm_cxt.XlogCbmSys->startLSN)));
t_thrd.cbm_cxt.XlogCbmSys->needReset = false;
LWLockRelease(CBMParseXlogLock);
return;
} else {
RemoveAllCBMFiles(PANIC);
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("The xlog LSN to be parsed %08X/%08X is smaller than "
"already tracked xlog LSN %08X/%08X. This may be caused by "
"xlog truncation (pg_rewind), xlog corruption or PITR (at present "
"CBM does not support multiple timelines). Under these scenarios, "
"inconsistent CBM files may be created. "
"To be safe, we zap all existing CBM files and restart CBM tracking",
(uint32)(tmpEndLSN >> 32), (uint32)tmpEndLSN,
(uint32)(t_thrd.cbm_cxt.XlogCbmSys->startLSN >> 32),
(uint32)t_thrd.cbm_cxt.XlogCbmSys->startLSN)));
}
} else if (XLByteEQ(tmpEndLSN, t_thrd.cbm_cxt.XlogCbmSys->startLSN)) {
if (checkUserRequstAndRotateCbm()) {
RotateCBMFile();
}
ereport(LOG, (errmsg("The xlog LSN to be parsed %08X/%08X is equal to "
"already tracked xlog LSN %08X/%08X. Skip CBM track this time",
(uint32)(tmpEndLSN >> 32), (uint32)tmpEndLSN,
(uint32)(t_thrd.cbm_cxt.XlogCbmSys->startLSN >> 32),
(uint32)t_thrd.cbm_cxt.XlogCbmSys->startLSN)));
t_thrd.cbm_cxt.XlogCbmSys->needReset = false;
LWLockRelease(CBMParseXlogLock);
return;
} else
ereport(LOG, (errmsg("The xlog LSN to be parsed %08X/%08X is larger than "
"already tracked xlog LSN %08X/%08X. Do CBM track one time",
(uint32)(tmpEndLSN >> 32), (uint32)tmpEndLSN,
(uint32)(t_thrd.cbm_cxt.XlogCbmSys->startLSN >> 32),
(uint32)t_thrd.cbm_cxt.XlogCbmSys->startLSN)));
t_thrd.cbm_cxt.XlogCbmSys->endLSN = tmpEndLSN;
if (ParseXlogIntoCBMPages(timeLine, isRecEnd)) {
ereport(LOG, (errmsg("Found no any valid xlog record From the already tracked xlog "
"LSN %08X/%08X. Skip CBM track this time",
(uint32)(t_thrd.cbm_cxt.XlogCbmSys->startLSN >> 32),
(uint32)t_thrd.cbm_cxt.XlogCbmSys->startLSN)));
t_thrd.cbm_cxt.XlogCbmSys->needReset = false;
LWLockRelease(CBMParseXlogLock);
return;
}
if (t_thrd.cbm_cxt.XlogCbmSys->totalPageNum == 0)
CreateDummyCBMEtyPageAndInsert();
PrintCBMHashTab(t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash);
FlushCBMPagesToDisk(t_thrd.cbm_cxt.XlogCbmSys, true);
t_thrd.cbm_cxt.XlogCbmSys->startLSN = t_thrd.cbm_cxt.XlogCbmSys->endLSN;
SetCBMTrackedLSN(t_thrd.cbm_cxt.XlogCbmSys->startLSN);
if (!XLogRecPtrIsInvalid(tmpForceEnd)) {
Assert(XLByteLT(GetLatestCompTargetLSN(), tmpForceEnd));
if (isRecEnd) {
Assert(XLByteEQ(tmpForceEnd, t_thrd.cbm_cxt.XlogCbmSys->startLSN));
}
/*
* we use XlogCbmSys->startLSN to mark LatestCompTargetLSN,
* because we COULD NOT GUARANTEE that GetTmpTargetLSN() return
* a available xlog record end if isRecEnd is false.
*/
SetLatestCompTargetLSN(t_thrd.cbm_cxt.XlogCbmSys->startLSN);
SetTmpTargetLSN(InvalidXLogRecPtr, true);
}
if (DLListLength(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList) > MAXCBMFREEPAGENUM) {
MemoryContextResetAndDeleteChildren(t_thrd.cbm_cxt.cbmwriter_page_context);
DLInitList(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList);
t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash = NULL;
}
t_thrd.cbm_cxt.XlogCbmSys->needReset = false;
LWLockRelease(CBMParseXlogLock);
}
static bool checkUserRequstAndRotateCbm()
{
XLogRecPtr requestRotateLsn, currStartLsn, currTrackedLsn;
requestRotateLsn = GetCBMRotateLsn();
currStartLsn = GetCBMFileStartLsn();
/* read thread level var instead of gloabl shem instance var */
currTrackedLsn = t_thrd.cbm_cxt.XlogCbmSys->startLSN;
if (unlikely(XLByteLT(currStartLsn, requestRotateLsn))) {
if (XLByteLE(requestRotateLsn, currTrackedLsn)) {
ereport(LOG, (errmsg("rotate cbm for user request at %08X/%08X",
(uint32)(requestRotateLsn >> 32), (uint32)requestRotateLsn)));
return true;
}
}
return false;
}
static bool ParseXlogIntoCBMPages(TimeLineID timeLine, bool isRecEnd)
{
XLogRecord *record = NULL;
XLogReaderState *xlogreader = NULL;
char *errormsg = NULL;
XLogPageReadPrivateCBM readprivate;
XLogRecPtr startPoint = t_thrd.cbm_cxt.XlogCbmSys->startLSN;
bool parseSkip = false;
readprivate.datadir = t_thrd.proc_cxt.DataDir;
readprivate.tli = timeLine;
xlogreader = XLogReaderAllocate(&CBMXLogPageRead, &readprivate);
if (xlogreader == NULL)
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate xlog reader")));
do {
record = XLogReadRecord(xlogreader, startPoint, &errormsg);
if (record == NULL) {
XLogRecPtr errptr;
if (XLByteEQ(startPoint, InvalidXLogRecPtr))
errptr = xlogreader->EndRecPtr;
else
errptr = startPoint;
if (!isRecEnd) {
if (errormsg != NULL)
ereport(WARNING, (errmsg("could not read WAL record at %08X/%08X: %s", (uint32)(errptr >> 32),
(uint32)errptr, errormsg)));
else
ereport(WARNING,
(errmsg("could not read WAL record at %08X/%08X", (uint32)(errptr >> 32), (uint32)errptr)));
if (XLByteEQ(startPoint, InvalidXLogRecPtr)) {
ereport(LOG, (errmsg("reach CBM parse end. The next xlog record starts at %08X/%08X",
(uint32)(xlogreader->EndRecPtr >> 32), (uint32)xlogreader->EndRecPtr)));
t_thrd.cbm_cxt.XlogCbmSys->endLSN = xlogreader->EndRecPtr;
parseSkip = false;
} else
parseSkip = true;
break;
} else {
t_thrd.cbm_cxt.XlogCbmSys->xlogParseFailed = true;
if (errormsg != NULL)
ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION),
errmsg("could not read WAL record at %08X/%08X: %s", (uint32)(errptr >> 32),
(uint32)errptr, errormsg)));
else
ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("could not read WAL record at %08X/%08X",
(uint32)(errptr >> 32), (uint32)errptr)));
}
}
TrackChangeBlock(xlogreader);
advanceXlogPtrToNextPageIfNeeded(&(xlogreader->EndRecPtr));
if (XLByteLE(t_thrd.cbm_cxt.XlogCbmSys->endLSN, xlogreader->EndRecPtr)) {
ereport(LOG, (errmsg("reach CBM parse end. The next xlog record starts at %08X/%08X",
(uint32)(xlogreader->EndRecPtr >> 32), (uint32)xlogreader->EndRecPtr)));
/*
* Force the coming startLSN be set to a valid xlog record start LSN
* if this is a forced cbm track with a non-record-end stop position.
*/
if (!isRecEnd)
t_thrd.cbm_cxt.XlogCbmSys->endLSN = xlogreader->EndRecPtr;
parseSkip = false;
break;
}
startPoint = InvalidXLogRecPtr; /* continue reading at next record */
} while (true);
XLogReaderFree(xlogreader);
if (t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd != -1) {
if (close(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd) != 0)
ereport(WARNING, (errcode_for_file_access(),
errmsg("could not close file \"%s\" ", t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath)));
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd = -1;
}
return parseSkip;
}
/* XLogreader callback function, to read a WAL page */
static int CBMXLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetRecPtr,
char *readBuf, TimeLineID *pageTLI, char* xlog_path)
{
XLogPageReadPrivateCBM *readprivate = (XLogPageReadPrivateCBM *)xlogreader->private_data;
uint32 targetPageOff;
int rc = 0;
targetPageOff = targetPagePtr % XLogSegSize;
/*
* See if we need to switch to a new segment because the requested record
* is not in the currently open one.
*/
if (t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd >= 0 &&
!XLByteInSeg(targetPagePtr, t_thrd.cbm_cxt.XlogCbmSys->xlogRead.logSegNo)) {
if (close(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd) != 0)
ereport(WARNING, (errcode_for_file_access(),
errmsg("could not close file \"%s\" ", t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath)));
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd = -1;
}
XLByteToSeg(targetPagePtr, t_thrd.cbm_cxt.XlogCbmSys->xlogRead.logSegNo);
if (t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd < 0) {
char xlogfname[MAXFNAMELEN];
rc = snprintf_s(xlogfname, MAXFNAMELEN, MAXFNAMELEN - 1, "%08X%08X%08X", readprivate->tli,
(uint32)((t_thrd.cbm_cxt.XlogCbmSys->xlogRead.logSegNo) / XLogSegmentsPerXLogId),
(uint32)((t_thrd.cbm_cxt.XlogCbmSys->xlogRead.logSegNo) % XLogSegmentsPerXLogId));
securec_check_ss(rc, "", "");
rc = snprintf_s(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath, MAXPGPATH, MAXPGPATH - 1, "%s/" XLOGDIR "/%s",
readprivate->datadir, xlogfname);
securec_check_ss(rc, "\0", "\0");
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd = BasicOpenFile(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath,
O_RDONLY | PG_BINARY, 0);
if (t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd < 0) {
ereport(WARNING, (errmsg("could not open file \"%s\": %s", t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath,
strerror(errno))));
return -1;
}
}
/*
* At this point, we have the right segment open.
*/
Assert(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd != -1);
PGSTAT_INIT_TIME_RECORD();
PGSTAT_START_TIME_RECORD();
if (pread(t_thrd.cbm_cxt.XlogCbmSys->xlogRead.fd, readBuf, XLOG_BLCKSZ, (off_t)targetPageOff) != XLOG_BLCKSZ) {
PGSTAT_END_TIME_RECORD(DATA_IO_TIME);
ereport(WARNING, (errmsg("could not read page from file \"%s\" at page offset %u: %s",
t_thrd.cbm_cxt.XlogCbmSys->xlogRead.filePath, targetPageOff, strerror(errno))));
return -1;
}
PGSTAT_END_TIME_RECORD(DATA_IO_TIME);
*pageTLI = readprivate->tli;
return XLOG_BLCKSZ;
}
/*
* Extract information on which blocks the current record modifies.
*/
static void TrackChangeBlock(XLogReaderState *record)
{
XLogRecPtr lsn = record->EndRecPtr;
XLogRecPtr prev = XLogRecGetPrev(record);
Assert(record != NULL);
ereport(DEBUG1, (errmsg("extract WAL: cur: %X/%X; prev %X/%X; xid %lu; "
"len/total_len %u/%u; info %u; rmid %u",
(uint32)(lsn >> 32), (uint32)lsn, (uint32)(prev >> 32), (uint32)prev, XLogRecGetXid(record),
XLogRecGetDataLen(record), XLogRecGetTotalLen(record), (uint32)XLogRecGetInfo(record),
(uint32)XLogRecGetRmid(record))));
if (XLogRecHasAnyBlockRefs(record))
TrackRelPageModification(record);
else if (XLogRecGetRmid(record) == RM_HEAP2_ID) {
/* CU modification does not have block ref */
Assert(!XLogRecHasAnyBlockRefs(record));
TrackCuBlockModification(record);
}
/*
* Following xlog record rm catagories do not have block ref.
* Later we can also add cbm track functions in rmgrlist for each rmgr.
*/
switch (XLogRecGetRmid(record)){
case RM_XACT_ID: {
Assert(!XLogRecHasAnyBlockRefs(record));
TrackRelStorageDrop(record);
break;
}
case RM_SMGR_ID: {
Assert(!XLogRecHasAnyBlockRefs(record));
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info == XLOG_SMGR_CREATE)
TrackRelStorageCreate(record);
else if (info == XLOG_SMGR_TRUNCATE)
TrackRelStorageTruncate(record);
break;
}
case RM_DBASE_ID: {
Assert(!XLogRecHasAnyBlockRefs(record));
TrackDbStorageChange(record);
break;
}
case RM_TBLSPC_ID: {
Assert(!XLogRecHasAnyBlockRefs(record));
TrackTblspcStorageChange(record);
break;
}
case RM_RELMAP_ID: {
Assert(!XLogRecHasAnyBlockRefs(record));
TrackRelmapChange(record);
break;
}
case RM_SEGPAGE_ID: {
TrackSegmentPageChange(record);
break;
}
case RM_UHEAP_ID: {
TrackUndoPageModification(record);
break;
}
case RM_UNDOLOG_ID: {
Assert(!XLogRecHasAnyBlockRefs(record));
TrackUndoStorageModification(record);
break;
}
case RM_UNDOACTION_ID: {
Assert(!XLogRecHasAnyBlockRefs(record));
TrackTransSlotModification(record);
break;
}
/* other xlog record types are tracked by block refs within records */
default:
break;
}
/* clean of vm bit needs to be tracked seperately */
TrackVMPageModification(record);
}
static void TrackRelPageModification(XLogReaderState *record)
{
int block_id = 0;
RelFileNode rNode;
ForkNumber forkNum;
BlockNumber blkNo;
/* do actual block analyze */
for (block_id = 0; block_id <= record->max_block_id; block_id++) {
if (!XLogRecGetBlockTag(record, block_id, &rNode, &forkNum, &blkNo))
continue;
if (XLOG_NEED_PHYSICAL_LOCATION(rNode)) {
SegmentCheck(forkNum >= 0);
/* Logic location: relNode & blockno is changed */
uint8 segfileno;
BlockNumber segblock;
XLogRecGetPhysicalBlock(record, block_id, &segfileno, &segblock);
rNode.relNode = segfileno;
rNode.bucketNode = SegmentBktId;
blkNo = segblock;
}
/*
* At present, we do not record page modification information for dfs relations.
*/
if (forkNum <= InvalidForkNumber)
continue;
/* Only main fork, fsm fork, vm fork and init fork have page ref xlog */
Assert(forkNum == MAIN_FORKNUM || forkNum == FSM_FORKNUM || forkNum == VISIBILITYMAP_FORKNUM ||
forkNum == INIT_FORKNUM);
ereport(DEBUG3, (errmsg("block%d: rel %u/%u/%u forknum %d blkno %u", block_id, rNode.spcNode, rNode.dbNode,
rNode.relNode, forkNum, blkNo)));
RegisterBlockChange(rNode, forkNum, blkNo);
}
}
static void TrackSegmentPageChange(XLogReaderState *record)
{
/*
* We only needs to consider XLOG_SEG_CREATE_EXTENT_GROUP, which creates the segment file.
* Other segment-page meta-data modifications are recorded in "TrackRelPageModification"
*/
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if (info == XLOG_SEG_CREATE_EXTENT_GROUP) {
char *data = XLogRecGetData(record);
RelFileNode *rnode = (RelFileNode *)data;
ForkNumber *forknum = (ForkNumber *)(data + sizeof(RelFileNode));
RegisterBlockChangeExtended(*rnode, *forknum, InvalidBlockNumber, PAGETYPE_CREATE, InvalidBlockNumber);
/* Modify map head */
RegisterBlockChange(*rnode, *forknum, DF_MAP_HEAD_PAGE);
}
}
static void TrackCuBlockModification(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
if ((info & XLOG_HEAP_OPMASK) == XLOG_HEAP2_LOGICAL_NEWPAGE) {
xl_heap_logical_newpage *xlrec = (xl_heap_logical_newpage *)XLogRecGetData(record);
BlockNumber blkNo;
Assert(xlrec->type == COLUMN_STORE);
Assert(xlrec->attid > 0);
int align_size = CUAlignUtils::GetCuAlignSizeColumnId(xlrec->attid);
Assert(xlrec->offset % align_size == 0);
Assert(xlrec->blockSize % align_size == 0);
RelFileNode tmp_node;
RelFileNodeCopy(tmp_node, xlrec->node, XLogRecGetBucketId(record));
for (blkNo = (xlrec->offset / align_size); blkNo < ((xlrec->offset + xlrec->blockSize) / align_size); blkNo++)
RegisterBlockChange(tmp_node, ColumnId2ColForkNum(xlrec->attid), blkNo);
}
}
static void TrackRelStorageDrop(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
int nrels = 0;
ColFileNodeRel *xnodes = NULL;
if (info == XLOG_XACT_COMMIT) {
xl_xact_commit *xlrec = (xl_xact_commit *)XLogRecGetData(record);
nrels = xlrec->nrels;
xnodes = xlrec->xnodes;
} else if (info == XLOG_XACT_ABORT || info == XLOG_XACT_ABORT_WITH_XID) {
xl_xact_abort *xlrec = (xl_xact_abort *)XLogRecGetData(record);
nrels = xlrec->nrels;
xnodes = xlrec->xnodes;
} else if (info == XLOG_XACT_COMMIT_PREPARED) {
xl_xact_commit_prepared *xlrec = (xl_xact_commit_prepared *)XLogRecGetData(record);
nrels = xlrec->crec.nrels;
xnodes = xlrec->crec.xnodes;
} else if (info == XLOG_XACT_ABORT_PREPARED) {
xl_xact_abort_prepared *xlrec = (xl_xact_abort_prepared *)XLogRecGetData(record);
nrels = xlrec->arec.nrels;
xnodes = xlrec->arec.xnodes;
}
for (int i = 0; i < nrels; ++i) {
ColFileNode colFileNodeData;
ColFileNodeRel *colFileNodeRel = xnodes + i;
ColFileNodeCopy(&colFileNodeData, colFileNodeRel);
/* set opt to compressOpt if FORKNUM is compress forknum */
if (IS_COMPRESS_DELETE_FORK(colFileNodeData.forknum)) {
SET_OPT_BY_NEGATIVE_FORK(colFileNodeData.filenode, colFileNodeData.forknum);
colFileNodeData.forknum = MAIN_FORKNUM;
}
/* Logic relfilenode delete is ignored */
if (IsSegmentFileNode(colFileNodeData.filenode)) {
continue;
}
/*
* At present, we do not record relation drop on hdfs storage.
*/
if (colFileNodeData.forknum <= InvalidForkNumber)
continue;
Assert(colFileNodeData.forknum == MAIN_FORKNUM || IsValidColForkNum(colFileNodeData.forknum));
RegisterBlockChangeExtended(colFileNodeData.filenode, colFileNodeData.forknum, InvalidBlockNumber,
PAGETYPE_DROP, InvalidBlockNumber);
}
}
static void TrackRelStorageCreate(XLogReaderState *record)
{
xl_smgr_create *xlrec = (xl_smgr_create *)XLogRecGetData(record);
/*
* Originally, only main fork and init fork will have storage create xlog. However,
* alter table set tablespace also record storage create for fsm and vm fork
* (seems unnecessary), so we have to loosen the check.
*/
Assert(xlrec->forkNum >= MAIN_FORKNUM);
/* At present, we do not record dfs relation information */
if (xlrec->forkNum <= InvalidForkNumber)
return;
RelFileNode rnode;
RelFileNodeCopy(rnode, xlrec->rnode, XLogRecGetBucketId(record));
/* Logic relfilenode create is ignored */
if (IsSegmentFileNode(rnode)) {
return;
}
RegisterBlockChangeExtended(rnode, xlrec->forkNum, InvalidBlockNumber, PAGETYPE_CREATE, InvalidBlockNumber);
}
static void TrackRelStorageTruncate(XLogReaderState *record)
{
xl_smgr_truncate *xlrec = (xl_smgr_truncate *)XLogRecGetData(record);
BlockNumber mainTruncBlkNo, fsmTruncBlkNo, vmTruncBlkNo;
RelFileNode rnode;
RelFileNodeCopy(rnode, xlrec->rnode, XLogRecGetBucketId(record));
/* Logic relfilenode create is ignored */
if (IsSegmentFileNode(rnode)) {
return;
}
mainTruncBlkNo = xlrec->blkno;
RegisterBlockChangeExtended(rnode, MAIN_FORKNUM, InvalidBlockNumber, PAGETYPE_TRUNCATE, mainTruncBlkNo);
fsmTruncBlkNo = FreeSpaceMapCalTruncBlkNo(mainTruncBlkNo);
RegisterBlockChangeExtended(rnode, FSM_FORKNUM, InvalidBlockNumber, PAGETYPE_TRUNCATE, fsmTruncBlkNo);
vmTruncBlkNo = VisibilityMapCalTruncBlkNo(mainTruncBlkNo);
RegisterBlockChangeExtended(rnode, VISIBILITYMAP_FORKNUM, InvalidBlockNumber, PAGETYPE_TRUNCATE, vmTruncBlkNo);
}
static void TrackSegmentVMPageModification(XLogReaderState *record, int blockId)
{
RelFileNode rNode = InvalidRelFileNode;
uint8 vmFile;
BlockNumber vmBlkNo;
bool has_vm_loc = false;
XLogRecGetBlockTag(record, blockId, &rNode, NULL, NULL);
XLogRecGetVMPhysicalBlock(record, blockId, &vmFile, &vmBlkNo, &has_vm_loc);
Assert(OidIsValid(rNode.relNode));
Assert(has_vm_loc);
rNode.relNode = vmFile;
rNode.bucketNode = SegmentBktId;
RegisterBlockChange(rNode, VISIBILITYMAP_FORKNUM, vmBlkNo);
}
static void RegisterVMPageModification(XLogReaderState *record, int blockId)
{
BlockNumber blkNo = InvalidBlockNumber;
RelFileNode rNode = InvalidRelFileNode;
XLogRecGetBlockTag(record, blockId, &rNode, NULL, &blkNo);
if (IsSegmentFileNode(rNode)) {
TrackSegmentVMPageModification(record, blockId);
} else {
BlockNumber vmBlkNo = HEAPBLK_TO_MAPBLOCK(blkNo);
RegisterBlockChange(rNode, VISIBILITYMAP_FORKNUM, vmBlkNo);
}
}
static void TrackVMPageModification(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
bool isinit = ((XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0);
Pointer rec_data = (Pointer)XLogRecGetData(record);
BlockNumber oldblk;
if (XLogRecGetRmid(record) == RM_HEAP_ID) {
switch (info & XLOG_HEAP_OPMASK) {
case XLOG_HEAP_INSERT: {
if (isinit)
rec_data += sizeof(TransactionId);
xl_heap_insert *xlrec = (xl_heap_insert *)rec_data;
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
RegisterVMPageModification(record, HEAP_INSERT_ORIG_BLOCK_NUM);
break;
}
case XLOG_HEAP_DELETE: {
xl_heap_delete *xlrec = (xl_heap_delete *)rec_data;
if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
RegisterVMPageModification(record, HEAP_DELETE_ORIG_BLOCK_NUM);
break;
}
case XLOG_HEAP_HOT_UPDATE:
case XLOG_HEAP_UPDATE: {
if (isinit)
rec_data += sizeof(TransactionId);
xl_heap_update *xlrec = (xl_heap_update *)rec_data;
/* heapBlkNo1 is always block#0 heapBlkNo2 is always block#1 */
if ((xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED) &&
XLogRecGetBlockTag(record, HEAP_UPDATE_OLD_BLOCK_NUM, NULL, NULL, &oldblk))
RegisterVMPageModification(record, HEAP_UPDATE_OLD_BLOCK_NUM);
if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
RegisterVMPageModification(record, HEAP_UPDATE_NEW_BLOCK_NUM);
break;
}
default:
break;
};
} else if (XLogRecGetRmid(record) == RM_HEAP2_ID) {
if ((info & XLOG_HEAP_OPMASK) == XLOG_HEAP2_MULTI_INSERT) {
if (isinit)
rec_data += sizeof(TransactionId);
xl_heap_multi_insert *xlrec = (xl_heap_multi_insert *)rec_data;
if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
RegisterVMPageModification(record, HEAP_MULTI_INSERT_ORIG_BLOCK_NUM);
}
} else
return;
}
static void TrackDbStorageChange(XLogReaderState *record)
{
/*
* segment-page storage does not change the database directory. Thus it behaves the same
* as heap-disk storage.
*/
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
RelFileNode rNode = InvalidRelFileNode;
if (info == XLOG_DBASE_CREATE) {
xl_dbase_create_rec *xlrec = (xl_dbase_create_rec *)XLogRecGetData(record);
rNode.spcNode = xlrec->tablespace_id;
rNode.dbNode = xlrec->db_id;
Assert(rNode.dbNode != InvalidOid);
RegisterBlockChangeExtended(rNode, MAIN_FORKNUM, InvalidBlockNumber, PAGETYPE_CREATE, InvalidBlockNumber);
} else if (info == XLOG_DBASE_DROP) {
xl_dbase_drop_rec *xlrec = (xl_dbase_drop_rec *)XLogRecGetData(record);
rNode.spcNode = xlrec->tablespace_id;
rNode.dbNode = xlrec->db_id;
Assert(rNode.dbNode != InvalidOid);
RegisterBlockChangeExtended(rNode, MAIN_FORKNUM, InvalidBlockNumber, PAGETYPE_DROP, InvalidBlockNumber);
}
}
static void TrackTblspcStorageChange(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
RelFileNode rNode = InvalidRelFileNode;
if (info == XLOG_TBLSPC_CREATE || info == XLOG_TBLSPC_RELATIVE_CREATE) {
xl_tblspc_create_rec *xlrec = (xl_tblspc_create_rec *)XLogRecGetData(record);
rNode.spcNode = xlrec->ts_id;
RegisterBlockChangeExtended(rNode, MAIN_FORKNUM, InvalidBlockNumber, PAGETYPE_CREATE, InvalidBlockNumber);
} else if (info == XLOG_TBLSPC_DROP) {
xl_tblspc_drop_rec *xlrec = (xl_tblspc_drop_rec *)XLogRecGetData(record);
rNode.spcNode = xlrec->ts_id;
RegisterBlockChangeExtended(rNode, MAIN_FORKNUM, InvalidBlockNumber, PAGETYPE_DROP, InvalidBlockNumber);
}
}
static void TrackRelmapChange(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
RelFileNode rNode = InvalidRelFileNode;
if (info == XLOG_RELMAP_UPDATE) {
xl_relmap_update *xlrec = (xl_relmap_update *)XLogRecGetData(record);
rNode.spcNode = xlrec->tsid;
rNode.dbNode = xlrec->dbid;
RegisterBlockChangeExtended(rNode, MAIN_FORKNUM, InvalidBlockNumber, PAGETYPE_TRUNCATE, 0);
}
}
static void skipUndoRecBody(char **currLogPtr, XlUndoHeader *xlundohdr)
{
if ((xlundohdr->flag & XLOG_UNDO_HEADER_HAS_SUB_XACT) != 0) {
*currLogPtr += sizeof(bool);
}
if ((xlundohdr->flag & XLOG_UNDO_HEADER_HAS_BLK_PREV) != 0) {
*currLogPtr += sizeof(UndoRecPtr);
}
if ((xlundohdr->flag & XLOG_UNDO_HEADER_HAS_PREV_URP) != 0) {
*currLogPtr += sizeof(UndoRecPtr);
}
if ((xlundohdr->flag & XLOG_UNDO_HEADER_HAS_PARTITION_OID) != 0) {
*currLogPtr += sizeof(Oid);
}
if ((xlundohdr->flag & XLOG_UNDO_HEADER_HAS_CURRENT_XID) != 0) {
*currLogPtr += sizeof(TransactionId);
}
}
static void TrackUheapInsert(XLogReaderState *record)
{
undo::XlogUndoMeta *undometa;
BlockNumber undoStartBlk;
UndoSlotPtr slotPtr;
RelFileNode rnode;
XlUHeapInsert *xlrec = (XlUHeapInsert *)XLogRecGetData(record);
XlUndoHeader *xlundohdr = (XlUndoHeader *)((char *)xlrec + SizeOfUHeapInsert);
char *currLogPtr = ((char *)xlundohdr + SizeOfXLUndoHeader);
skipUndoRecBody(&currLogPtr, xlundohdr);
undometa = (undo::XlogUndoMeta *)((char *)currLogPtr);
/*
* Register undo page modification.
* We do not bother parsing whole undo record size. Instead, we register
* all possible modified undo pages starting from xlundohdr->urecptr.
*/
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, xlundohdr->urecptr, UNDO_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(xlundohdr->urecptr);
for (int i = 0; i < MAX_BUFFER_PER_UNDO; i++) {
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk + i);
}
/* Register transaction slot page modification. */
slotPtr = MAKE_UNDO_PTR(UNDO_PTR_GET_ZONE_ID(xlundohdr->urecptr), undometa->slotPtr);
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, slotPtr, UNDO_SLOT_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(slotPtr);
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk);
}
static void TrackUheapDelete(XLogReaderState *record)
{
undo::XlogUndoMeta *undometa;
BlockNumber undoStartBlk;
UndoSlotPtr slotPtr;
RelFileNode rnode;
XlUHeapDelete *xlrec = (XlUHeapDelete *)XLogRecGetData(record);
XlUndoHeader *xlundohdr = (XlUndoHeader *)((char *)xlrec + SizeOfUHeapDelete);
char *currLogPtr = ((char *)xlundohdr + SizeOfXLUndoHeader);
skipUndoRecBody(&currLogPtr, xlundohdr);
undometa = (undo::XlogUndoMeta *)((char *)currLogPtr);
/*
* Register undo page modification.
* We do not bother parsing whole undo record size. Instead, we register
* all possible modified undo pages starting from xlundohdr->urecptr.
*/
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, xlundohdr->urecptr, UNDO_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(xlundohdr->urecptr);
for (int i = 0; i < MAX_BUFFER_PER_UNDO; i++) {
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk + i);
}
/* Register transaction slot page modification. */
slotPtr = MAKE_UNDO_PTR(UNDO_PTR_GET_ZONE_ID(xlundohdr->urecptr), undometa->slotPtr);
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, slotPtr, UNDO_SLOT_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(slotPtr);
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk);
}
static void TrackUheapUpdate(XLogReaderState *record)
{
undo::XlogUndoMeta *undometa;
BlockNumber undoStartBlk;
UndoSlotPtr slotPtr;
RelFileNode rnode;
XlUHeapUpdate *xlrec = (XlUHeapUpdate *)XLogRecGetData(record);
XlUndoHeader *xlundohdr = (XlUndoHeader *)((char *)xlrec + SizeOfUHeapUpdate);
char *currLogPtr = ((char *)xlundohdr + SizeOfXLUndoHeader);
skipUndoRecBody(&currLogPtr, xlundohdr);
/*
* Register undo page modification.
* We do not bother parsing whole undo record size. Instead, we register
* all possible modified undo pages starting from xlundohdr->urecptr.
*/
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, xlundohdr->urecptr, UNDO_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(xlundohdr->urecptr);
for (int i = 0; i < MAX_BUFFER_PER_UNDO; i++) {
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk + i);
}
if (xlrec->flags & XLZ_NON_INPLACE_UPDATE) {
XlUndoHeader *xlnewundohdr = (XlUndoHeader *)currLogPtr;
currLogPtr += SizeOfXLUndoHeader;
skipUndoRecBody(&currLogPtr, xlnewundohdr);
/* Register undo page modification for non-inplace update. */
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, xlnewundohdr->urecptr, UNDO_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(xlnewundohdr->urecptr);
for (int i = 0; i < MAX_BUFFER_PER_UNDO; i++) {
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk + i);
}
}
undometa = (undo::XlogUndoMeta *)((char *)currLogPtr);
/* Register transaction slot page modification. */
slotPtr = MAKE_UNDO_PTR(UNDO_PTR_GET_ZONE_ID(xlundohdr->urecptr), undometa->slotPtr);
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, slotPtr, UNDO_SLOT_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(slotPtr);
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk);
}
static void TrackUheapMultiInsert(XLogReaderState *record)
{
undo::XlogUndoMeta *undometa;
BlockNumber undoStartBlk;
UndoSlotPtr slotPtr;
RelFileNode rnode;
int nranges;
UndoRecPtr *urpvec = NULL;
bool isinit = (XLogRecGetInfo(record) & XLOG_UHEAP_INIT_PAGE) != 0;
XlUndoHeader *xlundohdr = (XlUndoHeader *)XLogRecGetData(record);
char *currLogPtr = ((char *)xlundohdr + SizeOfXLUndoHeader);
skipUndoRecBody(&currLogPtr, xlundohdr);
currLogPtr += sizeof(UndoRecPtr);
undometa = (undo::XlogUndoMeta *)((char *)currLogPtr);
currLogPtr += undometa->Size();;
if (isinit) {
currLogPtr += sizeof(TransactionId);
currLogPtr += sizeof(uint16);
}
if ((record->decoded_record->xl_term & XLOG_CONTAIN_CSN) != 0) {
currLogPtr += sizeof(CommitSeqNo);
}
currLogPtr += SizeOfUHeapMultiInsert;
nranges = *(int *)currLogPtr;
currLogPtr += sizeof(int);
urpvec = (UndoRecPtr *)currLogPtr;
/*
* Register undo page modification.
* We do not bother parsing whole undo record size. Instead, we register
* all possible modified undo pages starting from xlundohdr->urecptr.
*/
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, xlundohdr->urecptr, UNDO_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(xlundohdr->urecptr);
for (int i = 0; i < MAX_BUFFER_PER_UNDO; i++) {
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk + i);
}
/* Register undo page modification for each undo record in heap multi insert */
for (int i = 0; i < nranges; i++) {
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, urpvec[i], UNDO_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(urpvec[i]);
for (int j = 0; j < MAX_BUFFER_PER_UNDO; j++) {
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk + j);
}
}
/* Register transaction slot page modification. */
slotPtr = MAKE_UNDO_PTR(UNDO_PTR_GET_ZONE_ID(xlundohdr->urecptr), undometa->slotPtr);
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, slotPtr, UNDO_SLOT_DB_OID);
undoStartBlk = UNDO_PTR_GET_BLOCK_NUM(slotPtr);
RegisterBlockChange(rnode, UNDO_FORKNUM, undoStartBlk);
}
/*
* TrackUndoModification only tracks undo page modification.
* Uheap page modification is tracked by parsing referenced blocks in the xlog record.
*/
static void TrackUndoPageModification(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
switch (info & XLOG_UHEAP_OPMASK) {
case XLOG_UHEAP_INSERT:
TrackUheapInsert(record);
break;
case XLOG_UHEAP_DELETE:
TrackUheapDelete(record);
break;
case XLOG_UHEAP_UPDATE:
TrackUheapUpdate(record);
break;
case XLOG_UHEAP_MULTI_INSERT:
TrackUheapMultiInsert(record);
break;
/* other undo xlog record types are tracked by block refs within records */
case XLOG_UHEAP_FREEZE_TD_SLOT:
case XLOG_UHEAP_INVALID_TD_SLOT:
case XLOG_UHEAP_CLEAN:
break;
default:
ereport(WARNING, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("UHeapRedo: unknown op code %u", (uint8)info)));
}
}
static void TrackUndoStorageModification(XLogReaderState *record)
{
Assert(record != NULL);
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
void *xlrec = (void *)XLogRecGetData(record);
bool extend = false;
bool undoFile = true;
UndoRecPtr begUrp = INVALID_UNDO_REC_PTR;
UndoRecPtr endUrp = INVALID_UNDO_REC_PTR;
BlockNumber begBlk;
BlockNumber endBlk;
RelFileNode rnode;
switch (info) {
case XLOG_UNDO_UNLINK:
extend = false;
undoFile = true;
endUrp = ((undo::XlogUndoUnlink *)xlrec)->head;
break;
case XLOG_UNDO_EXTEND:
extend = true;
undoFile = true;
begUrp = ((undo::XlogUndoExtend *)xlrec)->prevtail;
endUrp = ((undo::XlogUndoExtend *)xlrec)->tail;
break;
case XLOG_UNDO_CLEAN:
extend = false;
undoFile = true;
/* always clean the last undo seg, see CleanUndoSpace */
endUrp = ((undo::XlogUndoClean *)xlrec)->tail + UNDO_LOG_SEGMENT_SIZE;
break;
case XLOG_SLOT_CLEAN:
extend = false;
undoFile = false;
/* always clean the last undo seg, see CleanSlotSpace */
endUrp = ((undo::XlogUndoClean *)xlrec)->tail + UNDO_META_SEGMENT_SIZE;
break;
case XLOG_SLOT_UNLINK:
extend = false;
undoFile = false;
endUrp = ((undo::XlogUndoUnlink *)xlrec)->head;
break;
case XLOG_SLOT_EXTEND:
extend = true;
undoFile = false;
begUrp = ((undo::XlogUndoExtend *)xlrec)->prevtail;
endUrp = ((undo::XlogUndoExtend *)xlrec)->tail;
break;
/* undo discard only modifies undo meta */
case XLOG_UNDO_DISCARD:
return;
default:
ereport(WARNING, (errcode(ERRCODE_DATA_CORRUPTED),
errmsg("UndoLog: unknown op code %u", (uint8)info)));
return;
}
if (extend) {
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, begUrp, undoFile ? UNDO_DB_OID : UNDO_SLOT_DB_OID);
begBlk = UNDO_PTR_GET_BLOCK_NUM(begUrp);
endBlk = UNDO_PTR_GET_BLOCK_NUM(endUrp);
/* blocks before new tail were extended */
while (begBlk < endBlk) {
RegisterBlockChange(rnode, UNDO_FORKNUM, begBlk);
begBlk++;
}
} else {
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, endUrp, undoFile ? UNDO_DB_OID : UNDO_SLOT_DB_OID);
endBlk = UNDO_PTR_GET_BLOCK_NUM(endUrp);
/* blocks(seg) before new head(seg) were recycled */
RegisterBlockChangeExtended(rnode, UNDO_FORKNUM, InvalidBlockNumber, PAGETYPE_TRUNCATE, endBlk);
}
}
static void TrackTransSlotModification(XLogReaderState *record)
{
uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
UndoSlotPtr slotPtr;
RelFileNode rnode;
BlockNumber blk;
switch (info) {
case XLOG_ROLLBACK_FINISH:
slotPtr = ((undo::XlogRollbackFinish *)XLogRecGetData(record))->slotPtr;
blk = UNDO_PTR_GET_BLOCK_NUM(slotPtr);
UNDO_PTR_ASSIGN_REL_FILE_NODE(rnode, slotPtr, UNDO_SLOT_DB_OID);
RegisterBlockChange(rnode, UNDO_FORKNUM, blk);
break;
default:
elog(WARNING, "UndoXlogRollbackFinishRedo: unknown op code %u", info);
}
}
static void RegisterBlockChange(const RelFileNode &rNode, ForkNumber forkNum, BlockNumber blkNo)
{
if (IsSegmentFileNode(rNode)) {
Assert(IsSegmentPhysicalRelNode(rNode));
RegisterBlockChangeExtended(rNode, forkNum, blkNo, PAGETYPE_MODIFY, InvalidBlockNumber);
/*
* Segment-page files are extended with 128MB granularity. But CBM does not track
* ftruncate information. Thus, we need CBM records the last block in this 128MB,
* to ensure the segment-page file is always 128MB aligned after restore.
*/
RegisterBlockChangeExtended(rNode, forkNum, CM_ALIGN_ANY(blkNo, DF_FILE_EXTEND_STEP_BLOCKS) - 1,
PAGETYPE_MODIFY, InvalidBlockNumber);
} else {
RegisterBlockChangeExtended(rNode, forkNum, blkNo, PAGETYPE_MODIFY, InvalidBlockNumber);
}
}
static void RegisterBlockChangeExtended(const RelFileNode &rNode, ForkNumber forkNum, BlockNumber blkNo, uint8 pageType,
BlockNumber truncBlkNo)
{
Assert((BlockNumberIsValid(blkNo) && pageType == PAGETYPE_MODIFY) ||
(!BlockNumberIsValid(blkNo) &&
(pageType == PAGETYPE_DROP || pageType == PAGETYPE_TRUNCATE || pageType == PAGETYPE_CREATE)));
Assert((BlockNumberIsValid(truncBlkNo) && pageType == PAGETYPE_TRUNCATE) ||
(!BlockNumberIsValid(truncBlkNo) && pageType != PAGETYPE_TRUNCATE));
Assert(!RelFileNodeEquals(rNode, InvalidRelFileNode));
CBMPageTag cbmPageTag;
CbmHashEntry *cbmPageEntry = NULL;
bool found = true;
INIT_CBMPAGETAG(cbmPageTag, rNode, forkNum);
if (pageType == PAGETYPE_DROP) {
CBMHashRemove(cbmPageTag, t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash, true);
} else if (pageType == PAGETYPE_TRUNCATE &&
(((forkNum == MAIN_FORKNUM || forkNum == VISIBILITYMAP_FORKNUM) && rNode.relNode != InvalidOid) ||
(forkNum == UNDO_FORKNUM && IS_UNDO_RELFILENODE(rNode)))) {
Assert(BlockNumberIsValid(truncBlkNo));
if (IS_UNDO_RELFILENODE(rNode)) {
CBMPageEtyTruncateBefore(cbmPageTag, t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash, truncBlkNo, true);
} else {
CBMPageEtyTruncate(cbmPageTag, t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash, truncBlkNo, true);
}
}
cbmPageEntry = (CbmHashEntry *)hash_search(t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash, (void *)&cbmPageTag, HASH_ENTER,
&found);
if (cbmPageEntry == NULL)
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("could not find or create CBM page entry: rel %u/%u/%u "
"forknum %d blkno %u page type %d truncate blkno %u",
rNode.spcNode, rNode.dbNode, rNode.relNode, forkNum, blkNo, pageType, truncBlkNo)));
if (!found)
INIT_CBMPAGEENTRY(cbmPageEntry);
CBMPageEtySetBitmap(cbmPageEntry, blkNo, pageType, truncBlkNo);
}
static void CBMPageEtySetBitmap(CbmHashEntry *cbmPageEntry, BlockNumber blkNo, uint8 pageType, BlockNumber truncBlkNo)
{
BlockNumber pageFirstBlock = BLKNO_TO_CBM_PAGEFIRSTBOCK(blkNo);
CbmPageHeader *cbmPageHeader = NULL;
Dlelem *eltPagelist = NULL;
eltPagelist = FindPageElemFromEntry(cbmPageEntry, pageFirstBlock);
if (eltPagelist != NULL) {
cbmPageHeader = (CbmPageHeader *)DLE_VAL(eltPagelist);
CBMPageSetBitmap((char *)cbmPageHeader, blkNo, pageType, truncBlkNo);
return;
}
CreateNewCBMPageAndInsert(cbmPageEntry, blkNo, pageType, truncBlkNo);
}
static void CBMPageSetBitmap(char *page, BlockNumber blkNo, uint8 pageType, BlockNumber truncBlkNo)
{
Assert(page);
CbmPageHeader *cbmPageHeader = (CbmPageHeader *)page;
cbmPageHeader->pageType |= pageType;
if (BlockNumberIsValid(truncBlkNo)) {
Assert(pageType == PAGETYPE_TRUNCATE);
cbmPageHeader->truncBlkNo = truncBlkNo;
}
if (!BlockNumberIsValid(blkNo))
return;
char *bitMap = page + MAXALIGN(sizeof(CbmPageHeader));
int mapByte = BLKNO_TO_CBMBYTEOFPAGE(blkNo);
int mapBit = BLKNO_TO_CBMBITOFBYTE(blkNo);
SET_CBM_PAGE_BITMAP(bitMap[mapByte], mapBit, CBM_PAGE_CHANGED);
}
static void CreateNewCBMPageAndInsert(CbmHashEntry *cbmPageEntry, BlockNumber blkNo, uint8 pageType,
BlockNumber truncBlkNo)
{
CbmPageHeader *cbmPageHeader = NULL;
BlockNumber pageFirstBlock = BLKNO_TO_CBM_PAGEFIRSTBOCK(blkNo);
Dlelem *elt = NULL;
if (DLGetHead(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList)) {
int rc = 0;
elt = DLRemHead(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList);
cbmPageHeader = (CbmPageHeader *)DLE_VAL(elt);
Assert(cbmPageHeader);
rc = memset_s(cbmPageHeader, CBMPAGESIZE, 0, CBMPAGESIZE);
securec_check(rc, "\0", "\0");
} else {
/* For now, only cbm writer thread can do cbm-parsing-xlog */
Assert(CurrentMemoryContext == t_thrd.cbm_cxt.cbmwriter_context);
MemoryContextSwitchTo(t_thrd.cbm_cxt.cbmwriter_page_context);
cbmPageHeader = (CbmPageHeader *)palloc_extended(CBMPAGESIZE, MCXT_ALLOC_NO_OOM | MCXT_ALLOC_ZERO);
if (cbmPageHeader == NULL)
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate new CBM page")));
elt = DLNewElem((void *)cbmPageHeader);
MemoryContextSwitchTo(t_thrd.cbm_cxt.cbmwriter_context);
}
INIT_CBMPAGEHEADER(cbmPageHeader, cbmPageEntry->cbmTag, pageFirstBlock);
CBMPageSetBitmap((char *)cbmPageHeader, blkNo, pageType, truncBlkNo);
InsertCbmPageElemToEntry(cbmPageEntry, elt, pageFirstBlock);
t_thrd.cbm_cxt.XlogCbmSys->totalPageNum++;
ereport(DEBUG1, (errmsg("create new CBM page: rel %u/%u/%u forknum %d first blkno %u "
"page type %d truncate blkno %u",
cbmPageHeader->rNode.spcNode, cbmPageHeader->rNode.dbNode, cbmPageHeader->rNode.relNode,
cbmPageHeader->forkNum, cbmPageHeader->firstBlkNo, cbmPageHeader->pageType,
cbmPageHeader->truncBlkNo)));
}
static void CreateDummyCBMEtyPageAndInsert(void)
{
CBMPageTag dummyPageTag;
CbmHashEntry *dummyPageEntry = NULL;
bool found = false;
INIT_DUMMYCBMPAGETAG(dummyPageTag);
dummyPageEntry = (CbmHashEntry *)hash_search(t_thrd.cbm_cxt.XlogCbmSys->cbmPageHash, (void *)&dummyPageTag,
HASH_ENTER, &found);
if (dummyPageEntry == NULL)
ereport(ERROR,
(errcode(ERRCODE_FETCH_DATA_FAILED), errmsg("could not create dummy CBM page entry: rel %u/%u/%u "
"forknum %d",
dummyPageTag.rNode.spcNode, dummyPageTag.rNode.dbNode,
dummyPageTag.rNode.relNode, dummyPageTag.forkNum)));
Assert(!found);
INIT_CBMPAGEENTRY(dummyPageEntry);
CreateNewCBMPageAndInsert(dummyPageEntry, InvalidBlockNumber, PAGETYPE_MODIFY, InvalidBlockNumber);
}
static void FlushCBMPagesToDisk(XlogBitmap *xlogCbmSys, bool isCBMWriter)
{
HASH_SEQ_STATUS status;
CbmHashEntry *cbmPageEntry = NULL;
hash_seq_init(&status, xlogCbmSys->cbmPageHash);
while ((cbmPageEntry = (CbmHashEntry *)hash_seq_search(&status)) != NULL) {
Dlelem *eltCbmSegPageList = NULL;
Dlelem *eltPagelist = NULL;
int pageNum = cbmPageEntry->pageNum;
Assert(pageNum);
CbmPageHeader **cbmPageHeaderArray = (CbmPageHeader **)palloc_extended(pageNum * sizeof(CbmPageHeader *),
MCXT_ALLOC_NO_OOM | MCXT_ALLOC_ZERO);
if (cbmPageHeaderArray == NULL)
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate CBM page header array")));
while (ForeachEntryForPage(cbmPageEntry, &eltCbmSegPageList, &eltPagelist)) {
cbmPageHeaderArray[--pageNum] = (CbmPageHeader *)DLE_VAL(eltPagelist);
Assert(cbmPageHeaderArray[pageNum]);
}
Assert(!pageNum);
qsort(cbmPageHeaderArray, cbmPageEntry->pageNum, sizeof(CbmPageHeader *), CBMPageSeqCmp);
for (pageNum = 0; pageNum < cbmPageEntry->pageNum; pageNum++) {
FlushOneCBMPage((char *)(cbmPageHeaderArray[pageNum]), xlogCbmSys);
ereport(DEBUG1,
(errmsg("flush CBM page: rel %u/%u/%u forknum %d first blkno %u "
"page type %d truncate blkno %u",
cbmPageHeaderArray[pageNum]->rNode.spcNode, cbmPageHeaderArray[pageNum]->rNode.dbNode,
cbmPageHeaderArray[pageNum]->rNode.relNode, cbmPageHeaderArray[pageNum]->forkNum,
cbmPageHeaderArray[pageNum]->firstBlkNo, cbmPageHeaderArray[pageNum]->pageType,
cbmPageHeaderArray[pageNum]->truncBlkNo)));
}
pfree(cbmPageHeaderArray);
DestoryCbmHashEntry(cbmPageEntry, isCBMWriter, xlogCbmSys, false, NULL);
if (hash_search(xlogCbmSys->cbmPageHash, (void *)&cbmPageEntry->cbmTag, HASH_REMOVE, NULL) == NULL)
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("CBM hash table corrupted")));
}
Assert(xlogCbmSys->totalPageNum == 0);
if (pg_fsync(xlogCbmSys->out.fd) != 0)
ereport(ERROR, (errcode_for_file_access(),
errmsg("fsync CBM file \"%s\" failed during flushing", xlogCbmSys->out.name)));
if (isCBMWriter && (xlogCbmSys->out.offset >= MAXCBMFILESIZE || checkUserRequstAndRotateCbm())) {
RotateCBMFile();
}
}
static int CBMPageSeqCmp(const void *a, const void *b)
{
CbmPageHeader *pha = *((CbmPageHeader **)a);
CbmPageHeader *phb = *((CbmPageHeader **)b);
return CBM_BLOCKNO_CMP(pha->firstBlkNo, phb->firstBlkNo);
}
static void FlushOneCBMPage(const char *page, XlogBitmap *xlogCbmSys)
{
CbmPageHeader *cbmPageHeader = (CbmPageHeader *)page;
cbmPageHeader->isLastBlock = (xlogCbmSys->totalPageNum == 1 ? true : false);
cbmPageHeader->pageStartLsn = xlogCbmSys->startLSN;
cbmPageHeader->pageEndLsn = xlogCbmSys->endLSN;
cbmPageHeader->pageCrc = CBMPageCalcCRC(page);
PGSTAT_INIT_TIME_RECORD();
PGSTAT_START_TIME_RECORD();
ssize_t size = pwrite(xlogCbmSys->out.fd, page, (size_t)CBMPAGESIZE, xlogCbmSys->out.offset);
PGSTAT_END_TIME_RECORD(DATA_IO_TIME);
if (size != (ssize_t)CBMPAGESIZE)
ereport(ERROR, (errcode_for_file_access(), errmsg("could not write CBM file \"%s\", page offset %ld",
xlogCbmSys->out.name, xlogCbmSys->out.offset)));
xlogCbmSys->out.offset += (off_t)CBMPAGESIZE;
xlogCbmSys->totalPageNum--;
}
static void RotateCBMFile(void)
{
char prefix[MAXPGPATH];
char filePath[MAXPGPATH];
uint64 seqNum;
XLogRecPtr startLSN;
XLogRecPtr endLSN;
uint32 startLSN_hi = 0;
uint32 startLSN_lo = 0;
uint32 endLSN_hi = 0;
uint32 endLSN_lo = 0;
Assert(t_thrd.cbm_cxt.XlogCbmSys->out.fd >= 0);
if (close(t_thrd.cbm_cxt.XlogCbmSys->out.fd) != 0)
ereport(ERROR, (errcode_for_file_access(),
errmsg("close CBM file \"%s\" failed during rotate", t_thrd.cbm_cxt.XlogCbmSys->out.name)));
if (strncmp(t_thrd.cbm_cxt.XlogCbmSys->out.name, t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome,
strlen(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome)) ||
sscanf_s(t_thrd.cbm_cxt.XlogCbmSys->out.name + strlen(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome),
"%[a-z_]%lu_%8X%8X_%8X%8X.cbm", prefix, MAXPGPATH - 1, &seqNum, &startLSN_hi, &startLSN_lo, &endLSN_hi,
&endLSN_lo) != 6) {
ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("found invalid CBM file name\"%s\" before rotate",
t_thrd.cbm_cxt.XlogCbmSys->out.name)));
}
startLSN = (((uint64)startLSN_hi) << 32) | startLSN_lo;
endLSN = (((uint64)endLSN_hi) << 32) | endLSN_lo;
Assert(XLByteLT(startLSN, t_thrd.cbm_cxt.XlogCbmSys->endLSN));
Assert(XLogRecPtrIsInvalid(endLSN));
Assert(seqNum == t_thrd.cbm_cxt.XlogCbmSys->outSeqNum);
SetCBMFileName(filePath, seqNum, startLSN, t_thrd.cbm_cxt.XlogCbmSys->endLSN);
if (durable_rename(t_thrd.cbm_cxt.XlogCbmSys->out.name, filePath, ERROR))
ereport(ERROR, (errcode_for_file_access(), errmsg("could not rename file \"%s\" to \"%s\" during rotate: %m",
t_thrd.cbm_cxt.XlogCbmSys->out.name, filePath)));
t_thrd.cbm_cxt.XlogCbmSys->outSeqNum++;
StartNextCBMFile(t_thrd.cbm_cxt.XlogCbmSys->endLSN);
}
static void RemoveAllCBMFiles(int elevel)
{
if (!rmtree(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, false, true))
ereport(elevel, (errmsg("Failed to remove all CBM files")));
}
static void PrintCBMHashTab(HTAB *cbmPageHash)
{
int log_level = DEBUG1;
if (u_sess->attr.attr_common.log_min_messages < log_level) {
return;
}
HASH_SEQ_STATUS status;
CbmHashEntry *cbmPageEntry = NULL;
hash_seq_init(&status, cbmPageHash);
while ((cbmPageEntry = (CbmHashEntry *)hash_seq_search(&status)) != NULL) {
Dlelem *eltCbmSegPageList = NULL;
Dlelem *eltPagelist = NULL;
CbmPageHeader *cbmPageHeader = NULL;
int pageNum = cbmPageEntry->pageNum;
while (ForeachEntryForPage(cbmPageEntry, &eltCbmSegPageList, &eltPagelist)) {
cbmPageHeader = (CbmPageHeader *)DLE_VAL(eltPagelist);
ereport(log_level, (errmsg("print CBM page info: rel %u/%u/%u forknum %d first blkno %u "
"page type %d truncate blkno %u",
cbmPageHeader->rNode.spcNode, cbmPageHeader->rNode.dbNode,
cbmPageHeader->rNode.relNode, cbmPageHeader->forkNum, cbmPageHeader->firstBlkNo,
cbmPageHeader->pageType, cbmPageHeader->truncBlkNo)));
pageNum--;
}
Assert(!pageNum);
}
}
void CBMGetMergedFile(XLogRecPtr startLSN, XLogRecPtr endLSN, char *mergedFileName)
{
XlogBitmap mergedXlogCbmSys;
FILE *mergeFile = NULL;
int rc;
mergedXlogCbmSys.cbmPageHash = CBMPageHashInitialize(CurrentMemoryContext);
CBMGetMergedHash(startLSN, endLSN, mergedXlogCbmSys.cbmPageHash, &mergedXlogCbmSys.startLSN,
&mergedXlogCbmSys.endLSN);
mergedXlogCbmSys.totalPageNum = GetCBMHashTotalPageNum(mergedXlogCbmSys.cbmPageHash);
PrintCBMHashTab(mergedXlogCbmSys.cbmPageHash);
mergeFile = MergedXlogCBMSysInitFile(&mergedXlogCbmSys);
FlushCBMPagesToDisk(&mergedXlogCbmSys, false);
if (FreeFile(mergeFile))
ereport(WARNING, (errcode_for_file_access(),
errmsg("could not close merged CBM file \"%s\": %m", mergedXlogCbmSys.out.name)));
hash_destroy(mergedXlogCbmSys.cbmPageHash);
rc = strncpy_s(mergedFileName, MAXPGPATH, mergedXlogCbmSys.out.name, strlen(mergedXlogCbmSys.out.name));
securec_check(rc, "\0", "\0");
}
static void CBMGetMergedHash(XLogRecPtr startLSN, XLogRecPtr endLSN, HTAB *cbmPageHash, XLogRecPtr *mergeStartLSN,
XLogRecPtr *mergeEndLSN)
{
int cbmFileNum = 0;
CbmFileName **cbmFileNameArray;
cbmFileNameArray = GetAndValidateCBMFileArray(startLSN, endLSN, &cbmFileNum);
MergeCBMFileArrayIntoHash(cbmFileNameArray, cbmFileNum, startLSN, endLSN, cbmPageHash, mergeStartLSN, mergeEndLSN);
FreeCBMFileArray(cbmFileNameArray, cbmFileNum);
}
static CbmFileName **GetAndValidateCBMFileArray(XLogRecPtr startLSN, XLogRecPtr endLSN, int *fileNum)
{
int cbmFileNum = 0;
CbmFileName **cbmFileNameArray;
cbmFileNameArray = GetCBMFileArray(startLSN, endLSN, &cbmFileNum, false);
PrintCBMFileArray(cbmFileNameArray, cbmFileNum, startLSN, endLSN);
ValidateCBMFileArray(cbmFileNameArray, cbmFileNum, startLSN, endLSN);
*fileNum = cbmFileNum;
return cbmFileNameArray;
}
static CbmFileName **GetCBMFileArray(XLogRecPtr startLSN, XLogRecPtr endLSN, int *fileNum, bool missingOk)
{
struct dirent *cbmde = NULL;
Dllist *cbmSegPageList = NULL;
int cbmFileNum = 0;
Dlelem *elt = NULL;
CbmFileName **cbmFileNameArray = NULL;
DIR *cbmdir = AllocateDir(t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome);
if (cbmdir == NULL) {
ereport(ERROR, (errcode_for_file_access(), errmsg("could not open CBM file directory \"%s\": %m",
t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome)));
}
cbmSegPageList = DLNewList();
while ((cbmde = ReadDir(cbmdir, t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome)) != NULL) {
uint64 fileSeqNum = 0;
XLogRecPtr fileStartLSN = InvalidXLogRecPtr;
XLogRecPtr fileEndLSN = InvalidXLogRecPtr;
/* Ignore files that are not CBM files. */
if (!IsCBMFile(cbmde->d_name, &fileSeqNum, &fileStartLSN, &fileEndLSN)) {
continue;
}
Assert(!XLogRecPtrIsInvalid(fileStartLSN));
Assert(XLByteLT(fileStartLSN, fileEndLSN) || XLogRecPtrIsInvalid(fileEndLSN));
if ((XLByteLE(fileStartLSN, startLSN) && (XLByteLT(startLSN, fileEndLSN) || XLogRecPtrIsInvalid(fileEndLSN))) ||
(XLByteLE(startLSN, fileStartLSN) && !XLogRecPtrIsInvalid(fileEndLSN) && (XLByteLE(fileEndLSN, endLSN))) ||
(XLByteLT(fileStartLSN, endLSN) && (XLByteLE(endLSN, fileEndLSN) || XLogRecPtrIsInvalid(fileEndLSN)))) {
CbmFileName *cbmFileName = (CbmFileName *)palloc0(sizeof(CbmFileName));
INIT_CBMFILENAME(cbmFileName, cbmde->d_name, fileSeqNum, fileStartLSN, fileEndLSN);
elt = DLNewElem((void *)cbmFileName);
DLAddHead(cbmSegPageList, elt);
cbmFileNum++;
}
}
(void)FreeDir(cbmdir);
*fileNum = cbmFileNum;
if (cbmFileNum == 0) {
if (missingOk) {
DLFreeList(cbmSegPageList);
return NULL;
} else {
ereport(ERROR,
(errcode(ERRCODE_FILE_READ_FAILED),
errmsg("could not find valid CBM file between %08X/%08X and "
"%08X/%08X, which may be caused by previous CBM switch-off, "
"truncation, or corruption",
(uint32)(startLSN >> 32), (uint32)startLSN, (uint32)(endLSN >> 32), (uint32)endLSN)));
}
}
cbmFileNameArray = SortCBMFilesList(cbmSegPageList, cbmFileNum);
DLFreeList(cbmSegPageList);
return cbmFileNameArray;
}
static CbmFileName **SortCBMFilesList(Dllist *cbmSegPageList, int cbmFileNum)
{
CbmFileName **cbmFileNameArray = (CbmFileName **)palloc(cbmFileNum * sizeof(CbmFileName *));
Dlelem *elt = NULL;
int fileNum = cbmFileNum;
for (elt = DLGetHead(cbmSegPageList); elt; elt = DLGetSucc(elt)) {
cbmFileNameArray[--fileNum] = (CbmFileName *)DLE_VAL(elt);
Assert(cbmFileNameArray[fileNum]);
}
Assert(!fileNum);
qsort(cbmFileNameArray, cbmFileNum, sizeof(CbmFileName *), CBMFileNameSeqCmp);
return cbmFileNameArray;
}
static int CBMFileNameSeqCmp(const void *a, const void *b)
{
CbmFileName *fna = *((CbmFileName **)a);
CbmFileName *fnb = *((CbmFileName **)b);
return fna->seqNum < fnb->seqNum ? -1 : (fna->seqNum > fnb->seqNum ? 1 : 0);
}
static void PrintCBMFileArray(CbmFileName **cbmFileNameArray, int cbmFileNum, XLogRecPtr startLSN, XLogRecPtr endLSN)
{
int log_level = DEBUG1;
if (u_sess->attr.attr_common.log_min_messages < log_level) {
return;
}
int i;
StringInfo log = makeStringInfo();
for (i = 0; i < cbmFileNum; i++) {
appendStringInfo(log, " %s", cbmFileNameArray[i]->name);
}
ereport(DEBUG1,
(errmsg("CBM file list for merging between %08X/%08X and "
"%08X/%08X is:%s",
(uint32)(startLSN >> 32), (uint32)startLSN, (uint32)(endLSN >> 32), (uint32)endLSN, log->data)));
pfree(log->data);
pfree(log);
}
static void ValidateCBMFileArray(CbmFileName **cbmFileNameArray, int cbmFileNum, XLogRecPtr startLSN, XLogRecPtr endLSN)
{
uint64 lastfileSize = 0;
XLogRecPtr lastfileTrackedLSN = InvalidXLogRecPtr;
int i;
if (XLByteLT(startLSN, cbmFileNameArray[0]->startLSN)) {
ereport(ERROR,
(errcode(ERRCODE_FILE_READ_FAILED), errmsg("could not find valid CBM file that contains the merging "
"start point %08X/%08X",
(uint32)(startLSN >> 32), (uint32)startLSN)));
}
ValidateCBMFile(cbmFileNameArray[cbmFileNum - 1]->name, &lastfileTrackedLSN, &lastfileSize, false);
if (XLByteLT(lastfileTrackedLSN, endLSN)) {
ereport(ERROR,
(errcode(ERRCODE_FILE_READ_FAILED), errmsg("could not find valid CBM file that contains the merging "
"end point %08X/%08X",
(uint32)(endLSN >> 32), (uint32)endLSN)));
}
if (cbmFileNum == 1) {
return;
}
for (i = 1; i < cbmFileNum; i++) {
if (!XLByteEQ(cbmFileNameArray[i - 1]->endLSN, cbmFileNameArray[i]->startLSN)) {
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("there is a gap between CBM file %s and %s, which "
"may be caused by previous CBM switch-off, truncation, or "
"corruption",
cbmFileNameArray[i - 1]->name, cbmFileNameArray[i]->name)));
}
}
}
static void MergeCBMFileArrayIntoHash(CbmFileName **cbmFileNameArray, int cbmFileNum, XLogRecPtr startLSN,
XLogRecPtr endLSN, HTAB *cbmPageHash, XLogRecPtr *mergeStartLSN,
XLogRecPtr *mergeEndLSN)
{
int i;
FILE *file = NULL;
char *page = NULL;
char filePath[MAXPGPATH];
XLogRecPtr mergeStartPos = InvalidXLogRecPtr;
XLogRecPtr mergeEndPos = InvalidXLogRecPtr;
int rc;
page = (char *)palloc_extended(CBMPAGESIZE, MCXT_ALLOC_NO_OOM);
if (page == NULL)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("memory is temporarily unavailable while allocate "
"page read buffer during merge CBM file array")));
for (i = 0; i < cbmFileNum; i++) {
cbmPageIterator pageIterator;
rc = snprintf_s(filePath, MAXPGPATH, MAXPGPATH - 1, "%s%s", t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome,
cbmFileNameArray[i]->name);
securec_check_ss(rc, "\0", "\0");
file = AllocateFile(filePath, PG_BINARY_R);
if (file == NULL)
ereport(ERROR,
(errcode_for_file_access(), errmsg("could not open CBM file \"%s\" while merging: %m", filePath)));
ereport(DEBUG1, (errmsg("start iterating through CBM file \"%s\"", filePath)));
INIT_CBMPAGEITERATOR(pageIterator, file, page);
CBMPageIterBegin(&pageIterator, cbmFileNameArray[i]);
while (CBMPageIterNext(&pageIterator, cbmFileNameArray[i])) {
if (XLByteLE(pageIterator.pageEndLsn, startLSN)) {
Assert(i == 0);
continue;
}
MergeCBMPageIntoHash(pageIterator.buffer, cbmPageHash);
if (pageIterator.isLastBlock) {
if (XLByteLE(pageIterator.pageStartLsn, startLSN)) {
Assert(i == 0);
Assert(XLogRecPtrIsInvalid(mergeStartPos));
mergeStartPos = pageIterator.pageStartLsn;
}
if (XLByteLE(endLSN, pageIterator.pageEndLsn)) {
Assert(i == (cbmFileNum - 1));
Assert(XLogRecPtrIsInvalid(mergeEndPos));
mergeEndPos = pageIterator.pageEndLsn;
}
}
if (!XLogRecPtrIsInvalid(mergeEndPos))
break;
}
if (FreeFile(file))
ereport(WARNING, (errcode_for_file_access(), errmsg("could not close CBM file \"%s\": %m", filePath)));
if (XLogRecPtrIsInvalid(mergeEndPos))
CBMPageIterEnd(&pageIterator, cbmFileNameArray[i]);
if (i == 0 && XLogRecPtrIsInvalid(mergeStartPos))
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED), errmsg("could not find merge start point %08X/%08X "
"in CBM files ",
(uint32)(startLSN >> 32), (uint32)startLSN)));
if (i == (cbmFileNum - 1) && XLogRecPtrIsInvalid(mergeEndPos) &&
XLByteLT((mergeEndPos = pageIterator.pageEndLsn), endLSN))
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("could not find merge end point %08X/%08X "
"in CBM files, the last cbm page end at %08X/%08X",
(uint32)(endLSN >> 32), (uint32)endLSN, (uint32)(pageIterator.pageEndLsn >> 32),
(uint32)pageIterator.pageEndLsn)));
}
if (mergeStartLSN != NULL)
*mergeStartLSN = mergeStartPos;
if (mergeEndLSN != NULL)
*mergeEndLSN = mergeEndPos;
pfree(page);
}
static void CBMPageIterBegin(cbmPageIterator *pageIteratorPtr, CbmFileName *cbmFile)
{
ssize_t readLen;
Assert(pageIteratorPtr->readOffset == (off_t)0);
PGSTAT_INIT_TIME_RECORD();
PGSTAT_START_TIME_RECORD();
readLen = pread(fileno(pageIteratorPtr->file), pageIteratorPtr->buffer, (size_t)sizeof(cbmpageheader),
pageIteratorPtr->readOffset);
PGSTAT_END_TIME_RECORD(DATA_IO_TIME);
if (readLen != (ssize_t)sizeof(cbmpageheader))
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("could not read the first page head of CBM file \"%s\" ", cbmFile->name)));
pageIteratorPtr->pageStartLsn = ((cbmpageheader *)(pageIteratorPtr->buffer))->pageStartLsn;
if (!XLByteEQ(pageIteratorPtr->pageStartLsn, cbmFile->startLSN))
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("the first page start LSN %08X/%08X of CBM file \"%s\" "
"does not equal the file start LSN %08X/%08X",
(uint32)(pageIteratorPtr->pageStartLsn >> 32), (uint32)pageIteratorPtr->pageStartLsn,
cbmFile->name, (uint32)(cbmFile->startLSN >> 32), (uint32)cbmFile->startLSN)));
pageIteratorPtr->pageEndLsn = ((cbmpageheader *)(pageIteratorPtr->buffer))->pageEndLsn;
pageIteratorPtr->isLastBlock = false;
}
static bool CBMPageIterNext(cbmPageIterator *pageIteratorPtr, CbmFileName *cbmFile)
{
pg_crc32c checksum;
pg_crc32c actual_checksum;
ssize_t readLen;
Assert(pageIteratorPtr->readOffset % (off_t)CBMPAGESIZE == (off_t)0);
PGSTAT_INIT_TIME_RECORD();
PGSTAT_START_TIME_RECORD();
readLen = pread(fileno(pageIteratorPtr->file), pageIteratorPtr->buffer, (size_t)CBMPAGESIZE,
pageIteratorPtr->readOffset);
PGSTAT_END_TIME_RECORD(DATA_IO_TIME);
if (readLen != (ssize_t)CBMPAGESIZE) {
if (readLen == (ssize_t)0)
ereport(DEBUG1, (errmsg("reach end at page offset %ld of CBM file \"%s\", stop reading",
pageIteratorPtr->readOffset, cbmFile->name)));
else
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED),
errmsg("partial page read occurs at page offset %ld of CBM file \"%s\", "
"stop reading",
pageIteratorPtr->readOffset, cbmFile->name)));
return false;
}
checksum = ((cbmpageheader *)(pageIteratorPtr->buffer))->pageCrc;
actual_checksum = CBMPageCalcCRC(pageIteratorPtr->buffer);
pageIteratorPtr->checksumOk = (checksum == actual_checksum);
if (!pageIteratorPtr->checksumOk)
ereport(ERROR,
(errcode(ERRCODE_FILE_READ_FAILED), errmsg("Corruption detected in CBM file \"%s\", page offset %ld",
cbmFile->name, pageIteratorPtr->readOffset)));
pageIteratorPtr->prevStartLsn = pageIteratorPtr->pageStartLsn;
pageIteratorPtr->prevEndLsn = pageIteratorPtr->pageEndLsn;
pageIteratorPtr->isPrevLastBlock = pageIteratorPtr->isLastBlock;
pageIteratorPtr->pageStartLsn = ((cbmpageheader *)(pageIteratorPtr->buffer))->pageStartLsn;
pageIteratorPtr->pageEndLsn = ((cbmpageheader *)(pageIteratorPtr->buffer))->pageEndLsn;
pageIteratorPtr->isLastBlock = ((cbmpageheader *)(pageIteratorPtr->buffer))->isLastBlock;
if (pageIteratorPtr->isPrevLastBlock) {
if (!XLByteEQ(pageIteratorPtr->pageStartLsn, pageIteratorPtr->prevEndLsn))
ereport(ERROR,
(errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("LSN track gap detected in CBM file \"%s\", page offset %ld: "
"previous page batch end LSN is %08X/%08X, current page batch start "
"LSN is %08X/%08X",
cbmFile->name, pageIteratorPtr->readOffset, (uint32)(pageIteratorPtr->prevEndLsn >> 32),
(uint32)pageIteratorPtr->prevEndLsn, (uint32)(pageIteratorPtr->pageStartLsn >> 32),
(uint32)pageIteratorPtr->pageStartLsn)));
} else {
if (!XLByteEQ(pageIteratorPtr->pageStartLsn, pageIteratorPtr->prevStartLsn) ||
!XLByteEQ(pageIteratorPtr->pageEndLsn, pageIteratorPtr->prevEndLsn))
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("Inconsistent start/end LSN in one page batch for CBM file "
"\"%s\" at page offset %ld: previous page start-end LSN %08X/%08X-"
"%08X/%08X, curent page start-end LSN %08X/%08X-%08X/%08X",
cbmFile->name, pageIteratorPtr->readOffset,
(uint32)(pageIteratorPtr->prevStartLsn >> 32), (uint32)pageIteratorPtr->prevStartLsn,
(uint32)(pageIteratorPtr->prevEndLsn >> 32), (uint32)pageIteratorPtr->prevEndLsn,
(uint32)(pageIteratorPtr->pageStartLsn >> 32), (uint32)pageIteratorPtr->pageStartLsn,
(uint32)(pageIteratorPtr->pageEndLsn >> 32), (uint32)pageIteratorPtr->pageEndLsn)));
}
pageIteratorPtr->readOffset += (off_t)CBMPAGESIZE;
return true;
}
static void CBMPageIterEnd(cbmPageIterator *pageIteratorPtr, CbmFileName *cbmFile)
{
if (!XLogRecPtrIsInvalid(cbmFile->endLSN) && !XLByteEQ(pageIteratorPtr->pageEndLsn, cbmFile->endLSN))
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("the last read page end LSN %08X/%08X of CBM file \"%s\" "
"does not equal the file end LSN %08X/%08X",
(uint32)(pageIteratorPtr->pageEndLsn >> 32), (uint32)pageIteratorPtr->pageEndLsn,
cbmFile->name, (uint32)(cbmFile->endLSN >> 32), (uint32)cbmFile->endLSN)));
}
static void MergeCBMPageIntoHash(const char *page, HTAB *cbmPageHash)
{
CbmPageHeader *cbmPageHeader = (cbmpageheader *)page;
CBMPageTag cbmPageTag;
CbmHashEntry *cbmPageEntry = NULL;
bool found = true;
if (CBM_PAGE_IS_DUMMY(cbmPageHeader)) {
Assert(cbmPageHeader->firstBlkNo == InvalidBlockNumber);
Assert(cbmPageHeader->isLastBlock);
ereport(DEBUG1, (errmsg("reach a dummy page for LSN range %08X/%08X to "
"%08X/%08X, so skip",
(uint32)(cbmPageHeader->pageStartLsn >> 32), (uint32)cbmPageHeader->pageStartLsn,
(uint32)(cbmPageHeader->pageEndLsn >> 32), (uint32)cbmPageHeader->pageEndLsn)));
return;
}
ValidateCBMPageHeader(cbmPageHeader);
INIT_CBMPAGETAG(cbmPageTag, cbmPageHeader->rNode, cbmPageHeader->forkNum);
if (cbmPageHeader->pageType & PAGETYPE_DROP) {
Assert(!BlockNumberIsValid(cbmPageHeader->firstBlkNo));
CBMHashRemove(cbmPageTag, cbmPageHash, false);
}
if (cbmPageHeader->pageType & PAGETYPE_TRUNCATE) {
Assert(!BlockNumberIsValid(cbmPageHeader->firstBlkNo));
Assert(BlockNumberIsValid(cbmPageHeader->truncBlkNo));
Assert(cbmPageHeader->forkNum == MAIN_FORKNUM || cbmPageHeader->forkNum == FSM_FORKNUM ||
cbmPageHeader->forkNum == VISIBILITYMAP_FORKNUM || cbmPageHeader->forkNum == UNDO_FORKNUM);
if (IS_UNDO_RELFILENODE(cbmPageHeader->rNode)) {
CBMPageEtyTruncateBefore(cbmPageTag, cbmPageHash, cbmPageHeader->truncBlkNo, false);
} else if ((cbmPageHeader->forkNum == MAIN_FORKNUM || cbmPageHeader->forkNum == VISIBILITYMAP_FORKNUM) &&
cbmPageHeader->rNode.relNode != InvalidOid) {
CBMPageEtyTruncate(cbmPageTag, cbmPageHash, cbmPageHeader->truncBlkNo, false);
}
}
cbmPageEntry = (CbmHashEntry *)hash_search(cbmPageHash, (void *)&cbmPageTag, HASH_ENTER, &found);
if (cbmPageEntry == NULL)
ereport(ERROR,
(errcode(ERRCODE_FILE_READ_FAILED), errmsg("could not find or create CBM page entry: rel %u/%u/%u "
"forknum %d during merge into hash",
cbmPageTag.rNode.spcNode, cbmPageTag.rNode.dbNode,
cbmPageTag.rNode.relNode, cbmPageTag.forkNum)));
if (!found)
INIT_CBMPAGEENTRY(cbmPageEntry);
CBMPageEtyMergePage(cbmPageEntry, page);
}
static void ValidateCBMPageHeader(cbmpageheader *cbmPageHeader)
{
uint8 pageType = cbmPageHeader->pageType;
XLogRecPtr pageStartLsn = cbmPageHeader->pageStartLsn;
XLogRecPtr pageEndLsn = cbmPageHeader->pageEndLsn;
RelFileNode rNode = cbmPageHeader->rNode;
BlockNumber firstBlkNo = cbmPageHeader->firstBlkNo;
BlockNumber truncBlkNo = cbmPageHeader->truncBlkNo;
if (XLByteLE(pageEndLsn, pageStartLsn) || RelFileNodeEquals(rNode, InvalidRelFileNode) ||
(BlockNumberIsValid(firstBlkNo) && pageType != PAGETYPE_MODIFY) ||
(!BlockNumberIsValid(firstBlkNo) &&
!((pageType & PAGETYPE_DROP) || (pageType & PAGETYPE_TRUNCATE) || (pageType & PAGETYPE_CREATE))) ||
(BlockNumberIsValid(truncBlkNo) && !(pageType & PAGETYPE_TRUNCATE)) ||
(!BlockNumberIsValid(truncBlkNo) && (pageType & PAGETYPE_TRUNCATE)))
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("invalid CBM page header: rel %u/%u/%u forknum %d "
"first blkno %u page type %d truncate blkno %u",
cbmPageHeader->rNode.spcNode, cbmPageHeader->rNode.dbNode, cbmPageHeader->rNode.relNode,
cbmPageHeader->forkNum, cbmPageHeader->firstBlkNo, cbmPageHeader->pageType,
cbmPageHeader->truncBlkNo)));
}
static void CBMHashRemove(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter)
{
Assert(cbmPageTag.rNode.spcNode != InvalidOid);
if (cbmPageTag.rNode.relNode != InvalidOid) {
CBMPageEtyRemove(cbmPageTag, cbmPageHash, isCBMWriter, false);
if (cbmPageTag.forkNum == MAIN_FORKNUM) {
CBMPageEtyRemoveRestFork(cbmPageTag, cbmPageHash, isCBMWriter);
}
} else if (cbmPageTag.rNode.dbNode != InvalidOid) {
CBMHashRemoveDb(cbmPageTag, cbmPageHash, isCBMWriter);
} else {
CBMHashRemoveTblspc(cbmPageTag, cbmPageHash, isCBMWriter);
}
}
static void CBMHashRemoveDb(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter)
{
HASH_SEQ_STATUS status;
CbmHashEntry *cbmPageEntry = NULL;
Assert(cbmPageTag.rNode.spcNode != InvalidOid && cbmPageTag.rNode.dbNode != InvalidOid &&
cbmPageTag.rNode.relNode == InvalidOid);
hash_seq_init(&status, cbmPageHash);
while ((cbmPageEntry = (CbmHashEntry *)hash_seq_search(&status)) != NULL) {
if (cbmPageEntry->cbmTag.rNode.spcNode == cbmPageTag.rNode.spcNode &&
cbmPageEntry->cbmTag.rNode.dbNode == cbmPageTag.rNode.dbNode)
CBMPageEtyRemove(cbmPageEntry->cbmTag, cbmPageHash, isCBMWriter, true);
}
}
static void CBMHashRemoveTblspc(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter)
{
HASH_SEQ_STATUS status;
CbmHashEntry *cbmPageEntry = NULL;
Assert(cbmPageTag.rNode.spcNode != InvalidOid && cbmPageTag.rNode.dbNode == InvalidOid &&
cbmPageTag.rNode.relNode == InvalidOid);
hash_seq_init(&status, cbmPageHash);
while ((cbmPageEntry = (CbmHashEntry *)hash_seq_search(&status)) != NULL) {
if (cbmPageEntry->cbmTag.rNode.spcNode == cbmPageTag.rNode.spcNode)
CBMPageEtyRemove(cbmPageEntry->cbmTag, cbmPageHash, isCBMWriter, true);
}
}
static void CBMPageEtyRemove(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter, bool removeEntry)
{
CbmHashEntry *cbmPageEntry = NULL;
bool found = true;
cbmPageEntry = (CbmHashEntry *)hash_search(cbmPageHash, (void *)&cbmPageTag, HASH_FIND, &found);
if (!found) {
return;
}
Assert(cbmPageEntry != NULL);
StringInfo log = makeStringInfo();
appendStringInfo(log, "remove all cbm pages of rel %u/%u/%u forknum %d", cbmPageTag.rNode.spcNode,
cbmPageTag.rNode.dbNode, cbmPageTag.rNode.relNode, cbmPageTag.forkNum);
DestoryCbmHashEntry(cbmPageEntry, isCBMWriter, t_thrd.cbm_cxt.XlogCbmSys, true, log);
if (removeEntry && hash_search(cbmPageHash, (void *)&cbmPageTag, HASH_REMOVE, NULL) == NULL) {
ereport(ERROR, (errcode(ERRCODE_FILE_READ_FAILED), errmsg("CBM hash table corrupted")));
}
ereport(DEBUG1, (errmsg("Remove CBM hash entry: %s", log->data)));
pfree(log->data);
pfree(log);
}
static void CBMPageEtyRemoveRestFork(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, bool isCBMWriter)
{
int tmpForkNum;
CBMPageTag tmpTag = cbmPageTag;
for (tmpForkNum = FSM_FORKNUM; tmpForkNum <= MAX_FORKNUM; tmpForkNum++) {
tmpTag.forkNum = tmpForkNum;
CBMPageEtyRemove(tmpTag, cbmPageHash, isCBMWriter, true);
}
}
/* remove all cbm pages and clear all bits after (truncBlkNo - 1) */
static void CBMPageEtyTruncate(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, BlockNumber truncBlkNo,
bool isCBMWriter)
{
CbmHashEntry *cbmPageEntry = NULL;
CbmPageHeader *cbmPageHeader = NULL;
Dlelem *eltCbmSegPageList = NULL;
Dlelem *eltPagelist = NULL;
Dlelem *neltCbmSegPageList = NULL;
Dlelem *neltPagelist = NULL;
CbmSegPageList *cbmSegPageList = NULL;
bool found = true;
cbmPageEntry = (CbmHashEntry *)hash_search(cbmPageHash, (void *)&cbmPageTag, HASH_FIND, &found);
if (!found) {
return;
}
Assert(cbmPageEntry != NULL);
BlockNumber resPageFirstBlkNo;
StringInfo log = makeStringInfo();
appendStringInfo(log, "truncate cbm pages of rel %u/%u/%u forknum %d to %u blocks",
cbmPageTag.rNode.spcNode, cbmPageTag.rNode.dbNode, cbmPageTag.rNode.relNode, cbmPageTag.forkNum, truncBlkNo);
resPageFirstBlkNo = (truncBlkNo == 0 ? InvalidBlockNumber : BLKNO_TO_CBM_PAGEFIRSTBOCK(truncBlkNo - 1));
for (eltCbmSegPageList = DLGetHead(&cbmPageEntry->cbmSegPageList); eltCbmSegPageList;
eltCbmSegPageList = neltCbmSegPageList) {
/* we donot remove the level 1 dllist at this scene */
neltCbmSegPageList = DLGetSucc(eltCbmSegPageList);
cbmSegPageList = (CbmSegPageList *)DLE_VAL(eltCbmSegPageList);
for (eltPagelist = DLGetHead(&cbmSegPageList->pageDllist); eltPagelist; eltPagelist = neltPagelist) {
neltPagelist = DLGetSucc(eltPagelist);
cbmPageHeader = (CbmPageHeader *)DLE_VAL(eltPagelist);
if (!BlockNumberIsValid(cbmPageHeader->firstBlkNo)) {
continue;
}
if (CBM_BLOCKNO_CMP(cbmPageHeader->firstBlkNo, resPageFirstBlkNo) < 0) {
continue;
} else if (CBM_BLOCKNO_CMP(cbmPageHeader->firstBlkNo, resPageFirstBlkNo) > 0) {
appendStringInfo(log, " truncate whole page with first blocknum %u", cbmPageHeader->firstBlkNo);
DLRemove(eltPagelist);
cbmPageEntry->pageNum--;
if (isCBMWriter) {
t_thrd.cbm_cxt.XlogCbmSys->totalPageNum--;
DLAddTail(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList, eltPagelist);
} else {
pfree(DLE_VAL(eltPagelist));
DLFreeElem(eltPagelist);
}
} else {
bool needReserve = false;
BlockNumber blkNo;
char *bitMap = (char *)cbmPageHeader + MAXALIGN(sizeof(CbmPageHeader));
int mapByte, mapBit;
for (blkNo = cbmPageHeader->firstBlkNo; blkNo <= (truncBlkNo - 1); blkNo++) {
mapByte = BLKNO_TO_CBMBYTEOFPAGE(blkNo);
mapBit = BLKNO_TO_CBMBITOFBYTE(blkNo);
if (bitMap[mapByte] & (1U << (uint32)mapBit)) {
needReserve = true;
break;
}
}
appendStringInfo(log, " truncate %s page with first blocknum %u", needReserve ? "partial" : "whole",
cbmPageHeader->firstBlkNo);
if (needReserve) {
for (blkNo = truncBlkNo; blkNo < (cbmPageHeader->firstBlkNo + CBM_BLOCKS_PER_PAGE); blkNo++) {
mapByte = BLKNO_TO_CBMBYTEOFPAGE(blkNo);
mapBit = BLKNO_TO_CBMBITOFBYTE(blkNo);
CLEAR_CBM_PAGE_BITMAP(bitMap[mapByte], mapBit);
}
} else {
DLRemove(eltPagelist);
cbmPageEntry->pageNum--;
if (isCBMWriter) {
t_thrd.cbm_cxt.XlogCbmSys->totalPageNum--;
DLAddTail(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList, eltPagelist);
} else {
pfree(DLE_VAL(eltPagelist));
DLFreeElem(eltPagelist);
}
}
}
}
}
ereport(DEBUG1, (errmsg("Truncate CBM hash entry: %s", log->data)));
pfree(log->data);
pfree(log);
}
/* remove all cbm pages and clear all bits before truncBlkNo */
static void CBMPageEtyTruncateBefore(const CBMPageTag &cbmPageTag, HTAB *cbmPageHash, BlockNumber truncBlkNo,
bool isCBMWriter)
{
CbmHashEntry *cbmPageEntry = NULL;
CbmPageHeader *cbmPageHeader = NULL;
Dlelem *eltCbmSegPageList = NULL;
Dlelem *eltPagelist = NULL;
Dlelem *neltCbmSegPageList = NULL;
Dlelem *neltPagelist = NULL;
CbmSegPageList *cbmSegPageList = NULL;
bool found = true;
if (truncBlkNo == 0) {
Assert(0);
return;
}
cbmPageEntry = (CbmHashEntry *)hash_search(cbmPageHash, (void *)&cbmPageTag, HASH_FIND, &found);
if (!found) {
return;
}
Assert(cbmPageEntry != NULL);
BlockNumber resPageFirstBlkNo;
StringInfo log = makeStringInfo();
appendStringInfo(log, "truncate cbm pages of rel %u/%u/%u forknum %d before %u blocks",
cbmPageTag.rNode.spcNode, cbmPageTag.rNode.dbNode, cbmPageTag.rNode.relNode, cbmPageTag.forkNum, truncBlkNo);
resPageFirstBlkNo = BLKNO_TO_CBM_PAGEFIRSTBOCK(truncBlkNo);
for (eltCbmSegPageList = DLGetHead(&cbmPageEntry->cbmSegPageList); eltCbmSegPageList;
eltCbmSegPageList = neltCbmSegPageList) {
/* we donot remove the level 1 dllist at this scene */
neltCbmSegPageList = DLGetSucc(eltCbmSegPageList);
cbmSegPageList = (CbmSegPageList *)DLE_VAL(eltCbmSegPageList);
for (eltPagelist = DLGetHead(&cbmSegPageList->pageDllist); eltPagelist; eltPagelist = neltPagelist) {
neltPagelist = DLGetSucc(eltPagelist);
cbmPageHeader = (CbmPageHeader *)DLE_VAL(eltPagelist);
if (!BlockNumberIsValid(cbmPageHeader->firstBlkNo)) {
continue;
}
if (CBM_BLOCKNO_CMP(cbmPageHeader->firstBlkNo, resPageFirstBlkNo) > 0) {
continue;
} else if (CBM_BLOCKNO_CMP(cbmPageHeader->firstBlkNo, resPageFirstBlkNo) < 0) {
appendStringInfo(log, " truncate whole page with first blocknum %u", cbmPageHeader->firstBlkNo);
DLRemove(eltPagelist);
cbmPageEntry->pageNum--;
if (isCBMWriter) {
t_thrd.cbm_cxt.XlogCbmSys->totalPageNum--;
DLAddTail(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList, eltPagelist);
} else {
pfree(DLE_VAL(eltPagelist));
DLFreeElem(eltPagelist);
}
} else {
bool needReserve = false;
BlockNumber blkNo;
unsigned char *bitMap = (unsigned char *)cbmPageHeader + MAXALIGN(sizeof(CbmPageHeader));
uint32 mapByte, mapBit;
for (blkNo = truncBlkNo; blkNo < (cbmPageHeader->firstBlkNo + CBM_BLOCKS_PER_PAGE); blkNo++) {
mapByte = BLKNO_TO_CBMBYTEOFPAGE(blkNo);
mapBit = BLKNO_TO_CBMBITOFBYTE(blkNo);
if (bitMap[mapByte] & (1U << mapBit)) {
needReserve = true;
break;
}
}
if (needReserve) {
for (blkNo = cbmPageHeader->firstBlkNo; blkNo < truncBlkNo; blkNo++) {
mapByte = BLKNO_TO_CBMBYTEOFPAGE(blkNo);
mapBit = BLKNO_TO_CBMBITOFBYTE(blkNo);
CLEAR_CBM_PAGE_BITMAP(bitMap[mapByte], mapBit);
}
} else {
DLRemove(eltPagelist);
cbmPageEntry->pageNum--;
if (isCBMWriter) {
t_thrd.cbm_cxt.XlogCbmSys->totalPageNum--;
DLAddTail(&t_thrd.cbm_cxt.XlogCbmSys->pageFreeList, eltPagelist);
} else {
pfree(DLE_VAL(eltPagelist));
DLFreeElem(eltPagelist);
}
}
appendStringInfo(log, " truncate %s page with first blocknum %u", needReserve ? "partial" : "whole",
cbmPageHeader->firstBlkNo);
}
}
}
ereport(DEBUG1, (errmsg("Truncate CBM hash entry: %s", log->data)));
pfree(log->data);
pfree(log);
}
static void CBMPageEtyMergePage(CbmHashEntry *cbmPageEntry, const char *page)
{
BlockNumber pageFirstBlock = ((CbmPageHeader *)page)->firstBlkNo;
Dlelem *eltPagelist = NULL;
CbmPageHeader *cbmPageHeader = NULL;
eltPagelist = FindPageElemFromEntry(cbmPageEntry, pageFirstBlock);
if (eltPagelist != NULL) {
cbmPageHeader = (CbmPageHeader *)DLE_VAL(eltPagelist);
CBMPageMergeBitmap((char *)cbmPageHeader, page);
return;
}
CopyCBMPageAndInsert(cbmPageEntry, page);
}
static void CBMPageMergeBitmap(char *cbmHashPage, const char *newPage)
{
int i;
CbmPageHeader *cbmPageHeader = (CbmPageHeader *)cbmHashPage;
CbmPageHeader *newPageHeader = (CbmPageHeader *)newPage;
cbmPageHeader->pageType |= newPageHeader->pageType;
if (BlockNumberIsValid(newPageHeader->truncBlkNo)) {
Assert(newPageHeader->pageType & PAGETYPE_TRUNCATE);
cbmPageHeader->truncBlkNo = newPageHeader->truncBlkNo;
}
if (!BlockNumberIsValid(newPageHeader->firstBlkNo))
return;
for (i = MAXALIGN(sizeof(CbmPageHeader)); i < CBMPAGESIZE; i++)
cbmHashPage[i] = (char)((unsigned char)cbmHashPage[i] | (unsigned char)newPage[i]);
}
static void CopyCBMPageAndInsert(CbmHashEntry *cbmPageEntry, const char *page)
{
Dlelem *elt = NULL;
int rc;
CbmPageHeader *cbmPageHeader = (CbmPageHeader *)palloc_extended(CBMPAGESIZE, MCXT_ALLOC_NO_OOM);
if (cbmPageHeader == NULL)
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate new CBM page")));
rc = memcpy_s(cbmPageHeader, CBMPAGESIZE, page, CBMPAGESIZE);
securec_check(rc, "\0", "\0");
elt = DLNewElem((void *)cbmPageHeader);
InsertCbmPageElemToEntry(cbmPageEntry, elt, (((CbmPageHeader *)page)->firstBlkNo));
}
static uint64 GetCBMHashTotalPageNum(HTAB *cbmPageHash)
{
HASH_SEQ_STATUS status;
CbmHashEntry *cbmPageEntry = NULL;
uint64 totalPageNum = 0;
hash_seq_init(&status, cbmPageHash);
while ((cbmPageEntry = (CbmHashEntry *)hash_seq_search(&status)) != NULL)
totalPageNum += cbmPageEntry->pageNum;
return totalPageNum;
}
static FILE *MergedXlogCBMSysInitFile(XlogBitmap *mergedXlogCbmSys)
{
FILE *file = NULL;
int rc;
struct timeval curTime = { 0, 0 };
(void)gettimeofday(&curTime, NULL);
rc = snprintf_s(mergedXlogCbmSys->out.name, MAXPGPATH, MAXPGPATH - 1, merged_bmp_file_name_template,
t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, mergedCbmFileNameStem,
(uint32)(mergedXlogCbmSys->startLSN >> 32), (uint32)mergedXlogCbmSys->startLSN,
(uint32)(mergedXlogCbmSys->endLSN >> 32), (uint32)mergedXlogCbmSys->endLSN, curTime.tv_sec,
curTime.tv_usec);
securec_check_ss(rc, "\0", "\0");
file = AllocateFile(mergedXlogCbmSys->out.name, PG_BINARY_W);
if (file == NULL || (mergedXlogCbmSys->out.fd = fileno(file)) < 0)
ereport(ERROR, (errcode_for_file_access(),
errmsg("could not create merge dest CBM file \"%s\": %m", mergedXlogCbmSys->out.name)));
mergedXlogCbmSys->out.offset = (off_t)0;
return file;
}
static void FreeCBMFileArray(CbmFileName **cbmFileNameArray, int cbmFileNum)
{
int i;
for (i = 0; i < cbmFileNum; i++) {
pfree(cbmFileNameArray[i]->name);
pfree(cbmFileNameArray[i]);
}
pfree(cbmFileNameArray);
}
CBMArray *CBMGetMergedArray(XLogRecPtr startLSN, XLogRecPtr endLSN)
{
HTAB *cbmPageHash = NULL;
CBMArray *cbmArray = (CBMArray *)palloc_extended(sizeof(CBMArray), MCXT_ALLOC_NO_OOM | MCXT_ALLOC_ZERO);
if (cbmArray == NULL) {
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate CBM array")));
}
cbmPageHash = CBMPageHashInitialize(CurrentMemoryContext);
CBMGetMergedHash(startLSN, endLSN, cbmPageHash, &cbmArray->startLSN, &cbmArray->endLSN);
PrintCBMHashTab(cbmPageHash);
if (GetCBMHashTotalPageNum(cbmPageHash) != 0)
cbmArray->arrayEntry = ConvertCBMHashIntoArray(cbmPageHash, &cbmArray->arrayLength, true);
hash_destroy(cbmPageHash);
return cbmArray;
}
static CBMArrayEntry *ConvertCBMHashIntoArray(HTAB *cbmPageHash, long *arrayLength, bool destroyHashEntry)
{
HASH_SEQ_STATUS status;
CbmHashEntry *cbmPageEntry = NULL;
int totalHashEntryNum = hash_get_num_entries(cbmPageHash);
int hashEntryIndex = 0;
int rc = 0;
/* Considering 44-bytes-wide CBMArrayEntry, cbmArrayEntry total size may exceed 1G with over 20M relations */
CBMArrayEntry *cbmArrayEntry =
(CBMArrayEntry *)palloc_huge(CurrentMemoryContext, totalHashEntryNum * sizeof(CBMArrayEntry));
rc = memset_s(cbmArrayEntry, totalHashEntryNum * sizeof(CBMArrayEntry), 0, totalHashEntryNum * sizeof(CBMArrayEntry));
securec_check(rc, "\0", "\0");
hash_seq_init(&status, cbmPageHash);
while ((cbmPageEntry = (CbmHashEntry *)hash_seq_search(&status)) != NULL) {
int pageNum = cbmPageEntry->pageNum;
Assert(pageNum);
Dlelem *eltCbmSegPageList = NULL;
Dlelem *eltPagelist = NULL;
CbmPageHeader **cbmPageHeaderArray = (CbmPageHeader **)palloc_extended(pageNum * sizeof(CbmPageHeader *),
MCXT_ALLOC_NO_OOM | MCXT_ALLOC_ZERO);
if (cbmPageHeaderArray == NULL)
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate CBM page header array")));
while (ForeachEntryForPage(cbmPageEntry, &eltCbmSegPageList, &eltPagelist)) {
cbmPageHeaderArray[--pageNum] = (CbmPageHeader *)DLE_VAL(eltPagelist);
Assert(cbmPageHeaderArray[pageNum]);
}
Assert(!pageNum);
qsort(cbmPageHeaderArray, cbmPageEntry->pageNum, sizeof(CbmPageHeader *), CBMPageSeqCmp);
INIT_CBMARRAYENTRY(&(cbmArrayEntry[hashEntryIndex]), cbmPageEntry->cbmTag);
for (pageNum = 0; pageNum < cbmPageEntry->pageNum; pageNum++) {
MergeOneCBMPageIntoArrayEntry((char *)(cbmPageHeaderArray[pageNum]), &(cbmArrayEntry[hashEntryIndex]));
ereport(DEBUG1,
(errmsg("convert CBM page into array: rel %u/%u/%u "
"forknum %d first blkno %u page type %d truncate blkno %u",
cbmPageHeaderArray[pageNum]->rNode.spcNode, cbmPageHeaderArray[pageNum]->rNode.dbNode,
cbmPageHeaderArray[pageNum]->rNode.relNode, cbmPageHeaderArray[pageNum]->forkNum,
cbmPageHeaderArray[pageNum]->firstBlkNo, cbmPageHeaderArray[pageNum]->pageType,
cbmPageHeaderArray[pageNum]->truncBlkNo)));
}
pfree(cbmPageHeaderArray);
hashEntryIndex++;
if (destroyHashEntry) {
DestoryCbmHashEntry(cbmPageEntry, false, NULL, false, NULL);
if (hash_search(cbmPageHash, (void *)&cbmPageEntry->cbmTag, HASH_REMOVE, NULL) == NULL)
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED), errmsg("CBM hash table corrupted")));
}
}
if ((*arrayLength = hashEntryIndex) != totalHashEntryNum)
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED),
errmsg("CBM hash table corrupted: incorrect total hash entry number")));
return cbmArrayEntry;
}
static void MergeOneCBMPageIntoArrayEntry(char *page, CBMArrayEntry *cbmArrayEntry)
{
CbmPageHeader *cbmPageHeader = (CbmPageHeader *)page;
CBMBitmapIterator bitmapIter;
BlockNumber blkNo;
if (!BlockNumberIsValid(cbmPageHeader->firstBlkNo)) {
cbmArrayEntry->changeType = cbmPageHeader->pageType;
if (cbmArrayEntry->changeType & PAGETYPE_TRUNCATE) {
Assert(BlockNumberIsValid(cbmPageHeader->truncBlkNo));
cbmArrayEntry->truncBlockNum = cbmPageHeader->truncBlkNo;
}
return;
}
INIT_CBMBITMAPITERATOR(bitmapIter, (char *)(page + MAXALIGN(sizeof(CbmPageHeader))), cbmPageHeader->firstBlkNo,
cbmPageHeader->firstBlkNo + CBM_BLOCKS_PER_PAGE - 1);
while (CBMBitmap_next(&bitmapIter, &blkNo))
CBMArrayEntryRigsterBlock(cbmArrayEntry, blkNo);
}
static bool CBMBitmap_next(CBMBitmapIterator *iter, BlockNumber *blkno)
{
char *bitmap = iter->bitmap;
BlockNumber blkNo;
int mapByte;
int mapBit;
for (;;) {
blkNo = iter->nextBlkNo++;
if (blkNo > iter->endBlkNo)
break;
mapByte = BLKNO_TO_CBMBYTEOFPAGE(blkNo);
mapBit = BLKNO_TO_CBMBITOFBYTE(blkNo);
if (bitmap[mapByte] & (1U << (uint32)mapBit)) {
*blkno = blkNo;
return true;
}
}
/* no more set bits in this bitmap. */
return false;
}
static void InitCBMArrayEntryBlockArray(CBMArrayEntry *cbmArrayEntry)
{
cbmArrayEntry->changedBlock = (BlockNumber *)palloc_extended(INITBLOCKARRAYSIZE * sizeof(BlockNumber),
MCXT_ALLOC_NO_OOM | MCXT_ALLOC_ZERO);
if (cbmArrayEntry->changedBlock == NULL)
ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("memory is temporarily unavailable while allocate CBM block array")));
cbmArrayEntry->maxSize = INITBLOCKARRAYSIZE;
}
static void CBMArrayEntryRigsterBlock(CBMArrayEntry *cbmArrayEntry, BlockNumber blkNo)
{
if (cbmArrayEntry->totalBlockNum >= cbmArrayEntry->maxSize) {
cbmArrayEntry->maxSize *= 2;
/* for 32bit blocknumber, max size for changedBlock array is 16G */
cbmArrayEntry->changedBlock = (BlockNumber *)repalloc_huge(cbmArrayEntry->changedBlock,
cbmArrayEntry->maxSize * sizeof(BlockNumber));
}
cbmArrayEntry->changedBlock[cbmArrayEntry->totalBlockNum++] = blkNo;
}
void FreeCBMArray(CBMArray *cbmArray)
{
long i;
if (cbmArray == NULL)
return;
if (cbmArray->arrayLength > 0) {
for (i = 0; i < cbmArray->arrayLength; i++)
pfree(cbmArray->arrayEntry[i].changedBlock);
pfree(cbmArray->arrayEntry);
}
pfree(cbmArray);
}
static long GetSplitCBMArrayLength(CBMArray *orgCBMArray)
{
long i;
long splitLength = 0;
for (i = 0; i < orgCBMArray->arrayLength; i++) {
if (orgCBMArray->arrayEntry[i].totalBlockNum > 0) {
splitLength += (orgCBMArray->arrayEntry[i].totalBlockNum - 1) / MAX_BLOCKNO_PER_TUPLE + 1;
} else {
splitLength++;
}
}
return splitLength;
}
#ifndef MIN
#define MIN(A, B) ((B) < (A) ? (B) : (A))
#endif
/* Copy block number array to one splitted cbm entry, for atmost MAX_BLOCKNO_PER_TUPLE members */
static void copyChangedBlock
(CBMArrayEntry *newEntry, CBMArrayEntry *orgEntry, long *newIndex, long oldIndex, uint32 *offset)
{
int rc = 0;
newEntry[*newIndex].totalBlockNum = newEntry[*newIndex].maxSize =
MIN(MAX_BLOCKNO_PER_TUPLE, orgEntry[oldIndex].totalBlockNum - *offset);
/* no need to use palloc_huge here since MAX_BLOCKNO_PER_TUPLE multiplies 4 bytes is less than 1G */
newEntry[*newIndex].changedBlock =
(BlockNumber*)palloc(newEntry[*newIndex].totalBlockNum * sizeof(BlockNumber));
rc = memcpy_s(newEntry[*newIndex].changedBlock,
newEntry[*newIndex].totalBlockNum * sizeof(BlockNumber),
orgEntry[oldIndex].changedBlock + *offset,
newEntry[*newIndex].totalBlockNum * sizeof(BlockNumber));
securec_check(rc, "\0", "\0");
*offset += newEntry[*newIndex].totalBlockNum;
*newIndex = *newIndex + 1;
}
CBMArray *SplitCBMArray(CBMArray **orgCBMArrayPtr)
{
CBMArray *orgCBMArray = *orgCBMArrayPtr;
long splitLength = GetSplitCBMArrayLength(orgCBMArray);
CBMArray *newCBMArray = NULL;
long oldIndex = 0;
long newIndex = 0;
int rc = 0;
/* no need to split */
if (splitLength == orgCBMArray->arrayLength) {
return orgCBMArray;
}
/* Considering 44-bytes-wide CBMArrayEntry, cbmArrayEntry total size may exceed 1G with over 20M relations */
CBMArrayEntry *cbmArrayEntry =
(CBMArrayEntry *)palloc_huge(CurrentMemoryContext, splitLength * sizeof(CBMArrayEntry));
rc = memset_s(cbmArrayEntry, splitLength * sizeof(CBMArrayEntry), 0, splitLength * sizeof(CBMArrayEntry));
securec_check(rc, "\0", "\0");
for (oldIndex = 0; oldIndex < orgCBMArray->arrayLength; oldIndex++) {
uint32 curEntryBlockNo = 0;
/* the first entry in each splitted group should contain ddl information */
cbmArrayEntry[newIndex].cbmTag = orgCBMArray->arrayEntry[oldIndex].cbmTag;
cbmArrayEntry[newIndex].changeType = orgCBMArray->arrayEntry[oldIndex].changeType;
cbmArrayEntry[newIndex].truncBlockNum = orgCBMArray->arrayEntry[oldIndex].truncBlockNum;
/* continue if no changed block for this entry */
if (orgCBMArray->arrayEntry[oldIndex].totalBlockNum == 0) {
newIndex++;
Assert(newIndex <= splitLength);
pfree_ext(orgCBMArray->arrayEntry[oldIndex].changedBlock);
continue;
}
copyChangedBlock(cbmArrayEntry, orgCBMArray->arrayEntry, &newIndex, oldIndex, &curEntryBlockNo);
Assert(newIndex <= splitLength);
while (curEntryBlockNo < orgCBMArray->arrayEntry[oldIndex].totalBlockNum) {
/* for splitted cbm array, no ddl information in latter rows */
cbmArrayEntry[newIndex].cbmTag = orgCBMArray->arrayEntry[oldIndex].cbmTag;
cbmArrayEntry[newIndex].truncBlockNum = InvalidBlockNumber;
copyChangedBlock(cbmArrayEntry, orgCBMArray->arrayEntry, &newIndex, oldIndex, &curEntryBlockNo);
Assert(newIndex <= splitLength);
}
Assert(curEntryBlockNo == orgCBMArray->arrayEntry[oldIndex].totalBlockNum);
pfree_ext(orgCBMArray->arrayEntry[oldIndex].changedBlock);
}
Assert(newIndex == splitLength);
newCBMArray = (CBMArray *)palloc0(sizeof(CBMArray));
newCBMArray->startLSN = orgCBMArray->startLSN;
newCBMArray->endLSN = orgCBMArray->endLSN;
newCBMArray->arrayLength = splitLength;
newCBMArray->arrayEntry = cbmArrayEntry;
pfree_ext(orgCBMArray->arrayEntry);
pfree_ext(*orgCBMArrayPtr);
return newCBMArray;
}
void CBMRecycleFile(XLogRecPtr targetLSN, XLogRecPtr *endLSN)
{
int cbmFileNum = 0;
CbmFileName **cbmFileNameArray;
uint64 fileSize = 0;
XLogRecPtr fileTrackedLSN = InvalidXLogRecPtr;
XLogRecPtr maxFileTrackedLSN = InvalidXLogRecPtr;
XLogRecPtr minFileStartLSN = InvalidXLogRecPtr;
int i;
cbmFileNameArray = GetCBMFileArray(InvalidXLogRecPtr, targetLSN, &cbmFileNum, true);
if (cbmFileNum == 0) {
*endLSN = targetLSN;
return;
}
for (i = 0; i < cbmFileNum; i++) {
fileTrackedLSN = cbmFileNameArray[i]->endLSN;
if (XLogRecPtrIsInvalid(fileTrackedLSN))
ValidateCBMFile(cbmFileNameArray[i]->name, &fileTrackedLSN, &fileSize, false);
if (XLogRecPtrIsInvalid(fileTrackedLSN))
fileTrackedLSN = cbmFileNameArray[i]->startLSN;
Assert(!XLogRecPtrIsInvalid(fileTrackedLSN));
if (XLByteLE(fileTrackedLSN, targetLSN)) {
UnlinkCBMFile(cbmFileNameArray[i]->name);
if (XLByteLT(maxFileTrackedLSN, fileTrackedLSN))
maxFileTrackedLSN = fileTrackedLSN;
} else {
if (XLogRecPtrIsInvalid(minFileStartLSN))
minFileStartLSN = cbmFileNameArray[i]->startLSN;
else if (XLByteLT(cbmFileNameArray[i]->startLSN, minFileStartLSN))
minFileStartLSN = cbmFileNameArray[i]->startLSN;
}
}
*endLSN = XLogRecPtrIsInvalid(maxFileTrackedLSN) ? minFileStartLSN : maxFileTrackedLSN;
FreeCBMFileArray(cbmFileNameArray, cbmFileNum);
}
static void UnlinkCBMFile(const char *cbmFileName)
{
char filePath[MAXPGPATH];
int rc;
rc = snprintf_s(filePath, MAXPGPATH, MAXPGPATH - 1, "%s%s", t_thrd.cbm_cxt.XlogCbmSys->cbmFileHome, cbmFileName);
securec_check_ss(rc, "\0", "\0");
rc = unlink(filePath);
if (rc < 0 && errno != ENOENT)
ereport(ERROR, (errcode_for_file_access(), errmsg("could not remove file \"%s\": %m", filePath)));
}
/*
* If needed, trigger one-cycle xlog parse at once, at least up to targetLSN.
* On success, the actual parse stop position is returned.
* On failure, invalid xlog position is returned.
*
* If one want to parse to an exact xlog position, caller should already hold
* CBMParseXlogLock exclusive lock , then write xlog and pass in the xlog position.
* On return, the CBMParseXlogLock lock would be released anyway.
*/
XLogRecPtr ForceTrackCBMOnce(XLogRecPtr targetLSN, int timeOut, bool wait, bool lockHeld, bool isRecEnd)
{
XLogRecPtr endLSN;
if (!u_sess->attr.attr_storage.enable_cbm_tracking)
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not force tracking cbm because cbm tracking function is not enabled!")));
if (wait && timeOut < 0)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("Negative timeout for force track cbm!")));
advanceXlogPtrToNextPageIfNeeded(&targetLSN);
endLSN = GetCBMTrackedLSN();
if (XLByteLT(endLSN, targetLSN)) {
if (!lockHeld)
(void)LWLockAcquire(CBMParseXlogLock, LW_EXCLUSIVE);
endLSN = GetCBMTrackedLSN();
if (XLByteLE(targetLSN, endLSN))
LWLockRelease(CBMParseXlogLock);
else {
XLogRecPtr tmpTargeLSN;
bool isTmpRecEnd = false;
tmpTargeLSN = GetTmpTargetLSN(&isTmpRecEnd);
if (XLogRecPtrIsInvalid(tmpTargeLSN)) {
SetTmpTargetLSN(targetLSN, isRecEnd);
} else if (lockHeld) {
/* when lock is held, we pass in just-finished xlog record end position */
Assert(XLByteLT(tmpTargeLSN, targetLSN));
Assert(isRecEnd);
SetTmpTargetLSN(targetLSN, isRecEnd);
}
if (g_instance.proc_base->cbmwriterLatch)
SetLatch(g_instance.proc_base->cbmwriterLatch);
LWLockRelease(CBMParseXlogLock);
for (;;) {
if (XLByteLE(targetLSN, endLSN))
break;
else if (!wait || timeOut <= 0) {
endLSN = InvalidXLogRecPtr;
break;
}
CHECK_FOR_INTERRUPTS();
pg_usleep(1000L);
timeOut--;
endLSN = GetCBMTrackedLSN();
}
}
} else {
if (lockHeld)
LWLockRelease(CBMParseXlogLock);
}
return endLSN;
}
static XLogRecPtr GetTmpTargetLSN(bool *isRecEnd)
{
*isRecEnd = tmpLSNIsRecEnd;
return tmpTargetLSN;
}
static void SetTmpTargetLSN(XLogRecPtr targetLSN, bool isRecEnd)
{
tmpTargetLSN = targetLSN;
tmpLSNIsRecEnd = isRecEnd;
}
static XLogRecPtr GetLatestCompTargetLSN(void)
{
return latestCompTargetLSN;
}
static void SetLatestCompTargetLSN(XLogRecPtr latestCompletedLSN)
{
latestCompTargetLSN = latestCompletedLSN;
}
void advanceXlogPtrToNextPageIfNeeded(XLogRecPtr *recPtr)
{
if (*recPtr % XLogSegSize == 0) {
XLByteAdvance((*recPtr), SizeOfXLogLongPHD);
} else if (*recPtr % XLOG_BLCKSZ == 0) {
XLByteAdvance((*recPtr), SizeOfXLogShortPHD);
}
}
static Dlelem *FindPageElemFromCbmSegList(CbmSegPageList *cbmSegPageList, BlockNumber pageFirstBlock)
{
CbmPageHeader *cbmPageHeader = NULL;
Dlelem *elt = NULL;
for (elt = DLGetHead(&cbmSegPageList->pageDllist); elt; elt = DLGetSucc(elt)) {
cbmPageHeader = (CbmPageHeader *)DLE_VAL(elt);
if (cbmPageHeader->firstBlkNo != pageFirstBlock)
continue;
return elt;
}
return NULL;
}
static Dlelem *FindCbmSegPageListemFromEntry(CbmHashEntry *cbmPageEntry, const int segIndex)
{
Dlelem *eltCbmSegPageList = NULL;
CbmSegPageList *cbmSegPageList = NULL;
for (eltCbmSegPageList = DLGetHead(&cbmPageEntry->cbmSegPageList); eltCbmSegPageList;
eltCbmSegPageList = DLGetSucc(eltCbmSegPageList)) {
cbmSegPageList = (CbmSegPageList *)DLE_VAL(eltCbmSegPageList);
if (cbmSegPageList->segIndex != segIndex)
continue;
return eltCbmSegPageList;
}
return NULL;
}
static Dlelem *FindPageElemFromEntry(CbmHashEntry *cbmPageEntry, BlockNumber pageFirstBlock)
{
CbmSegPageList *cbmSegPageList = NULL;
Dlelem *eltCbmSegPageList = NULL;
Dlelem *eltPagelist = NULL;
eltCbmSegPageList = FindCbmSegPageListemFromEntry(cbmPageEntry, GET_SEG_INDEX_FROM_BLOCK_NUM(pageFirstBlock));
if (eltCbmSegPageList != NULL) {
cbmSegPageList = (CbmSegPageList *)DLE_VAL(eltCbmSegPageList);
eltPagelist = FindPageElemFromCbmSegList(cbmSegPageList, pageFirstBlock);
DLMoveToFront(eltCbmSegPageList);
if (eltPagelist != NULL) {
/*
* We found a match in the cache. Move it to the front of the list
* for its hashbucket, in order to speed subsequent searches. (The
* most frequently accessed elements in any hashbucket will tend to be
* near the front of the hashbucket's list.)
*/
DLMoveToFront(eltPagelist);
return eltPagelist;
}
}
return NULL;
}
static Dlelem *InsertCbmSegPageListToEntry(CbmHashEntry *cbmPageEntry, const int segIndex)
{
CbmSegPageList *cbmSegPageList = NULL;
cbmSegPageList = (CbmSegPageList *)palloc(sizeof(CbmSegPageList));
DLInitList(&(cbmSegPageList->pageDllist));
cbmSegPageList->segIndex = segIndex;
Dlelem *elt = DLNewElem((void *)cbmSegPageList);
DLAddHead(&cbmPageEntry->cbmSegPageList, elt);
return elt;
}
static void InsertCbmPageElemToEntry(CbmHashEntry *cbmPageEntry, Dlelem *elt, BlockNumber pageFirstBlock)
{
Dlelem *eltCbmSegPageList = NULL;
CbmSegPageList *cbmSegPageList = NULL;
eltCbmSegPageList = FindCbmSegPageListemFromEntry(cbmPageEntry, GET_SEG_INDEX_FROM_BLOCK_NUM(pageFirstBlock));
if (eltCbmSegPageList == NULL) {
eltCbmSegPageList = InsertCbmSegPageListToEntry(cbmPageEntry, GET_SEG_INDEX_FROM_BLOCK_NUM(pageFirstBlock));
}
cbmSegPageList = (CbmSegPageList *)DLE_VAL(eltCbmSegPageList);
DLAddHead(&cbmSegPageList->pageDllist, elt);
cbmPageEntry->pageNum++;
}
/*
* foreach entry, return false when reach end, else return true
* eltCbmSegPageList point to next segelem, eltPagelist point to current page elem
* if eltCbmSegPageList and eltPagelist are null both, suggest that it is a init status, we forward eltCbmSegPageList
*/
static bool ForeachEntryForPage(CbmHashEntry *cbmPageEntry, Dlelem **eltCbmSegPageList, Dlelem **eltPagelist)
{
if (*eltCbmSegPageList == NULL && *eltPagelist == NULL) {
*eltCbmSegPageList = DLGetHead(&cbmPageEntry->cbmSegPageList);
}
CbmSegPageList *cbmSegPageList = NULL;
do {
if (*eltPagelist == NULL) {
if (*eltCbmSegPageList == NULL) {
break;
}
cbmSegPageList = (CbmSegPageList *)DLE_VAL(*eltCbmSegPageList);
*eltPagelist = DLGetHead(&cbmSegPageList->pageDllist);
*eltCbmSegPageList = DLGetSucc(*eltCbmSegPageList);
} else {
*eltPagelist = DLGetSucc(*eltPagelist);
}
} while (*eltPagelist == NULL);
return *eltPagelist != NULL;
}
static void DestoryCbmHashEntry(CbmHashEntry *cbmPageEntry, bool reuse, XlogBitmap *xlogCbmSys, bool changeTotalNum,
StringInfo log)
{
Dlelem *eltCbmSegPageList = NULL;
Dlelem *eltPagelist = NULL;
CbmSegPageList *cbmSegPageList = NULL;
while ((eltCbmSegPageList = DLRemHead(&cbmPageEntry->cbmSegPageList)) != NULL) {
cbmSegPageList = (CbmSegPageList *)DLE_VAL(eltCbmSegPageList);
Assert(eltCbmSegPageList);
while ((eltPagelist = DLRemHead(&cbmSegPageList->pageDllist)) != NULL) {
Assert(eltPagelist);
if (log != NULL) {
appendStringInfo(log, " page first blocknum %u", ((CbmPageHeader *)DLE_VAL(eltPagelist))->firstBlkNo);
}
if (eltPagelist != NULL) {
if (reuse == true) {
DLAddTail(&xlogCbmSys->pageFreeList, eltPagelist);
if (changeTotalNum) {
xlogCbmSys->totalPageNum--;
}
} else {
pfree(DLE_VAL(eltPagelist));
DLFreeElem(eltPagelist);
}
}
cbmPageEntry->pageNum--;
}
pfree(DLE_VAL(eltCbmSegPageList));
DLFreeElem(eltCbmSegPageList);
}
Assert(0 == cbmPageEntry->pageNum);
Assert(DLGetHead(&cbmPageEntry->cbmSegPageList) == NULL && DLGetTail(&cbmPageEntry->cbmSegPageList) == NULL);
}
void cbm_rotate_file(XLogRecPtr rotateLsn)
{
XLogRecPtr startLsn;
XLogRecPtr trackedLsn;
struct timeval curTime, startTime;
(void)gettimeofday(&startTime, NULL);
if (u_sess->attr.attr_storage.enable_cbm_tracking == false) {
goto success;
}
startLsn = GetCBMFileStartLsn();
if (XLByteLE(rotateLsn, startLsn)) {
goto success;
}
/* wait for tracked lsn for concurrent request ,
* for example a big lsn and a small lsn reach in same period, and small < tracked < big,
* the request for small lsn need wait tracked only
*/
trackedLsn = GetCBMTrackedLSN();
while (XLByteLT(trackedLsn, rotateLsn)) {
(void)gettimeofday(&curTime, NULL);
if ((curTime.tv_sec - startTime.tv_sec) > 600) {
goto timeout;
}
pg_usleep(1000 * 1000);
trackedLsn = GetCBMTrackedLSN();
}
/* cbm tracked lsn is bigger than request lsn */
SetCBMRotateLsn(rotateLsn);
startLsn = GetCBMFileStartLsn();
while (XLByteLT(startLsn, rotateLsn)) {
(void)gettimeofday(&curTime, NULL);
if ((curTime.tv_sec - startTime.tv_sec) > 600) {
goto timeout;
}
pg_usleep(1000 * 1000);
startLsn = GetCBMFileStartLsn();
}
success:
return ;
timeout:
ereport(ERROR, (errmsg("timeout when call pg_cbm_rotate_file")));
/* mute the compiler */
return;
}