* Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
*
* 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.
* -------------------------------------------------------------------------
*
* pagerepair.cpp
* Working mode of pagerepair thread, copy the data page from the primary.
*
* IDENTIFICATION
* src/gausskernel/process/postmaster/pagerepair.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "access/xlog_basic.h"
#include "access/xlog_internal.h"
#include "access/multi_redo_api.h"
#include "access/parallel_recovery/page_redo.h"
#include "access/parallel_recovery/dispatcher.h"
#include "catalog/catalog.h"
#include "gssignal/gs_signal.h"
#include "knl/knl_instance.h"
#include "service/remote_read_client.h"
#include "storage/ipc.h"
#include "storage/copydir.h"
#include "storage/lmgr.h"
#include "storage/remote_read.h"
#include "storage/smgr/relfilenode_hash.h"
#include "storage/smgr/fd.h"
#include "pgstat.h"
#include "postmaster/pagerepair.h"
#include "utils/plog.h"
#include "utils/plog.h"
#include "utils/inval.h"
#include "storage/cfs/cfs_converter.h"
#include "storage/cfs/cfs_repair.h"
#include "commands/verify.h"
const int MAX_THREAD_NAME_LEN = 64;
const int XLOG_LSN_SWAP = 32;
const int TEN_MILLISECOND = 10;
#define MAX(A, B) ((B) > (A) ? (B) : (A))
#define FILE_REPAIR_LOCK g_instance.repair_cxt.file_repair_hashtbl_lock
typedef struct XLogPageReadPrivate {
int emode;
bool fetching_ckpt;
bool randAccess;
} XLogPageReadPrivate;
* signal handler routines
* --------------------------------
*/
static void SetupPageRepairSignalHook(void);
static void PageRepairSigHupHandler(SIGNAL_ARGS);
static void PageRepairSigUsr1Handler(SIGNAL_ARGS);
static void PageRepairSigUsr2Handler(SIGNAL_ARGS);
static void PageRepairShutDownHandler(SIGNAL_ARGS);
static void PageRepairQuickDie(SIGNAL_ARGS);
static void PageRepairHandleInterrupts(void);
static void SeqRemoteReadPage();
static void SeqRemoteReadFile();
static void checkOtherFile(RepairFileKey key, uint32 max_segno, uint64 size);
static void PushBadFileToRemoteHashTbl(RepairFileKey key);
#define COMPARE_REPAIR_PAGE_KEY(key1, key2) \
((key1).relfilenode.relNode == (key2).relfilenode.relNode && \
(key1).relfilenode.dbNode == (key2).relfilenode.dbNode && \
(key1).relfilenode.spcNode == (key2).relfilenode.spcNode && \
(key1).relfilenode.bucketNode == (key2).relfilenode.bucketNode && \
(key1).relfilenode.opt == (key2).relfilenode.opt && (key1).forknum == (key2).forknum && \
(key1).blocknum == (key2).blocknum)
#define NOT_SUPPORT_PAGE_REPAIR \
(g_instance.attr.attr_common.cluster_run_mode == RUN_MODE_STANDBY || \
g_instance.dms_cxt.SSReformerControl.clusterRunMode == RUN_MODE_STANDBY || \
g_instance.attr.attr_common.stream_cluster_run_mode == RUN_MODE_STANDBY || \
t_thrd.xlog_cxt.is_hadr_main_standby || t_thrd.xlog_cxt.is_cascade_standby)
int CheckBlockLsn(XLogReaderState *xlogreader, RepairBlockKey key, XLogRecPtr page_old_lsn, XLogRecPtr *last_lsn)
{
RepairBlockKey temp_key = {0};
bool page_found = false;
bool getlsn = false;
for (int block_id = 0; block_id <= xlogreader->max_block_id; block_id++) {
XLogRecGetBlockTag(xlogreader, block_id, &temp_key.relfilenode, &temp_key.forknum, &temp_key.blocknum);
if (COMPARE_REPAIR_PAGE_KEY(key, temp_key)) {
page_found = true;
getlsn = XLogRecGetBlockLastLsn(xlogreader, block_id, last_lsn);
Assert(getlsn);
if (XLogRecPtrIsInvalid(*last_lsn)) {
ereport(LOG,
(errmsg("check the repair page successfully, last_lsn is 0,"
"the page %u/%u/%u bucketnode %d, forknum is %u, blocknum is %u",
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode,
key.relfilenode.bucketNode, key.forknum, key.blocknum)));
return CHECK_SUCCESS;
}
if (*last_lsn == page_old_lsn) {
ereport(LOG,
(errmsg("check the repair page successfully, the page %u/%u/%u bucketnode %d, "
"forknum is %u, blocknum is %u",
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode,
key.relfilenode.bucketNode, key.forknum, key.blocknum)));
return CHECK_SUCCESS;
}
if (*last_lsn < page_old_lsn) {
ereport(WARNING,
(errmsg("check the repair page, lsn not match, page_old_lsn is %X/%X, last_lsn is %X/%X, "
"could not repair the page %u/%u/%u bucketnode %d, forknum is %u, blocknum is %u",
(uint32)(page_old_lsn >> XLOG_LSN_SWAP), (uint32)page_old_lsn,
(uint32)(*last_lsn >> XLOG_LSN_SWAP), (uint32)*last_lsn,
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode,
key.relfilenode.bucketNode, key.forknum, key.blocknum)));
return CHECK_FAIL;
}
}
}
if (!page_found) {
ereport(WARNING,
(errmsg("check the repair page, not get page info, page_old_lsn is %X/%X, last_lsn is %X/%X, "
"could not repair the page %u/%u/%u bucketnode %d, forknum is %u, blocknum is %u",
(uint32)(page_old_lsn >> XLOG_LSN_SWAP), (uint32)page_old_lsn,
(uint32)(*last_lsn >> XLOG_LSN_SWAP), (uint32)*last_lsn,
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode,
key.relfilenode.bucketNode, key.forknum, key.blocknum)));
return CHECK_FAIL;
}
return NEED_CONTINUE_CHECK;
}
* Check whether the data page of the primary DN forms a complete xlog chain with the page of the standby DN.
*/
bool CheckPrimaryPageLSN(XLogRecPtr page_old_lsn, XLogRecPtr page_new_lsn, RepairBlockKey key)
{
XLogRecPtr prev_lsn = InvalidXLogRecPtr;
XLogRecPtr last_lsn = page_new_lsn;
XLogRecord *record = NULL;
char *errormsg = NULL;
XLogReaderState *xlogreader = NULL;
XLogPageReadPrivate readprivate;
errno_t rc;
int ret_code;
rc = memset_s(&readprivate, sizeof(XLogPageReadPrivate), 0, sizeof(XLogPageReadPrivate));
securec_check(rc, "", "");
xlogreader = XLogReaderAllocate(&XLogPageRead, &readprivate);
if (xlogreader == NULL) {
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"),
errdetail("Failed while allocating an XLog reading processor for pagerepair thread")));
}
xlogreader->system_identifier = t_thrd.shemem_ptr_cxt.ControlFile->system_identifier;
t_thrd.xlog_cxt.recoveryTargetTLI = t_thrd.shemem_ptr_cxt.ControlFile->checkPointCopy.ThisTimeLineID;
t_thrd.xlog_cxt.expectedTLIs = readTimeLineHistory(t_thrd.xlog_cxt.recoveryTargetTLI);
while (true) {
if (0 == last_lsn % XLogSegSize) {
XLByteAdvance(last_lsn, SizeOfXLogLongPHD);
} else if (0 == last_lsn % XLOG_BLCKSZ) {
XLByteAdvance(last_lsn, SizeOfXLogShortPHD);
}
record = XLogReadRecord(xlogreader, last_lsn, &errormsg);
if (record == NULL) {
ereport(WARNING,
(errmsg("check the repair page, page_old_lsn is %X/%X, could not get the xlog %X/%X "
"could not repair the page %u/%u/%u bucketnode %d, forknum is %u, blocknum is %u",
(uint32)(page_old_lsn >> XLOG_LSN_SWAP), (uint32)page_old_lsn,
(uint32)(last_lsn >> XLOG_LSN_SWAP), (uint32)last_lsn,
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode,
key.relfilenode.bucketNode, key.forknum, key.blocknum)));
return false;
}
prev_lsn = record->xl_prev;
record = XLogReadRecord(xlogreader, prev_lsn, &errormsg);
ret_code = CheckBlockLsn(xlogreader, key, page_old_lsn, &last_lsn);
if (ret_code == CHECK_SUCCESS) {
return true;
} else if (ret_code == CHECK_FAIL) {
return false;
}
}
ereport(WARNING,
(errmsg("check the repair page, could not found the page info from the xlog "
"could not repair the page, page old lsn is %X/%X, last lsn is %X/%X, page new lsn is %X/%X"
"page info is %u/%u/%u bucketnode %d, forknum is %u, blocknum is %u",
(uint32)(page_old_lsn >> XLOG_LSN_SWAP), (uint32)page_old_lsn,
(uint32)(last_lsn >> XLOG_LSN_SWAP), (uint32)last_lsn,
(uint32)(page_new_lsn >> XLOG_LSN_SWAP), (uint32)page_new_lsn,
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode,
key.relfilenode.bucketNode, key.forknum, key.blocknum)));
return false;
}
void PageRepairHashTblInit(void)
{
HASHCTL ctl;
if (g_instance.pid_cxt.PageRepairPID == 0) {
return;
}
if (g_instance.repair_cxt.page_repair_hashtbl_lock == NULL) {
g_instance.repair_cxt.page_repair_hashtbl_lock = LWLockAssign(LWTRANCHE_PAGE_REPAIR);
}
if (g_instance.repair_cxt.page_repair_hashtbl == NULL) {
errno_t rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "", "");
ctl.keysize = sizeof(RepairBlockKey);
ctl.entrysize = sizeof(RepairBlockEntry);
ctl.hash = RepairBlockKeyHash;
ctl.match = RepairBlockKeyMatch;
ctl.hcxt = INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE);
g_instance.repair_cxt.page_repair_hashtbl =
hash_create("Page Repair Hash Table", MAX_REPAIR_PAGE_NUM, &ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT | HASH_COMPARE);
if (!g_instance.repair_cxt.page_repair_hashtbl)
ereport(FATAL, (errmsg("could not initialize page repair Hash table")));
}
return;
}
void ClearPageRepairTheadMem(void)
{
if (g_instance.repair_cxt.page_repair_hashtbl != NULL) {
hash_destroy(g_instance.repair_cxt.page_repair_hashtbl);
g_instance.repair_cxt.page_repair_hashtbl = NULL;
}
if (g_instance.repair_cxt.file_repair_hashtbl != NULL) {
hash_destroy(g_instance.repair_cxt.file_repair_hashtbl);
g_instance.repair_cxt.file_repair_hashtbl = NULL;
}
return;
}
* After remote read, copy the page to hash table and update the page_new_lsn
* of page repair hash table, means that the page is correct.
*/
void CopyPageToRepairHashTbl(RepairBlockEntry *entry, char *page_content)
{
XLogRecPtr page_lsn = PageGetLSN(page_content);
errno_t rc = 0;
rc = memcpy_s(entry->page_content, BLCKSZ, page_content, BLCKSZ);
securec_check(rc, "", "");
if (entry->error_type == CRC_CHECK_FAIL) {
entry->page_new_lsn = page_lsn;
entry->page_state = WAIT_REPAIR;
} else {
entry->page_state = WAIT_LSN_CHECK;
}
return;
}
void CheckPageLSN(RepairBlockKey key)
{
HTAB *repair_hash = g_instance.repair_cxt.page_repair_hashtbl;
XLogRecPtr standby_flush_lsn;
RepairBlockEntry *entry = NULL;
bool found = false;
XLogRecPtr page_lsn;
XLogRecPtr page_old_lsn;
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
entry = (RepairBlockEntry*)hash_search(repair_hash, &(key), HASH_FIND, &found);
if (found) {
page_old_lsn = entry->page_old_lsn;
page_lsn = PageGetLSN(entry->page_content);
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
standby_flush_lsn = GetStandbyFlushRecPtr(NULL);
if (XLByteLE(standby_flush_lsn, page_lsn)) {
return;
}
bool check = CheckPrimaryPageLSN(page_old_lsn, page_lsn, key);
if (check) {
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
entry = (RepairBlockEntry*)hash_search(repair_hash, &(key), HASH_FIND, &found);
if (found) {
entry->page_new_lsn = page_lsn;
entry->page_state = WAIT_REPAIR;
(void)gs_signal_send(entry->recovery_tid, SIGUSR1);
}
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
} else {
ereport(PANIC,
(errmsg("check the repair page lsn failed, could not repair the page, "
"page old lsn is %X/%X, primary lsn is %X/%X, "
"page info is %u/%u/%u bucketnode %d, forknum is %u, blocknum is %u",
(uint32)(page_old_lsn >> XLOG_LSN_SWAP), (uint32)page_old_lsn,
(uint32)(page_lsn >> XLOG_LSN_SWAP), (uint32)page_lsn,
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode,
key.relfilenode.bucketNode, key.forknum, key.blocknum)));
}
} else {
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
}
return;
}
int RemoteReadFileSizeNoError(RepairFileKey *key, int64 *size)
{
char remote_address1[MAXPGPATH] = {0};
char remote_address2[MAXPGPATH] = {0};
int timeout = 120;
GetRemoteReadAddress(remote_address1, remote_address2, MAXPGPATH);
char *remote_address = remote_address1;
if (remote_address[0] == '\0' || remote_address[0] == ':') {
ereport(DEBUG1, (errmodule(MOD_REMOTE), errmsg("remote not available")));
return REMOTE_READ_IP_NOT_EXIST;
}
ereport(LOG, (errmodule(MOD_REMOTE), errmsg("remote read file size, file %s from %s",
relpathperm(key->relfilenode, key->forknum),
remote_address)));
RemoteReadFileKey read_key;
read_key.relfilenode = key->relfilenode;
read_key.forknum = key->forknum;
read_key.blockstart = 0;
PROFILING_REMOTE_START();
int retCode = RemoteGetFileSize(remote_address, &read_key, InvalidXLogRecPtr, size, timeout);
PROFILING_REMOTE_END_READ(sizeof(uint64) + sizeof(uint64), (retCode == REMOTE_READ_OK));
return retCode;
}
* standby dn use this function repair file.
*/
int RemoteReadFileNoError(RemoteReadFileKey *key, char *buf, XLogRecPtr lsn, uint32 size,
XLogRecPtr *remote_lsn, uint32 *remote_size)
{
char remote_address1[MAXPGPATH] = {0};
char remote_address2[MAXPGPATH] = {0};
char *remote_address = NULL;
GetRemoteReadAddress(remote_address1, remote_address2, MAXPGPATH);
remote_address = remote_address1;
int timeout = 0;
if (remote_address[0] == '\0' || remote_address[0] == ':') {
ereport(WARNING, (errcode(ERRCODE_IO_ERROR), errmodule(MOD_REMOTE), errmsg("remote not available")));
return REMOTE_READ_IP_NOT_EXIST;
}
ereport(LOG, (errmodule(MOD_REMOTE),
errmsg("remote read file, file %s from %s, block start is %u",
relpathperm(key->relfilenode, key->forknum), remote_address, key->blockstart)));
PROFILING_REMOTE_START();
int retCode = RemoteGetFile(remote_address, key, lsn, size, buf, remote_lsn, remote_size, timeout);
PROFILING_REMOTE_END_READ(size, (retCode == REMOTE_READ_OK));
return retCode;
}
int RemoteReadBlockNoError(RepairBlockKey *key, char *buf, XLogRecPtr lsn, const XLogPhyBlock *pblk)
{
char remote_address1[MAXPGPATH] = {0};
char remote_address2[MAXPGPATH] = {0};
GetRemoteReadAddress(remote_address1, remote_address2, MAXPGPATH);
char *remote_address = remote_address1;
if (remote_address[0] == '\0' || remote_address[0] == ':') {
ereport(DEBUG1, (errmodule(MOD_REMOTE), errmsg("remote not available")));
return REMOTE_READ_IP_NOT_EXIST;
}
if (pblk != NULL) {
ereport(LOG, (errmodule(MOD_REMOTE), errmsg("remote read page, file %s block %u (pblk %u/%d) from %s",
relpathperm(key->relfilenode, key->forknum), key->blocknum, pblk->relNode, pblk->block, remote_address)));
} else {
ereport(LOG, (errmodule(MOD_REMOTE), errmsg("remote read page, file %s block %u from %s",
relpathperm(key->relfilenode, key->forknum), key->blocknum, remote_address)));
}
const int TIMEOUT = 60;
PROFILING_REMOTE_START();
int retCode = RemoteGetPage(remote_address, key, BLCKSZ, lsn, buf, pblk, TIMEOUT);
PROFILING_REMOTE_END_READ(BLCKSZ, (retCode == REMOTE_READ_OK));
return retCode;
}
static void RepairPage(RepairBlockEntry *entry, char *page)
{
int retCode = 0;
and we need to try our best to repair this pca header page and the whole extent */
if (IS_COMPRESSED_RNODE(entry->key.relfilenode, MAIN_FORKNUM)) {
bool need_repair_pca = false;
SMgrRelation smgr = smgropen(entry->key.relfilenode, InvalidBackendId, GetColumnNum(MAIN_FORKNUM));
if (!IsSegmentPhysicalRelNode(entry->key.relfilenode)) {
CfsHeaderPageCheckAndRepair(smgr, entry->key.blocknum, NULL, ERR_MSG_LEN, &need_repair_pca);
if (need_repair_pca) {
CacheInvalidateSmgr(smgr->smgr_rnode);
gs_tryrepair_compress_extent(smgr, entry->key.blocknum);
}
} else {
;
}
}
if (entry->pblk.relNode != InvalidOid) {
retCode = RemoteReadBlockNoError(&entry->key, page, entry->page_old_lsn, &entry->pblk);
} else {
retCode = RemoteReadBlockNoError(&entry->key, page, entry->page_old_lsn, NULL);
}
if (retCode == REMOTE_READ_OK) {
CopyPageToRepairHashTbl(entry, page);
}
return;
}
const int MAX_CHECK_LSN_NUM = 100;
static void SeqRemoteReadPage()
{
HTAB *repair_hash = g_instance.repair_cxt.page_repair_hashtbl;
RepairBlockEntry *entry = NULL;
HASH_SEQ_STATUS status;
RepairBlockKey lsncheck[MAX_CHECK_LSN_NUM];
int check_lsn_num = 0;
int need_repair_num = 0;
int repair_num = 0;
char page[BLCKSZ] = {0};
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
hash_seq_init(&status, repair_hash);
while ((entry = (RepairBlockEntry *)hash_seq_search(&status)) != NULL) {
need_repair_num++;
switch (entry->page_state) {
case WAIT_REMOTE_READ:
RepairPage(entry, page);
if (entry->error_type == LSN_CHECK_FAIL && entry->page_state == WAIT_LSN_CHECK &&
check_lsn_num < MAX_CHECK_LSN_NUM) {
lsncheck[check_lsn_num] = entry->key;
check_lsn_num++;
}
if (entry->page_state == WAIT_REPAIR) {
(void)gs_signal_send(entry->recovery_tid, SIGUSR1);
repair_num++;
}
break;
case WAIT_LSN_CHECK:
if (check_lsn_num < MAX_CHECK_LSN_NUM) {
lsncheck[check_lsn_num] = entry->key;
check_lsn_num++;
}
break;
case WAIT_REPAIR:
repair_num++;
(void)gs_signal_send(entry->recovery_tid, SIGUSR1);
break;
default:
ereport(ERROR, (errmsg("error page state during remote read")));
}
}
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
for (int i = 0; i < check_lsn_num; i++) {
RepairBlockKey temp = lsncheck[i];
CheckPageLSN(temp);
}
if (need_repair_num == repair_num) {
t_thrd.pagerepair_cxt.page_repair_requested = false;
}
return;
}
static void PageRepairHandleInterrupts(void)
{
if (t_thrd.pagerepair_cxt.got_SIGHUP) {
t_thrd.pagerepair_cxt.got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
}
if (t_thrd.pagerepair_cxt.shutdown_requested && g_instance.pid_cxt.StartupPID == 0) {
ereport(LOG, (errmodule(MOD_REDO), errmsg("pagerepair thread shut down")));
u_sess->attr.attr_common.ExitOnAnyError = true;
proc_exit(0);
}
}
void PageRepairMain(void)
{
MemoryContext pagerepair_context;
char name[MAX_THREAD_NAME_LEN] = {0};
uint32 rc = 0;
t_thrd.role = PAGEREPAIR_THREAD;
SetupPageRepairSignalHook();
(void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT);
ereport(LOG, (errmodule(MOD_REDO), errmsg("pagerepair started")));
* Create a resource owner to keep track of our resources (currently only
* buffer pins).
*/
errno_t err_rc = snprintf_s(
name, MAX_THREAD_NAME_LEN, MAX_THREAD_NAME_LEN - 1, "%s", "PageRepair");
securec_check_ss(err_rc, "", "");
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, name,
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
* Create a memory context that we will do all our work in. We do this so
* that we can reset the context during error recovery and thereby avoid
* possible memory leaks. Formerly this code just ran in
* TopMemoryContext, but resetting that would be a really bad idea.
*/
pagerepair_context = AllocSetContextCreate(
TopMemoryContext, name, ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
(void)MemoryContextSwitchTo(pagerepair_context);
* Unblock signals (they were blocked when the postmaster forked us)
*/
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
(void)gs_signal_unblock_sigusr2();
pgstat_report_appname("PageRepair");
pgstat_report_activity(STATE_IDLE, NULL);
* Loop forever
*/
for (;;) {
PageRepairHandleInterrupts();
pgstat_report_activity(STATE_IDLE, NULL);
rc = WaitLatch(&t_thrd.proc->procLatch, WL_TIMEOUT | WL_POSTMASTER_DEATH, (long)TEN_MILLISECOND);
if (rc & WL_POSTMASTER_DEATH) {
gs_thread_exit(1);
}
ResetLatch(&t_thrd.proc->procLatch);
pgstat_report_activity(STATE_RUNNING, NULL);
if (!t_thrd.pagerepair_cxt.shutdown_requested) {
SeqRemoteReadPage();
SeqRemoteReadFile();
}
}
}
static void SetupPageRepairSignalHook(void)
{
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGHUP, PageRepairSigHupHandler);
(void)gspqsignal(SIGINT, SIG_IGN);
(void)gspqsignal(SIGTERM, PageRepairShutDownHandler);
(void)gspqsignal(SIGQUIT, PageRepairQuickDie);
(void)gspqsignal(SIGALRM, SIG_IGN);
(void)gspqsignal(SIGPIPE, SIG_IGN);
(void)gspqsignal(SIGUSR1, PageRepairSigUsr1Handler);
(void)gspqsignal(SIGUSR2, PageRepairSigUsr2Handler);
(void)gspqsignal(SIGURG, print_stack);
* Reset some signals that are accepted by postmaster but not here
*/
(void)gspqsignal(SIGCHLD, SIG_DFL);
(void)gspqsignal(SIGTTIN, SIG_DFL);
(void)gspqsignal(SIGTTOU, SIG_DFL);
(void)gspqsignal(SIGCONT, SIG_DFL);
(void)gspqsignal(SIGWINCH, SIG_DFL);
}
static void PageRepairSigUsr1Handler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.pagerepair_cxt.page_repair_requested = true;
if (t_thrd.proc) {
SetLatch(&t_thrd.proc->procLatch);
}
errno = save_errno;
}
static void PageRepairSigUsr2Handler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.pagerepair_cxt.file_repair_requested = true;
if (t_thrd.proc) {
SetLatch(&t_thrd.proc->procLatch);
}
errno = save_errno;
}
static void PageRepairSigHupHandler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.pagerepair_cxt.got_SIGHUP = true;
if (t_thrd.proc) {
SetLatch(&t_thrd.proc->procLatch);
}
errno = save_errno;
}
static void PageRepairShutDownHandler(SIGNAL_ARGS)
{
int save_errno = errno;
t_thrd.pagerepair_cxt.shutdown_requested = true;
if (t_thrd.proc) {
SetLatch(&t_thrd.proc->procLatch);
}
errno = save_errno;
}
static void PageRepairQuickDie(SIGNAL_ARGS)
{
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
* We DO NOT want to run proc_exit() callbacks -- we're here because
* shared memory may be corrupted, so we don't want to try to clean up our
* transaction. Just nail the windows shut and get out of town. Now that
* there's an atexit callback to prevent third-party code from breaking
* things by calling exit() directly, we have to reset the callbacks
* explicitly to make this work as intended.
*/
on_exit_reset();
* Note we do exit(2) not exit(0). This is to force the postmaster into a
* system reset cycle if some idiot DBA sends a manual SIGQUIT to a random
* backend. This is necessary precisely because we don't clean up our
* shared memory state. (The "dead man switch" mechanism in pmsignal.c
* should ensure the postmaster sees this as a crash, too, but no harm in
* being doubly sure.)
*/
gs_thread_exit(2);
}
bool PushBadPageToRemoteHashTbl(RepairBlockKey key, PageErrorType error_type, XLogRecPtr old_lsn,
XLogPhyBlock pblk, ThreadId tid)
{
HTAB *repair_hash = g_instance.repair_cxt.page_repair_hashtbl;
bool found = false;
Assert(repair_hash != NULL);
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
RepairBlockEntry *entry = (RepairBlockEntry*)hash_search(repair_hash, &(key), HASH_ENTER, &found);
if (!found) {
entry->key = key;
entry->recovery_tid = tid;
entry->error_type = error_type;
entry->page_state = WAIT_REMOTE_READ;
entry->page_old_lsn = old_lsn;
entry->page_new_lsn = InvalidXLogRecPtr;
entry->pblk = pblk;
}
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
if (!found) {
ThreadId PageRepairPID = g_instance.pid_cxt.PageRepairPID;
if (PageRepairPID != 0) {
(void)gs_signal_send(PageRepairPID, SIGUSR1);
}
if (g_instance.repair_cxt.repair_proc_latch != NULL) {
SetLatch(g_instance.repair_cxt.repair_proc_latch);
}
}
return found;
}
bool BlockNodeMatch(RepairBlockKey key, XLogPhyBlock pblk, RelFileNode node,
ForkNumber forknum, BlockNumber minblkno, bool segment_shrink)
{
if (segment_shrink) {
RelFileNode rnode = key.relfilenode;
rnode.relNode = pblk.relNode;
bool node_equal = RelFileNodeRelEquals(node, rnode);
return node_equal && key.forknum == forknum && pblk.block >= minblkno;
} else {
bool node_equal = IsBucketFileNode(node) ? RelFileNodeEquals(node, key.relfilenode)
: RelFileNodeRelEquals(node, key.relfilenode);
return node_equal && key.forknum == forknum && key.blocknum >= minblkno;
}
}
bool dbNodeandSpcNodeMatch(RelFileNode *rnode, Oid spcNode, Oid dbNode)
{
if (OidIsValid(spcNode) && rnode->spcNode != spcNode) {
return false;
}
if (OidIsValid(dbNode) && rnode->dbNode != dbNode) {
return false;
}
return true;
}
* drop database, or drop segmentspace, need clear the page repair hashTbl,
* if the repair page key dbNode match and spcNode match, need remove.
*/
void BatchClearPageRepairHashTbl(Oid spcNode, Oid dbNode)
{
HTAB *repair_hash = g_instance.repair_cxt.page_repair_hashtbl;
bool found = false;
RepairBlockEntry *entry = NULL;
HASH_SEQ_STATUS status;
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
hash_seq_init(&status, repair_hash);
while ((entry = (RepairBlockEntry *)hash_seq_search(&status)) != NULL) {
if (dbNodeandSpcNodeMatch(&(entry->key.relfilenode), spcNode, dbNode)) {
if (hash_search(repair_hash, &(entry->key), HASH_REMOVE, &found) == NULL) {
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("page repair hash table corrupted")));
}
}
}
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
return;
}
* drop table, or truncate table, need clear the page repair hashTbl, if the
* repair page Filenode match need remove.
*/
void ClearPageRepairHashTbl(const RelFileNode &node, ForkNumber forknum, BlockNumber minblkno,
bool segment_shrink)
{
HTAB *repair_hash = g_instance.repair_cxt.page_repair_hashtbl;
bool found = false;
RepairBlockEntry *entry = NULL;
HASH_SEQ_STATUS status;
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
hash_seq_init(&status, repair_hash);
while ((entry = (RepairBlockEntry *)hash_seq_search(&status)) != NULL) {
if (BlockNodeMatch(entry->key, entry->pblk, node, forknum, minblkno, segment_shrink)) {
if (hash_search(repair_hash, &(entry->key), HASH_REMOVE, &found) == NULL) {
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("page repair hash table corrupted")));
}
}
}
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
return;
}
* If the page repair finish, need clear the page repair hashTbl.
*/
void ClearSpecificsPageRepairHashTbl(RepairBlockKey key)
{
bool found = false;
HTAB *repair_hash = g_instance.repair_cxt.page_repair_hashtbl;
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
if ((RepairBlockEntry*)hash_search(repair_hash, &(key), HASH_REMOVE, &found) == NULL) {
ereport(WARNING,
(errmsg("the %u/%u/%u bucketnode %d forknum %u, blknum %u, remove form repair hashtbl, not found",
key.relfilenode.spcNode, key.relfilenode.dbNode, key.relfilenode.relNode, key.relfilenode.bucketNode,
key.forknum, key.blocknum)));
}
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
return;
}
* recovery thread check the primary page lsn (page_new_lsn) is in the range
* from record_min_lsn to record_max_lsn,
*/
bool CheckRepairPage(RepairBlockKey key, XLogRecPtr min_lsn, XLogRecPtr max_lsn, char *page)
{
bool found = false;
bool can_recovery = false;
RepairBlockEntry *entry = NULL;
HTAB *repair_hash = g_instance.repair_cxt.page_repair_hashtbl;
LWLockAcquire(g_instance.repair_cxt.page_repair_hashtbl_lock, LW_EXCLUSIVE);
entry = (RepairBlockEntry*)hash_search(repair_hash, &(key), HASH_FIND, &found);
if (entry == NULL) {
ereport(ERROR, (errmsg("the page repair hash table corrupted ")));
}
if (entry->page_state == WAIT_REPAIR && entry->page_new_lsn <= max_lsn && entry->page_new_lsn >= min_lsn) {
errno_t rc;
can_recovery = true;
rc = memcpy_s(page, BLCKSZ, entry->page_content, BLCKSZ);
securec_check(rc, "", "");
}
LWLockRelease(g_instance.repair_cxt.page_repair_hashtbl_lock);
return can_recovery;
}
void WaitRepalyFinish()
{
if (IsExtremeRedo()) {
ExtremeWaitAllReplayWorkerIdle();
} else if (IsParallelRedo()) {
parallel_recovery::WaitAllPageWorkersQueueEmpty();
} else {
XLogRecPtr standby_replay_lsn = GetXLogReplayRecPtr(NULL, NULL);
XLogRecPtr suspend_lsn = pg_atomic_read_u64(&g_instance.startup_cxt.suspend_lsn);
while (!XLByteLE(suspend_lsn, standby_replay_lsn)) {
PageRepairHandleInterrupts();
pg_usleep(1000000L);
(void)GetXLogReplayRecPtr(NULL, &standby_replay_lsn);
}
}
}
const int MAX_REPAIR_FILE_NUM = 20;
void FileRepairHashTblInit(void)
{
HASHCTL ctl;
if (g_instance.pid_cxt.PageRepairPID == 0) {
return;
}
if (g_instance.repair_cxt.file_repair_hashtbl_lock == NULL) {
g_instance.repair_cxt.file_repair_hashtbl_lock = LWLockAssign(LWTRANCHE_FILE_REPAIR);
}
if (g_instance.repair_cxt.file_repair_hashtbl == NULL) {
errno_t rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "", "");
ctl.keysize = sizeof(RepairFileKey);
ctl.entrysize = sizeof(RepairFileEntry);
ctl.hash = RepairFileKeyHash;
ctl.match = RepairFileKeyMatch;
ctl.hcxt = INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE);
g_instance.repair_cxt.file_repair_hashtbl = hash_create("File Repair Hash Table", MAX_REPAIR_FILE_NUM, &ctl,
HASH_ELEM | HASH_FUNCTION |HASH_CONTEXT | HASH_COMPARE);
if (!g_instance.repair_cxt.file_repair_hashtbl)
ereport(FATAL, (errmsg("could not initialize file repair Hash table")));
}
return;
}
bool CheckFileRepairHashTbl(RelFileNode rnode, ForkNumber forknum, uint32 segno)
{
HTAB* file_hashtbl = g_instance.repair_cxt.file_repair_hashtbl;
RepairFileKey key;
RepairFileEntry *entry = NULL;
bool found = false;
key.relfilenode = rnode;
key.forknum = forknum;
key.segno = segno;
if (file_hashtbl == NULL || g_instance.pid_cxt.PageRepairPID == 0) {
return found;
}
LWLockAcquire(FILE_REPAIR_LOCK, LW_SHARED);
entry = (RepairFileEntry*)hash_search(file_hashtbl, &(key), HASH_FIND, &found);
if (found) {
if (entry->file_state == WAIT_FILE_REPAIR || entry->file_state == WAIT_RENAME) {
found = true;
} else {
found = false;
}
}
LWLockRelease(FILE_REPAIR_LOCK);
return found;
}
void CheckNeedRecordBadFile(RepairFileKey key, uint32 nblock, uint32 blocknum, const XLogPhyBlock *pblk)
{
if (g_instance.pid_cxt.PageRepairPID != 0) {
return;
}
BlockNumber relSegSize =
IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM) ? CFS_LOGIC_BLOCKS_PER_FILE: RELSEG_SIZE;
if (CheckVerionSupportRepair() && (nblock == 0 || blocknum / relSegSize > nblock / relSegSize) &&
IsPrimaryClusterStandbyDN() && g_instance.repair_cxt.support_repair) {
if (pblk != NULL) {
key.relfilenode.relNode = pblk->relNode;
key.segno = pblk->block / relSegSize;
}
if (IsSegmentFileNode(key.relfilenode)) {
key.relfilenode.bucketNode = SegmentBktId;
}
PushBadFileToRemoteHashTbl(key);
}
}
static void PushBadFileToRemoteHashTbl(RepairFileKey key)
{
HTAB *file_hash = g_instance.repair_cxt.file_repair_hashtbl;
RepairFileEntry *entry = NULL;
bool found = false;
XLogRecPtr min_recovery_point;
LWLockAcquire(ControlFileLock, LW_SHARED);
min_recovery_point = t_thrd.shemem_ptr_cxt.ControlFile->minRecoveryPoint;
LWLockRelease(ControlFileLock);
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
entry = (RepairFileEntry*)hash_search(file_hash, &(key), HASH_ENTER, &found);
if (!found) {
entry->key.relfilenode.relNode = key.relfilenode.relNode;
entry->key.relfilenode.dbNode = key.relfilenode.dbNode;
entry->key.relfilenode.spcNode = key.relfilenode.spcNode;
entry->key.relfilenode.bucketNode = key.relfilenode.bucketNode;
entry->key.relfilenode.opt = key.relfilenode.opt;
entry->key.forknum = key.forknum;
entry->key.segno = key.segno;
entry->min_recovery_point = min_recovery_point;
entry->file_state = WAIT_FILE_CHECK_REPAIR;
entry->primary_file_lsn = InvalidXLogRecPtr;
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] push to file repair hashtbl, path is %s segno is %u",
relpathperm(entry->key.relfilenode, entry->key.forknum), entry->key.segno)));
}
LWLockRelease(FILE_REPAIR_LOCK);
return;
}
bool FileNodeMatch(RepairFileKey key, RelFileNode node, ForkNumber forknum, uint32 segno)
{
bool node_equal = RelFileNodeRelEquals(node, key.relfilenode);
return node_equal && key.forknum == forknum && key.segno >= segno;
}
void ClearBadFileHashTbl(const RelFileNode &node, ForkNumber forknum, uint32 segno)
{
HTAB *file_hash = g_instance.repair_cxt.file_repair_hashtbl;
RepairFileEntry *entry = NULL;
bool found = false;
HASH_SEQ_STATUS status;
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
hash_seq_init(&status, file_hash);
while ((entry = (RepairFileEntry *)hash_seq_search(&status)) != NULL) {
if (FileNodeMatch(entry->key, node, forknum, segno)) {
if (hash_search(file_hash, &(entry->key), HASH_REMOVE, &found) == NULL) {
LWLockRelease(FILE_REPAIR_LOCK);
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("file repair hash table corrupted")));
} else {
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] file %s segno is %u entry remove when drop table or truncate table",
relpathperm(entry->key.relfilenode, entry->key.forknum), entry->key.segno)));
}
}
}
LWLockRelease(FILE_REPAIR_LOCK);
return;
}
void BatchClearBadFileHashTbl(Oid spcNode, Oid dbNode)
{
HTAB *file_hash = g_instance.repair_cxt.file_repair_hashtbl;
RepairFileEntry *entry = NULL;
bool found = false;
HASH_SEQ_STATUS status;
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
hash_seq_init(&status, file_hash);
while ((entry = (RepairFileEntry *)hash_seq_search(&status)) != NULL) {
if (dbNodeandSpcNodeMatch(&(entry->key.relfilenode), spcNode, dbNode)) {
if (hash_search(file_hash, &(entry->key), HASH_REMOVE, &found) == NULL) {
LWLockRelease(FILE_REPAIR_LOCK);
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("file repair hash table corrupted")));
} else {
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] file %s segno is %u entry remove when drop database or segment space",
relpathperm(entry->key.relfilenode, entry->key.forknum), entry->key.segno)));
}
}
}
LWLockRelease(FILE_REPAIR_LOCK);
return;
}
void RenameRepairFile(RepairFileKey *key, bool clear_entry)
{
errno_t rc;
bool found = false;
HTAB *file_hash = g_instance.repair_cxt.file_repair_hashtbl;
char *path = relpathperm(key->relfilenode, key->forknum);
int64 segpathlen = (int64)(strlen(path) + SEGLEN + strlen(COMPRESS_STR));
char *tempsegpath = (char *)palloc((Size)segpathlen);
char *segpath = (char *)palloc((Size)segpathlen);
RequestCheckpoint(CHECKPOINT_FLUSH_DIRTY|CHECKPOINT_WAIT);
if (key->segno == 0) {
rc = sprintf_s(segpath, segpathlen, "%s%s", path,
IS_COMPRESSED_RNODE(key->relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
securec_check_ss(rc, "", "")
rc = sprintf_s(tempsegpath, segpathlen, "%s%s.repair", path,
IS_COMPRESSED_RNODE(key->relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
securec_check_ss(rc, "", "")
} else {
rc = sprintf_s(segpath, segpathlen, "%s.%u%s", path, key->segno,
IS_COMPRESSED_RNODE(key->relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
securec_check_ss(rc, "", "")
rc = sprintf_s(tempsegpath, segpathlen, "%s.%u%s.repair", path, key->segno,
IS_COMPRESSED_RNODE(key->relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
securec_check_ss(rc, "", "")
}
rc = durable_rename(tempsegpath, segpath, WARNING);
if (rc == 0) {
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] file rename from %s to %s finish", tempsegpath, segpath)));
if (IsExtremeRedo()) {
ExtremeDispatchCleanInvalidPageMarkToAllRedoWorker(*key);
ExtremeDispatchClosefdMarkToAllRedoWorker();
ExtremeWaitAllReplayWorkerIdle();
} else if (IsParallelRedo()) {
if (AmStartupProcess()) {
ProcTxnWorkLoad(true);
}
parallel_recovery::SendCleanInvalidPageMarkToAllWorkers(*key);
parallel_recovery::SendClosefdMarkToAllWorkers();
parallel_recovery::WaitAllPageWorkersQueueEmpty();
} else {
}
forget_range_invalid_pages((void*)key);
smgrcloseall();
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
if (clear_entry) {
if (hash_search(file_hash, key, HASH_REMOVE, &found) == NULL) {
pfree(path);
pfree(segpath);
pfree(tempsegpath);
LWLockRelease(FILE_REPAIR_LOCK);
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("file repair hash table corrupted")));
} else {
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] file %s repair finish, remove the entry", segpath)));
}
}
LWLockRelease(FILE_REPAIR_LOCK);
}
pfree(path);
pfree(segpath);
pfree(tempsegpath);
return;
}
void CheckNeedRenameFile()
{
HASH_SEQ_STATUS status;
RepairFileEntry *entry = NULL;
HTAB *file_hash = g_instance.repair_cxt.file_repair_hashtbl;
uint32 need_repair_num = 0;
uint32 need_rename_num = 0;
RepairFileKey *rename_key = NULL;
errno_t rc = 0;
uint32 i = 0;
if (g_instance.pid_cxt.PageRepairPID != 0) {
return;
}
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
hash_seq_init(&status, file_hash);
while ((entry = (RepairFileEntry *)hash_seq_search(&status)) != NULL) {
if (entry->file_state == WAIT_RENAME) {
need_rename_num++;
}
}
if (need_rename_num > 0) {
rename_key = (RepairFileKey*)palloc0(sizeof(RepairFileKey) * need_rename_num);
}
hash_seq_init(&status, file_hash);
while ((entry = (RepairFileEntry *)hash_seq_search(&status)) != NULL) {
switch (entry->file_state) {
case WAIT_RENAME:
Assert(XLByteLE(entry->primary_file_lsn, GetXLogReplayRecPtr(NULL, NULL)));
Assert(!IsSegmentFileNode(entry->key.relfilenode));
rc = memcpy_s(&rename_key[i], sizeof(RepairFileKey), &(entry->key), sizeof(RepairFileKey));
securec_check(rc, "", "");
i++;
break;
case WAIT_FILE_REMOTE_READ:
case WAIT_FILE_REPAIR_SEGMENT:
need_repair_num++;
break;
case WAIT_FOREGT_INVALID_PAGE:
{
forget_range_invalid_pages((void*)&entry->key);
bool found = false;
if (hash_search(file_hash, &(entry->key), HASH_REMOVE, &found) == NULL) {
LWLockRelease(FILE_REPAIR_LOCK);
ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("file repair hash table corrupted")));
} else {
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] file %s seg is %d, repair finish, remove the entry",
relpathperm(entry->key.relfilenode, entry->key.forknum), entry->key.segno)));
}
}
break;
default:
break;
}
}
LWLockRelease(FILE_REPAIR_LOCK);
for (i = 0; i < need_rename_num; i++) {
RepairFileKey *key = &rename_key[i];
RenameRepairFile(key, true);
}
if (need_rename_num > 0) {
pfree(rename_key);
rename_key = NULL;
}
if (need_repair_num == 0) {
SetRecoverySuspend(false);
ereport(LOG, (errmodule(MOD_REDO),
errmsg("set recovery suspend to false, the need repair num is zero")));
}
}
void CheckIsStopRecovery(void)
{
uint32 need_repair_num = 0;
uint32 need_rename_num = 0;
HASH_SEQ_STATUS status;
RepairFileEntry *entry = NULL;
HTAB *file_hash = g_instance.repair_cxt.file_repair_hashtbl;
if (file_hash == NULL) {
return;
}
if (LWLockConditionalAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE)) {
if (hash_get_num_entries(file_hash) == 0) {
LWLockRelease(FILE_REPAIR_LOCK);
return;
}
XLogRecPtr repaly = GetXLogReplayRecPtr(NULL, NULL);
hash_seq_init(&status, file_hash);
while ((entry = (RepairFileEntry *)hash_seq_search(&status)) != NULL) {
if (!XLogRecPtrIsInvalid(entry->min_recovery_point) && XLByteLT(entry->min_recovery_point, repaly)
&& entry->file_state == WAIT_FILE_CHECK_REPAIR) {
entry->file_state = WAIT_FILE_REMOTE_READ;
}
if (entry->file_state == WAIT_FILE_REMOTE_READ || entry->file_state == WAIT_FILE_REPAIR_SEGMENT) {
need_repair_num++;
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] need remote read or segment file wait rename, file path %s segno is %u",
relpathperm(entry->key.relfilenode, entry->key.forknum), entry->key.segno)));
}
if ((entry->file_state == WAIT_FILE_REPAIR && !IsSegmentFileNode(entry->key.relfilenode) &&
XLByteLT(entry->primary_file_lsn, repaly)) || entry->file_state == WAIT_RENAME) {
entry->file_state = WAIT_RENAME;
need_rename_num++;
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] need rename, file path %s segno is %u",
relpathperm(entry->key.relfilenode, entry->key.forknum), entry->key.segno)));
}
}
LWLockRelease(FILE_REPAIR_LOCK);
if (need_repair_num > 0 || need_rename_num > 0) {
load_server_mode();
if (NOT_SUPPORT_PAGE_REPAIR) {
return;
}
if (t_thrd.xlog_cxt.server_mode == STANDBY_MODE && g_instance.repair_cxt.support_repair) {
SetRecoverySuspend(true);
ereport(LOG, (errmodule(MOD_REDO),
errmsg("set recovery suspend to true, the need repair num is %d, need rename num is %d",
need_repair_num, need_rename_num)));
}
}
}
}
const int REPAIR_LEN = 8;
int CreateRepairFile(char *path)
{
int fd = -1;
int retry_times = 0;
const int MAX_RETRY_TIME = 2;
errno_t rc;
char *reapirpath = (char *)palloc(strlen(path) + REPAIR_LEN);
rc = sprintf_s(reapirpath, strlen(path) + REPAIR_LEN, "%s.repair", path);
securec_check_ss(rc, "", "");
RETRY:
fd = BasicOpenFile((char*)reapirpath, O_CREAT | O_RDWR | PG_BINARY, S_IRUSR | S_IWUSR);
retry_times++;
if (fd < 0) {
if (retry_times < MAX_RETRY_TIME) {
goto RETRY;
}
if (errno != ENOENT) {
ereport(WARNING, (errcode_for_file_access(),
errmsg("[file repair] could not open file \"%s\": %m", reapirpath)));
pfree(reapirpath);
return -1;
}
}
pfree(reapirpath);
return fd;
}
int WriteRepairFile(int fd, char* path, char *buf, uint32 offset, uint32 size)
{
errno_t rc = 0;
char *reapirpath = (char *)palloc(strlen(path) + REPAIR_LEN);
rc = sprintf_s(reapirpath, strlen(path) + REPAIR_LEN, "%s.repair", path);
securec_check_ss(rc, "", "");
if (lseek(fd, offset, SEEK_SET) < 0) {
ereport(WARNING, (errcode_for_file_access(), errmsg("[file repair] could not seek reapir file %s : %m",
reapirpath)));
pfree(reapirpath);
return -1;
}
if (write(fd, buf, size) != size) {
if (errno == 0) {
errno = ENOSPC;
}
ereport(WARNING, (errcode_for_file_access(), errmsg("[file repair] could not write to temp file %s : %m",
reapirpath)));
pfree(reapirpath);
return -1;
}
if (fsync(fd) != 0) {
ereport(WARNING, (errcode_for_file_access(), errmsg("[file repair] could not fsync temp file %s : %m",
reapirpath)));
pfree(reapirpath);
return -1;
}
pfree(reapirpath);
return 0;
}
void UnlinkOldBadFile(char *path, RepairFileKey key)
{
if (IsExtremeRedo()) {
ExtremeDispatchClosefdMarkToAllRedoWorker();
ExtremeWaitAllReplayWorkerIdle();
} else if (IsParallelRedo()) {
parallel_recovery::SendClosefdMarkToAllWorkers();
parallel_recovery::WaitAllPageWorkersQueueEmpty();
} else {
XLogRecPtr standby_replay_lsn = GetXLogReplayRecPtr(NULL, NULL);
XLogRecPtr suspend_lsn = pg_atomic_read_u64(&g_instance.startup_cxt.suspend_lsn);
while (!XLByteLE(suspend_lsn, standby_replay_lsn)) {
PageRepairHandleInterrupts();
pg_usleep(1000000L);
(void)GetXLogReplayRecPtr(NULL, &standby_replay_lsn);
}
}
RequestCheckpoint(CHECKPOINT_FLUSH_DIRTY|CHECKPOINT_WAIT);
RelFileNodeBackend rnode;
rnode.node = key.relfilenode;
rnode.backend = InvalidBackendId;
CacheInvalidateSmgr(rnode);
int ret = unlink(path);
if (ret < 0 && errno != ENOENT) {
ereport(WARNING, (errcode_for_file_access(), errmsg("[file repair] could not remove file \"%s\": %m", path)));
}
if (ret >= 0) {
ereport(LOG, (errcode_for_file_access(), errmsg("[file repair] remove file \"%s\": %m", path)));
}
CacheInvalidateSmgr(rnode);
LockRelFileNode(key.relfilenode, AccessExclusiveLock);
RangeForgetBuffer(key.relfilenode, key.forknum, key.segno * RELSEG_SIZE, (key.segno + 1) * RELSEG_SIZE);
UnlockRelFileNode(key.relfilenode, AccessExclusiveLock);
return;
}
static void RepairSegFile(RepairFileKey key, char *segpath, uint32 seg_no, uint32 max_segno, uint64 size)
{
char *buf = 0;
int ret_code = REMOTE_READ_NEED_WAIT;
int fd = -1;
errno_t rc = 0;
struct stat statBuf;
uint32 seg_size = 0;
uint32 remote_size = 0;
RemoteReadFileKey read_key;
XLogRecPtr remote_lsn = InvalidXLogRecPtr;
XLogRecPtr standby_flush_lsn = InvalidXLogRecPtr;
bool found = false;
BlockNumber relSegSize =
IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM) ? CFS_LOGIC_BLOCKS_PER_FILE : RELSEG_SIZE;
fd = CreateRepairFile(segpath);
if (fd < 0) {
ereport(WARNING, (errcode_for_file_access(),
errmsg("[file repair] could not create repair file \"%s\", segno is %d",
relpathperm(key.relfilenode, key.forknum), seg_no)));
return;
}
read_key.relfilenode = key.relfilenode;
read_key.forknum = key.forknum;
read_key.blockstart = seg_no * relSegSize;
int batch_size = MAX_BATCH_READ_BLOCKNUM * BLCKSZ;
buf = (char*)palloc((uint32)batch_size);
seg_size = (uint32)((seg_no < max_segno || (size % (relSegSize * BLCKSZ)) == 0) ?
(relSegSize * BLCKSZ) : (size % (relSegSize * BLCKSZ)));
int max_times = seg_size % batch_size == 0 ? seg_size / batch_size : (seg_size / batch_size + 1);
for (int j = 0; j < max_times; j++) {
int read_size = 0;
if (seg_size % batch_size != 0) {
read_size = (j == max_times - 1 ? seg_size % batch_size : batch_size);
} else {
read_size = batch_size;
}
read_key.blockstart = seg_no * relSegSize + j * MAX_BATCH_READ_BLOCKNUM;
ret_code = RemoteReadFileNoError(&read_key, buf, InvalidXLogRecPtr, read_size, &remote_lsn, &remote_size);
if (ret_code == REMOTE_READ_OK) {
if (IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM)) {
rc = WriteRepairFile_Compress(key.relfilenode, fd, segpath, buf,
(BlockNumber)(j * MAX_BATCH_READ_BLOCKNUM), (uint32)(read_size / BLCKSZ));
} else {
rc = WriteRepairFile(fd, segpath, buf, j * batch_size, read_size);
}
if (rc != 0) {
ereport(WARNING, (errcode_for_file_access(),
errmsg("[file repair] could not write repair file \"%s\", segno is %d",
relpathperm(key.relfilenode, key.forknum), seg_no)));
pfree(buf);
(void)close(fd);
return;
}
} else {
ereport(WARNING, (errcode_for_file_access(),
errmsg("[file repair] remote read file failed \"%s\", segno is %d, block start %u",
relpathperm(key.relfilenode, key.forknum), seg_no, read_key.blockstart)));
pfree(buf);
(void)close(fd);
return;
}
}
pfree(buf);
(void)close(fd);
if (ret_code == REMOTE_READ_OK) {
standby_flush_lsn = GetStandbyFlushRecPtr(NULL);
while (!XLByteLT(remote_lsn, standby_flush_lsn)) {
PageRepairHandleInterrupts();
pg_usleep(10000L);
standby_flush_lsn = GetStandbyFlushRecPtr(NULL);
}
ereport(LOG, (errmsg("[file repair] wait lsn flush, remote lsn is %X/%X",
(uint32)(remote_lsn >> XLOG_LSN_SWAP), (uint32)remote_lsn)));
} else {
return;
}
if (!IsSegmentFileNode(key.relfilenode)) {
if (stat(segpath, &statBuf) < 0) {
if (errno != ENOENT) {
ereport(WARNING, (errcode_for_file_access(),
errmsg("[file repair] could not stat file \"%s\" before repair: %m", segpath)));
UnlinkOldBadFile(segpath, key);
}
} else {
UnlinkOldBadFile(segpath, key);
}
}
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
RepairFileEntry *temp_entry = (RepairFileEntry*)hash_search(g_instance.repair_cxt.file_repair_hashtbl, &(key),
HASH_FIND, &found);
if (found) {
temp_entry->file_state = IsSegmentFileNode(key.relfilenode) ? WAIT_FILE_REPAIR_SEGMENT : WAIT_FILE_REPAIR;
temp_entry->primary_file_lsn = remote_lsn;
}
LWLockRelease(FILE_REPAIR_LOCK);
return;
}
bool CheckAllSegmentFileRepair(RepairFileKey key, uint32 max_segno)
{
uint32 repair_num = 0;
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
for (uint i = 0; i <= max_segno; i++) {
RepairFileKey temp_key;
bool found = false;
temp_key.relfilenode.relNode = key.relfilenode.relNode;
temp_key.relfilenode.dbNode = key.relfilenode.dbNode;
temp_key.relfilenode.spcNode = key.relfilenode.spcNode;
temp_key.relfilenode.bucketNode = key.relfilenode.bucketNode;
temp_key.relfilenode.bucketNode = (int2)key.relfilenode.opt;
temp_key.forknum = key.forknum;
temp_key.segno = i;
RepairFileEntry *entry = (RepairFileEntry*)hash_search(g_instance.repair_cxt.file_repair_hashtbl,
&(temp_key), HASH_FIND, &found);
Assert(found);
if (found && entry->file_state == WAIT_FILE_REPAIR_SEGMENT) {
repair_num++;
}
}
LWLockRelease(FILE_REPAIR_LOCK);
if (repair_num == max_segno + 1) {
RepairFileKey rename_key = {0};
for (uint i = 0; i <= max_segno; i++) {
bool found = false;
rename_key.relfilenode.relNode = key.relfilenode.relNode;
rename_key.relfilenode.dbNode = key.relfilenode.dbNode;
rename_key.relfilenode.spcNode = key.relfilenode.spcNode;
rename_key.relfilenode.bucketNode = key.relfilenode.bucketNode;
rename_key.relfilenode.opt = key.relfilenode.opt;
rename_key.forknum = key.forknum;
rename_key.segno = i;
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
(void*)hash_search(g_instance.repair_cxt.file_repair_hashtbl, &(rename_key), HASH_FIND, &found);
Assert(found);
LWLockRelease(FILE_REPAIR_LOCK);
if (found) {
RenameRepairFile(&rename_key, false);
}
}
df_open_all_file(rename_key, max_segno);
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
RepairFileKey change_key;
for (uint i = 0; i <= max_segno; i++) {
bool found = false;
change_key.relfilenode.relNode = key.relfilenode.relNode;
change_key.relfilenode.dbNode = key.relfilenode.dbNode;
change_key.relfilenode.spcNode = key.relfilenode.spcNode;
change_key.relfilenode.bucketNode = key.relfilenode.bucketNode;
change_key.relfilenode.opt = key.relfilenode.opt;
change_key.forknum = key.forknum;
change_key.segno = i;
RepairFileEntry *entry = (RepairFileEntry*)hash_search(g_instance.repair_cxt.file_repair_hashtbl,
&(change_key), HASH_FIND, &found);
Assert(found);
if (found) {
entry->file_state = WAIT_FOREGT_INVALID_PAGE;
}
}
LWLockRelease(FILE_REPAIR_LOCK);
return true;
}
return false;
}
void StandbyRemoteReadFile(RepairFileKey key)
{
int ret_code;
int64 size = 0;
errno_t rc;
bool found = false;
char *path = relpathperm(key.relfilenode, key.forknum);
int64 segpathlen = (int64)(strlen(path) + SEGLEN + strlen(COMPRESS_STR));
char *segpath = (char *)palloc((Size)segpathlen);
RETYR:
ret_code = RemoteReadFileSizeNoError(&key, &size);
if (ret_code == REMOTE_READ_OK) {
uint32 max_segno = 0;
if (size <= 0) {
pfree(path);
pfree(segpath);
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
RepairFileEntry *temp_entry = (RepairFileEntry*)hash_search(g_instance.repair_cxt.file_repair_hashtbl,
&(key), HASH_FIND, &found);
if (found) {
temp_entry->file_state = WAIT_CLEAN;
}
LWLockRelease(FILE_REPAIR_LOCK);
return;
}
BlockNumber relSegSize =
IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM) ? CFS_LOGIC_BLOCKS_PER_FILE: RELSEG_SIZE;
max_segno = (uint64)size / (relSegSize * BLCKSZ);
if (key.segno > max_segno) {
ereport(WARNING, (errcode_for_file_access(),
errmsg("[file repair] primary this file %s , segno is %d also not exist, can not repair, wait clean",
relpathperm(key.relfilenode, key.forknum), key.segno)));
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
RepairFileEntry *temp_entry = (RepairFileEntry*)hash_search(g_instance.repair_cxt.file_repair_hashtbl,
&(key), HASH_FIND, &found);
if (found) {
temp_entry->file_state = WAIT_CLEAN;
}
LWLockRelease(FILE_REPAIR_LOCK);
pfree(path);
pfree(segpath);
return;
}
if (IsSegmentFileNode(key.relfilenode)) {
WaitRepalyFinish();
RequestCheckpoint(CHECKPOINT_FLUSH_DIRTY|CHECKPOINT_WAIT);
df_clear_and_close_all_file(key, max_segno);
}
if (key.segno == 0) {
rc = sprintf_s(segpath, (uint64)segpathlen, "%s%s", path,
IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
} else {
rc = sprintf_s(segpath, (uint64)segpathlen, "%s.%u%s", path, key.segno,
IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
}
securec_check_ss(rc, "", "");
RepairSegFile(key, segpath, key.segno, max_segno, size);
checkOtherFile(key, max_segno, size);
if (IsSegmentFileNode(key.relfilenode) && !CheckAllSegmentFileRepair(key, max_segno)) {
goto RETYR;
}
}
pfree(path);
pfree(segpath);
return;
}
static void checkOtherFile(RepairFileKey key, uint32 max_segno, uint64 size)
{
errno_t rc;
bool found = false;
struct stat statBuf;
RepairFileKey temp_key;
RepairFileEntry *temp_entry = NULL;
char *path = relpathperm(key.relfilenode, key.forknum);
int64 segpathlen = (int64)(strlen(path) + SEGLEN + strlen(COMPRESS_STR));
char *segpath = (char *)palloc((Size)segpathlen);
HTAB *file_hash = g_instance.repair_cxt.file_repair_hashtbl;
for (uint i = 0; i <= max_segno; i++) {
if (i == 0) {
rc = sprintf_s(segpath, segpathlen, "%s%s", path,
IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
} else {
rc = sprintf_s(segpath, (uint64)segpathlen, "%s.%u%s", path, i,
IS_COMPRESSED_RNODE(key.relfilenode, MAIN_FORKNUM) ? COMPRESS_STR : "");
}
securec_check_ss(rc, "", "");
if (i == key.segno) {
continue;
}
temp_key.relfilenode.relNode = key.relfilenode.relNode;
temp_key.relfilenode.dbNode = key.relfilenode.dbNode;
temp_key.relfilenode.spcNode = key.relfilenode.spcNode;
temp_key.relfilenode.bucketNode = key.relfilenode.bucketNode;
temp_key.relfilenode.opt = key.relfilenode.opt;
temp_key.forknum = key.forknum;
temp_key.segno = i;
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
temp_entry = (RepairFileEntry*)hash_search(file_hash, &(temp_key), HASH_FIND, &found);
if (found && temp_entry->file_state == WAIT_FILE_REMOTE_READ) {
LWLockRelease(FILE_REPAIR_LOCK);
RepairSegFile(temp_key, segpath, i, max_segno, size);
continue;
}
LWLockRelease(FILE_REPAIR_LOCK);
if (stat(segpath, &statBuf) == 0) {
continue;
}
if (stat(segpath, &statBuf) < 0 && errno != ENOENT) {
continue;
}
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
temp_entry = (RepairFileEntry*)hash_search(file_hash, &(temp_key), HASH_ENTER, &found);
if (!found) {
LWLockAcquire(ControlFileLock, LW_SHARED);
XLogRecPtr min_recovery_point = t_thrd.shemem_ptr_cxt.ControlFile->minRecoveryPoint;
LWLockRelease(ControlFileLock);
temp_entry->key = temp_key;
temp_entry->min_recovery_point = min_recovery_point;
temp_entry->file_state = WAIT_FILE_REMOTE_READ;
temp_entry->primary_file_lsn = InvalidXLogRecPtr;
ereport(LOG, (errmodule(MOD_REDO),
errmsg("[file repair] check other seg file push to file repair hashtbl, path is %s segno is %u",
relpathperm(key.relfilenode, key.forknum), i)));
LWLockRelease(FILE_REPAIR_LOCK);
RepairSegFile(temp_key, segpath, i, max_segno, size);
} else {
LWLockRelease(FILE_REPAIR_LOCK);
}
}
pfree(path);
pfree(segpath);
return;
}
const int MAX_FILE_REPAIR_NUM = 10;
static void SeqRemoteReadFile()
{
HTAB *repair_hash = g_instance.repair_cxt.file_repair_hashtbl;
RepairFileEntry *entry = NULL;
HASH_SEQ_STATUS status;
uint32 need_repair_num = 0;
errno_t rc = 0;
RepairFileKey remote_read[MAX_FILE_REPAIR_NUM] = {0};
pg_memory_barrier();
if (!RecoveryIsSuspend() || XLogRecPtrIsInvalid(g_instance.startup_cxt.suspend_lsn)) {
return;
}
rc = memset_s(remote_read, sizeof(RepairFileEntry) * MAX_FILE_REPAIR_NUM, 0,
sizeof(RepairFileEntry) * MAX_FILE_REPAIR_NUM);
securec_check(rc, "", "");
WaitRepalyFinish();
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
hash_seq_init(&status, repair_hash);
while ((entry = (RepairFileEntry *)hash_seq_search(&status)) != NULL) {
switch (entry->file_state) {
case WAIT_FILE_CHECK_REPAIR:
case WAIT_FILE_REPAIR:
case WAIT_FOREGT_INVALID_PAGE:
case WAIT_CLEAN:
case WAIT_RENAME:
break;
case WAIT_FILE_REPAIR_SEGMENT:
case WAIT_FILE_REMOTE_READ:
entry->file_state = WAIT_FILE_REMOTE_READ;
if (need_repair_num >= MAX_FILE_REPAIR_NUM) {
break;
} else {
rc = memcpy_s(&remote_read[need_repair_num], sizeof(RepairFileEntry),
entry, sizeof(RepairFileEntry));
securec_check(rc, "", "");
}
break;
default:
LWLockRelease(FILE_REPAIR_LOCK);
ereport(ERROR, (errmsg("[file repair] error file state during remote read")));
break;
}
}
LWLockRelease(FILE_REPAIR_LOCK);
for (uint32 i = 0; i < need_repair_num; i++) {
RepairFileKey temp = remote_read[i];
StandbyRemoteReadFile(temp);
}
LWLockAcquire(FILE_REPAIR_LOCK, LW_EXCLUSIVE);
hash_seq_init(&status, repair_hash);
while ((entry = (RepairFileEntry *)hash_seq_search(&status)) != NULL) {
if (entry->file_state == WAIT_FILE_REPAIR_SEGMENT || entry->file_state == WAIT_FILE_REMOTE_READ ||
entry->file_state == WAIT_RENAME) {
need_repair_num++;
}
}
LWLockRelease(FILE_REPAIR_LOCK);
if (need_repair_num == 0) {
SetRecoverySuspend(false);
ereport(LOG, (errmodule(MOD_REDO),
errmsg("pagerepair thread set recovery suspend to false, the need repair num is zero")));
}
return;
}