/*
 * 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; /* are we fetching a checkpoint record? */
    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 the xlog record last_lsn equal the current standby page lsn, means found a complete xlog chain */
            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 the xlog record last lsn */
            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;
}

/* CheckPrimaryPageLSN
 *          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);

    /* page lsn is the xlog EndRecPtr, so need parse the next record, get the prev lsn */
    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) {
        /* hash accessed by database file id */
        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;
}

/* CopyPageToRepairHashTbl
 *                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;
        }
        /* after release the lock, check the page lsn where the error_type is LSN_CHECK_FAIL */
        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)
{
    /* get remote address */
    char remote_address1[MAXPGPATH] = {0}; /* remote_address1[0] = '\0'; */
    char remote_address2[MAXPGPATH] = {0}; /* remote_address2[0] = '\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);
    /* return file size + primary lsn */
    PROFILING_REMOTE_END_READ(sizeof(uint64) + sizeof(uint64), (retCode == REMOTE_READ_OK));
    return retCode;
}

/* RemoteReadFile
 *             standby dn use this function repair file.
 */
int RemoteReadFileNoError(RemoteReadFileKey *key, char *buf, XLogRecPtr lsn, uint32 size,
    XLogRecPtr *remote_lsn, uint32 *remote_size)
{
    /* get remote address */
    char remote_address1[MAXPGPATH] = {0}; /* remote_address1[0] = '\0'; */
    char remote_address2[MAXPGPATH] = {0}; /* remote_address2[0] = '\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)
{
    /* get remote address */
    char remote_address1[MAXPGPATH] = {0}; /* remote_address1[0] = '\0'; */
    char remote_address2[MAXPGPATH] = {0}; /* remote_address2[0] = '\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;

    /* we need check the pca header page first if the relation is compression relation,
       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) {
                /* clear local cache and smgr */
                CacheInvalidateSmgr(smgr->smgr_rnode);

                /* try repair the whole extent */
                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();

    /* We allow SIGQUIT (quickdie) at all times */
    (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); /* hard crash time */
    (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);
}

/* recovery thread function */

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) {
        /* need add array and wakeup the page repair thread */
        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;
}

/* BatchClearPageRepairHashTbl
 *         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;
}

/* ClearPageRepairHashTbl
 *         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;
}

/* ClearSpecificsPageRepairHashTbl
 *         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;
}

/* CheckRepairPage
 *               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 ")));
    }

    /* the page_new_lsn is in the range from record_min_lsn to record_max_lsn */
    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()
{
    /* file repair finish, need clean the invalid page */
    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);
        /* if suspend_lsn > standby_replay_lsn then need wait */
        while (!XLByteLE(suspend_lsn, standby_replay_lsn)) {
            /* sleep 1s */
            PageRepairHandleInterrupts();
            pg_usleep(1000000L);
            /* get current replay lsn again */
            (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) {
        /* hash accessed by database file id */
        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);

    /* wait all dirty page flush */
    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)));

        /* file repair finish, need clean the invalid page */
        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 write didn't set errno, assume problem is no disk space */
        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)
{
    /* wait the xlog repaly finish */
    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);
        /* if suspend_lsn > standby_replay_lsn then need wait */
        while (!XLByteLE(suspend_lsn, standby_replay_lsn)) {
            /* sleep 1s */
            PageRepairHandleInterrupts();
            pg_usleep(1000000L);
            /* get current replay lsn again */
            (void)GetXLogReplayRecPtr(NULL, &standby_replay_lsn);
        }
    }
    /* wait all dirty page flush */
    RequestCheckpoint(CHECKPOINT_FLUSH_DIRTY|CHECKPOINT_WAIT);

    /* handle the backend thread */
    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);
    /* invalidate shared buffer about this seg file */
    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();
            /* sleep 10ms */
            pg_usleep(10000L);
            /* get current replay lsn again */
            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;
    }

    /* wait xlog repaly */
    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);
        }
    }

    /* wait xlog repaly finish, need get lock */
    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;

    /* check all slicno file remote read finish */
    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) {
        /* 1. rename all file */
        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);
            }
        }

        /* 2. open all file */
        df_open_all_file(rename_key, max_segno);

        /* 3. change file state */
        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); /* max_segno start from 0 */
        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)) {
            /* wait all dirty page flush */
            WaitRepalyFinish();
            /* wait all dirty page flush */
            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;
        }

        /* Check whether other segment files exist in the hashtable. */
        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);

        /* Check whether other segment files exist */
        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, "", "");
    /* wait the xlog repaly finish */
    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) {
            /* page repair thread only handle need remote read file */
            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;
}