public boolean handleHbResponse(FrontendHbResponse hbResponse, boolean isReplay) {
        boolean isChanged = false;
        boolean prevIsAlive = isAlive;
        long prevStartTime = startTime;
        if (hbResponse.getStatus() == HbStatus.OK) {
            if (!isAlive && !isReplay) {
                if (GlobalStateMgr.getCurrentState().getHaProtocol() instanceof BDBHA) {
                    BDBHA ha = (BDBHA) GlobalStateMgr.getCurrentState().getHaProtocol();
                    ha.removeUnstableNode(host, GlobalStateMgr.getCurrentState().getNodeMgr().getFollowerCnt());
                }
            }
            isAlive = true;
            queryPort = hbResponse.getQueryPort();
            rpcPort = hbResponse.getRpcPort();
            replayedJournalId = hbResponse.getReplayedJournalId();
            lastUpdateTime = hbResponse.getHbTime();
            startTime = hbResponse.getFeStartTime();
            feVersion = hbResponse.getFeVersion();
            heartbeatErrMsg = "";
            isChanged = true;
            this.heartbeatRetryTimes = 0;
        } else {
            if (this.heartbeatRetryTimes < Config.heartbeat_retry_times) {
                this.heartbeatRetryTimes++;
            } else {
                if (isAlive) {
                    isAlive = false;
                }
                heartbeatErrMsg = hbResponse.getMsg() == null ? "Unknown error" : hbResponse.getMsg();
            }
            // When the master receives an error heartbeat info which status not ok, 
            // this heartbeat info also need to be synced to follower.
            // Since the failed heartbeat info also modifies fe's memory, (this.heartbeatRetryTimes++;)
            // if this heartbeat is not synchronized to the follower, 
            // that will cause the Follower and leader’s memory to be inconsistent
            isChanged = true;
        }
        if (!isReplay) {
            hbResponse.aliveStatus = isAlive ?
                HeartbeatResponse.AliveStatus.ALIVE : HeartbeatResponse.AliveStatus.NOT_ALIVE;
        } else {
            if (hbResponse.aliveStatus != null) {
                // The metadata before the upgrade does not contain hbResponse.aliveStatus,
                // in which case the alive status needs to be handled according to the original logic
                isAlive = hbResponse.aliveStatus == HeartbeatResponse.AliveStatus.ALIVE;
                heartbeatRetryTimes = 0;
            }
        }

        if (!GlobalStateMgr.isCheckpointThread()) {
            if (prevIsAlive && !isAlive) {
                GlobalStateMgr.getCurrentState().getSlotManager().notifyFrontendDeadAsync(nodeName);
            } else if (prevStartTime != 0 && prevStartTime != startTime) {
                GlobalStateMgr.getCurrentState().getSlotManager().notifyFrontendRestartAsync(nodeName, startTime);
            }
        }

        return isChanged;
    }
@Test
    public void testHbStatusBadNeedSync() {
        FrontendHbResponse hbResponse = new FrontendHbResponse("BAD", "");
        
        Frontend fe = new Frontend(FrontendNodeType.FOLLOWER, "name", "testHost", 1110);
        boolean needSync = fe.handleHbResponse(hbResponse, true);
        Assert.assertTrue(needSync);
    }