* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
* ubs-engine 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.
*/
#include "ubse_election_role_standby.h"
#include "ubse_context.h"
#include "ubse_election_role_mgr.h"
#include "ubse_logger_audit.h"
namespace ubse::election {
using namespace ubse::module;
UBSE_DEFINE_THIS_MODULE("ubse");
using namespace ubse::context;
using namespace ::ubse::common::def;
Standby::Standby(RoleContext& ctx) : turnId_(0), lastHeartTime_()
{
Node myself;
if (UBSE_ERROR == UbseElectionNodeMgr::GetInstance().GetMyselfNode(myself)) {
UBSE_LOG_ERROR << "[ELECTION] Master GetMyselfNode: no node found.";
return;
}
standbyId_ = myself.id;
masterId_ = ctx.masterId;
turnId_ = ctx.turnId;
auto result = GetBootTime(lastHeartTime_);
if (result != UBSE_OK) {
UBSE_LOG_WARN << "[ELECTION] GetBootTime fail";
}
UBSE_LOG_INFO << "[ELECTION] Standby start ProcTimer: " << standbyId_ << ".";
}
void Standby::ProcTimer()
{
uint32_t standbyLostHbSwitchThreshold = ElectionRole::GetHbLostTimes();
if (IsStandbyHeartBeatTimeout(standbyLostHbSwitchThreshold) && GetElectionCandidate()) {
UBSE_LOG_INFO << "[ELECTION] Standby ProcTimer: switch Master";
UBSE_AUDIT_RUNTIME_ALLOC << "Current node switched from standby to master, node ID: " << standbyId_;
SwitchMaster();
} else if (IsStandbyHeartBeatTimeout(standbyLostHbSwitchThreshold) && !GetElectionCandidate()) {
RoleContext ctx;
UBSE_LOG_INFO << "[ELECTION] Standby ProcTimer: switch Initializer";
RoleMgr::GetInstance().SwitchRole(RoleType::INITIALIZER, ctx);
}
}
void Standby::SwitchMaster()
{
RoleContext ctx;
ctx.masterId = standbyId_;
ctx.standbyId = INVALID_NODE_ID;
ctx.turnId = turnId_ + 1;
UbseContext::GetInstance().SetWorkReadiness(NOT_READY);
RoleMgr::GetInstance().SwitchRole(RoleType::MASTER, ctx);
RoleMgr::GetInstance().RoleChangeNotifyAsync(UbseElectionEventType::STANDBY_CHANGE_TO_MASTER, ctx.masterId);
}
void HandleMasterOnlineNotification(const ElectionPkt& rcvPkt, ElectionReplyPkt& reply)
{
if (rcvPkt.broadcast == 0) {
RoleMgr::GetInstance().RoleChangeNotifyAsync(UbseElectionEventType::MASTER_ONLINE_NOTIFICATION,
rcvPkt.masterId);
UBSE_LOG_INFO << "[ELECTION] The Master is online: " << rcvPkt.masterId << ", turnId is: " << rcvPkt.turnId;
reply.broadcast = 1;
}
}
uint32_t Standby::RecvPkt(UBSE_ID_TYPE srcID, const ElectionPkt rcvPkt, ElectionReplyPkt& reply)
{
if (rcvPkt.type == ELECTION_PKT_TYPE_SELECT) {
reply.replyId = standbyId_;
reply.masterId = masterId_;
reply.replyResult = ELECTION_PKT_TYPE_REJECT_HAS_MASTER;
} else if (rcvPkt.type == ELECTION_PKT_TYPE_HEART) {
RecvPktForHeart(rcvPkt, reply);
}
return 0;
}
void Standby::RecvPktForHeart(const ElectionPkt& rcvPkt, ElectionReplyPkt& reply)
{
if (rcvPkt.masterId == masterId_) {
if (rcvPkt.standbyId == standbyId_) {
turnId_ = rcvPkt.turnId;
sequenceId_ = rcvPkt.sequenceId;
auto ret = GetBootTime(lastHeartTime_);
if (ret != UBSE_OK) {
UBSE_LOG_WARN << "[ELECTION] GetBootTime fail";
}
masterStatus_ = rcvPkt.masterStatus;
auto currentStatus = UbseContext::GetInstance().GetWorkReadiness();
reply.standbyStatus = currentStatus;
reply.replyId = standbyId_;
reply.replyResult = ELECTION_PKT_RESULT_ACCEPT;
agentIds_ = rcvPkt.agentIds;
HandleMasterOnlineNotification(rcvPkt, reply);
} else {
RoleContext ctx;
ctx.masterId = rcvPkt.masterId;
ctx.standbyId = rcvPkt.standbyId;
ctx.turnId = rcvPkt.turnId;
RoleMgr::GetInstance().SwitchRole(RoleType::AGENT, ctx);
reply.replyResult = ELECTION_PKT_RESULT_ACCEPT;
}
} else {
uint32_t acceptMasterAfterLossThreshold = (ElectionRole::GetHbLostTimes() - NO_1);
if (IsStandbyHeartBeatTimeout(acceptMasterAfterLossThreshold)) {
reply.replyResult = ELECTION_PKT_RESULT_ACCEPT;
RoleContext ctx;
ctx.masterId = rcvPkt.masterId;
ctx.standbyId = rcvPkt.standbyId;
ctx.turnId = rcvPkt.turnId;
RoleMgr::GetInstance().SwitchRole(RoleType::AGENT, ctx);
} else {
reply.replyId = standbyId_;
reply.replyResult = ELECTION_PKT_TYPE_REJECT_HAS_MASTER;
}
}
}
UBSE_ID_TYPE Standby::GetMasterNode()
{
return masterId_;
}
UBSE_ID_TYPE Standby::GetStandbyNode()
{
return standbyId_;
}
std::vector<UBSE_ID_TYPE> Standby::GetAgentNodes()
{
return agentIds_;
}
uint8_t Standby::GetMasterStatus()
{
return masterStatus_;
}
uint8_t Standby::GetStandbyStatus()
{
auto currentStatus = UbseContext::GetInstance().GetWorkReadiness();
return currentStatus;
}
bool Standby::IsStandbyHeartBeatTimeout(uint32_t heartbeatMultiplier) const
{
uint64_t bootTime;
auto ret = GetBootTime(bootTime);
if (ret != UBSE_OK) {
UBSE_LOG_WARN << "[ELECTION] GetBootTime fail";
return false;
}
if (bootTime < lastHeartTime_) {
UBSE_LOG_WARN << "[ELECTION] Current boot time is earlier than last heart time.";
return false;
}
uint64_t timeSinceLastHeartbeat = bootTime - lastHeartTime_;
uint32_t maxAllowedHeartbeatInterval = heartbeatMultiplier * GetHeartTimeInterval();
return timeSinceLastHeartbeat > maxAllowedHeartbeatInterval;
}
}