Skip to content

Commit

Permalink
Add a callback for leader resignation (#484)
Browse files Browse the repository at this point in the history
* Callback can decline the resignation request.
  • Loading branch information
greensky00 authored Feb 13, 2024
1 parent b54b09c commit 78bae70
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
9 changes: 8 additions & 1 deletion include/libnuraft/callback.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,14 @@ public:
* Snapshot creation begins.
* ctx: pointer to `uint64_t` (committed_idx).
*/
SnapshotCreationBegin = 26
SnapshotCreationBegin = 26,

/**
* Got a resgination request either automatically or manually.
* ctx: null.
*/
ResignationFromLeader = 27,

};

struct Param {
Expand Down
11 changes: 11 additions & 0 deletions src/raft_server.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,17 @@ void raft_server::yield_leadership(bool immediate_yield,
return;
}

// Callback if necessary.
cb_func::Param param(id_, leader_, successor_id, nullptr);
cb_func::ReturnCode cb_ret =
ctx_->cb_func_.call(cb_func::ResignationFromLeader, &param);

// If callback function decided to refuse this request, return here.
if (cb_ret != cb_func::Ok) {
p_in("[RESIGNATION REQUEST] refused by callback function");
return;
}

recur_lock(lock_);

if (immediate_yield) {
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/raft_package_asio.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ public:
}
}

void dbgLog(const std::string& msg) {
_s_info(myLogWrapper->getLogger()) << msg;
}

TestMgr* getTestMgr() const {
return static_cast<TestMgr*>(sMgr.get());
}
Expand Down
29 changes: 28 additions & 1 deletion tests/unit/raft_server_test.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -1436,7 +1436,34 @@ int leadership_takeover_by_request_test() {
// Request leadership by the current leader, should fail.
CHK_FALSE( s1.raftServer->request_leadership() );

// S3 requests the leadership from S1.
// Set callback function to refuse resignation.
bool refuse_request = true;
s1.ctx->set_cb_func([&](cb_func::Type t, cb_func::Param* p) -> cb_func::ReturnCode {
if (t != cb_func::Type::ResignationFromLeader) {
return cb_default(t, p);
}
if (refuse_request) {
return cb_func::ReturnCode::ReturnNull;
}
return cb_func::ReturnCode::Ok;
});

// S3 requests the leadership from S1, and it is supposed to be declined.
s1.dbgLog(" --- request leadership ---");
CHK_TRUE( s3.raftServer->request_leadership() );
// Send request.
s3.fNet->execReqResp();

// Send heartbeat.
s1.fTimer->invoke( timer_task_type::heartbeat_timer );
s1.fNet->execReqResp();
s1.fNet->execReqResp();

// S1 should still be the leader.
CHK_TRUE( s1.raftServer->is_leader() );

// S3 requests the leadership from S1. Now it should succeed.
refuse_request = false;
s1.dbgLog(" --- request leadership ---");
CHK_TRUE( s3.raftServer->request_leadership() );
// Send request.
Expand Down

0 comments on commit 78bae70

Please sign in to comment.