mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-21 14:43:34 +08:00
Raft event tracing interface, more states of RaftCore, basic functions of RaftCore
This commit is contained in:
@@ -70,6 +70,25 @@ public:
|
|||||||
virtual ~IDynamicNodeIDStorageBackend() { }
|
virtual ~IDynamicNodeIDStorageBackend() { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface allows the application to trace events that happen in the server.
|
||||||
|
*/
|
||||||
|
class IDynamicNodeIDAllocationServerEventTracer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* The server invokes this method every time it believes that a noteworthy event has happened.
|
||||||
|
* The table of event codes can be found in the server sources.
|
||||||
|
* It is guaranteed that event code values will never change, but new ones can be added in future. This ensures
|
||||||
|
* full backward compatibility.
|
||||||
|
* @param event_code Event code, see the sources for the enum with values.
|
||||||
|
* @param event_argument Value associated with the event; its meaning depends on the event code.
|
||||||
|
*/
|
||||||
|
virtual void onEvent(uint16_t event_code, int64_t event_argument) = 0;
|
||||||
|
|
||||||
|
virtual ~IDynamicNodeIDAllocationServerEventTracer() { }
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internals, do not use anything from this namespace directly.
|
* Internals, do not use anything from this namespace directly.
|
||||||
*/
|
*/
|
||||||
@@ -83,6 +102,37 @@ using namespace protocol::dynamic_node_id::server;
|
|||||||
*/
|
*/
|
||||||
typedef StorageType<Entry::FieldTypes::term>::Type Term;
|
typedef StorageType<Entry::FieldTypes::term>::Type Term;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ref IDynamicNodeIDAllocationServerEventTracer.
|
||||||
|
* Event codes cannot be changed, only new ones can be added.
|
||||||
|
*/
|
||||||
|
enum TraceEvent
|
||||||
|
{
|
||||||
|
// Event name Argument
|
||||||
|
// 0
|
||||||
|
TraceError, // error code (may be negated)
|
||||||
|
TraceLogLastIndexRestored, // recovered last index value
|
||||||
|
TraceLogAppend, // index of new entry
|
||||||
|
TraceLogRemove, // new last index value
|
||||||
|
TraceCurrentTermRestored, // current term
|
||||||
|
// 5
|
||||||
|
TraceCurrentTermUpdate, // current term
|
||||||
|
TraceVotedForRestored, // value of votedFor
|
||||||
|
TraceVotedForUpdate, // value of votedFor
|
||||||
|
TraceDiscoveryBroadcast, // number of known servers
|
||||||
|
TraceNewServerDiscovered, // node ID of the new server
|
||||||
|
// 10
|
||||||
|
TraceDiscoveryReceived, // node ID of the sender
|
||||||
|
TraceClusterSizeInited, // cluster size
|
||||||
|
TraceRaftCoreInited, // update interval in usec
|
||||||
|
TraceRaftStateSwitch, // 0 - Follower, 1 - Candidate, 2 - Leader
|
||||||
|
TraceRaftModeSwitch, // 0 - Passive, 1 - Active
|
||||||
|
// 15
|
||||||
|
TraceRaftNewLogEntry, // node ID value
|
||||||
|
|
||||||
|
NumTraceEventCodes
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class extends the storage backend interface with serialization/deserialization functionality.
|
* This class extends the storage backend interface with serialization/deserialization functionality.
|
||||||
*/
|
*/
|
||||||
@@ -137,6 +187,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
IDynamicNodeIDStorageBackend& storage_;
|
IDynamicNodeIDStorageBackend& storage_;
|
||||||
|
IDynamicNodeIDAllocationServerEventTracer& tracer_;
|
||||||
Entry entries_[Capacity];
|
Entry entries_[Capacity];
|
||||||
Index last_index_; // Index zero always contains an empty entry
|
Index last_index_; // Index zero always contains an empty entry
|
||||||
|
|
||||||
@@ -149,8 +200,9 @@ private:
|
|||||||
int initEmptyLogStorage();
|
int initEmptyLogStorage();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Log(IDynamicNodeIDStorageBackend& storage)
|
Log(IDynamicNodeIDStorageBackend& storage, IDynamicNodeIDAllocationServerEventTracer& tracer)
|
||||||
: storage_(storage)
|
: storage_(storage)
|
||||||
|
, tracer_(tracer)
|
||||||
, last_index_(0)
|
, last_index_(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
@@ -195,6 +247,7 @@ public:
|
|||||||
class PersistentState
|
class PersistentState
|
||||||
{
|
{
|
||||||
IDynamicNodeIDStorageBackend& storage_;
|
IDynamicNodeIDStorageBackend& storage_;
|
||||||
|
IDynamicNodeIDAllocationServerEventTracer& tracer_;
|
||||||
|
|
||||||
Term current_term_;
|
Term current_term_;
|
||||||
NodeID voted_for_;
|
NodeID voted_for_;
|
||||||
@@ -204,10 +257,11 @@ class PersistentState
|
|||||||
static IDynamicNodeIDStorageBackend::String getVotedForKey() { return "voted_for"; }
|
static IDynamicNodeIDStorageBackend::String getVotedForKey() { return "voted_for"; }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PersistentState(IDynamicNodeIDStorageBackend& storage)
|
PersistentState(IDynamicNodeIDStorageBackend& storage, IDynamicNodeIDAllocationServerEventTracer& tracer)
|
||||||
: storage_(storage)
|
: storage_(storage)
|
||||||
|
, tracer_(tracer)
|
||||||
, current_term_(0)
|
, current_term_(0)
|
||||||
, log_(storage)
|
, log_(storage, tracer)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
int init();
|
int init();
|
||||||
@@ -257,6 +311,7 @@ class ClusterManager : private TimerBase
|
|||||||
enum { MaxServers = Discovery::FieldTypes::known_nodes::MaxSize };
|
enum { MaxServers = Discovery::FieldTypes::known_nodes::MaxSize };
|
||||||
|
|
||||||
IDynamicNodeIDStorageBackend& storage_;
|
IDynamicNodeIDStorageBackend& storage_;
|
||||||
|
IDynamicNodeIDAllocationServerEventTracer& tracer_;
|
||||||
const Log& log_;
|
const Log& log_;
|
||||||
|
|
||||||
Subscriber<Discovery, DiscoveryCallback> discovery_sub_;
|
Subscriber<Discovery, DiscoveryCallback> discovery_sub_;
|
||||||
@@ -293,9 +348,11 @@ public:
|
|||||||
* @param storage Needed to read the cluster size parameter from the storage
|
* @param storage Needed to read the cluster size parameter from the storage
|
||||||
* @param log Needed to initialize nextIndex[] values after elections
|
* @param log Needed to initialize nextIndex[] values after elections
|
||||||
*/
|
*/
|
||||||
ClusterManager(INode& node, IDynamicNodeIDStorageBackend& storage, const Log& log)
|
ClusterManager(INode& node, IDynamicNodeIDStorageBackend& storage, const Log& log,
|
||||||
|
IDynamicNodeIDAllocationServerEventTracer& tracer)
|
||||||
: TimerBase(node)
|
: TimerBase(node)
|
||||||
, storage_(storage)
|
, storage_(storage)
|
||||||
|
, tracer_(tracer)
|
||||||
, log_(log)
|
, log_(log)
|
||||||
, discovery_sub_(node)
|
, discovery_sub_(node)
|
||||||
, discovery_pub_(node)
|
, discovery_pub_(node)
|
||||||
@@ -348,7 +405,15 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of known servers can only grow, and it never exceeds the cluster size value.
|
||||||
|
* This number does not include the local server.
|
||||||
|
*/
|
||||||
uint8_t getNumKnownServers() const { return num_known_servers_; }
|
uint8_t getNumKnownServers() const { return num_known_servers_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cluster size and quorum size are constant.
|
||||||
|
*/
|
||||||
uint8_t getClusterSize() const { return cluster_size_; }
|
uint8_t getClusterSize() const { return cluster_size_; }
|
||||||
uint8_t getQuorumSize() const { return static_cast<uint8_t>(cluster_size_ / 2U + 1U); }
|
uint8_t getQuorumSize() const { return static_cast<uint8_t>(cluster_size_ / 2U + 1U); }
|
||||||
|
|
||||||
@@ -366,13 +431,13 @@ class RaftCore : private TimerBase
|
|||||||
ServiceResponseDataStructure<AppendEntries::Response>&)>
|
ServiceResponseDataStructure<AppendEntries::Response>&)>
|
||||||
AppendEntriesCallback;
|
AppendEntriesCallback;
|
||||||
|
|
||||||
|
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ServiceCallResult<AppendEntries>&)>
|
||||||
|
AppendEntriesResponseCallback;
|
||||||
|
|
||||||
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ReceivedDataStructure<RequestVote::Request>&,
|
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ReceivedDataStructure<RequestVote::Request>&,
|
||||||
ServiceResponseDataStructure<RequestVote::Response>&)>
|
ServiceResponseDataStructure<RequestVote::Response>&)>
|
||||||
RequestVoteCallback;
|
RequestVoteCallback;
|
||||||
|
|
||||||
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ServiceCallResult<AppendEntries>&)>
|
|
||||||
AppendEntriesResponseCallback;
|
|
||||||
|
|
||||||
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ServiceCallResult<RequestVote>&)>
|
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ServiceCallResult<RequestVote>&)>
|
||||||
RequestVoteResponseCallback;
|
RequestVoteResponseCallback;
|
||||||
|
|
||||||
@@ -383,48 +448,95 @@ class RaftCore : private TimerBase
|
|||||||
ServerStateLeader
|
ServerStateLeader
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IDynamicNodeIDAllocationServerEventTracer& tracer_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* States
|
||||||
|
*/
|
||||||
PersistentState persistent_state_;
|
PersistentState persistent_state_;
|
||||||
Log::Index commit_index_;
|
|
||||||
ClusterManager cluster_;
|
ClusterManager cluster_;
|
||||||
|
Log::Index commit_index_;
|
||||||
|
|
||||||
MonotonicTime last_activity_timestamp_;
|
MonotonicTime last_activity_timestamp_;
|
||||||
bool active_mode_;
|
bool active_mode_;
|
||||||
|
|
||||||
ServerState server_state_;
|
ServerState server_state_;
|
||||||
|
|
||||||
ServiceServer<AppendEntries, AppendEntriesCallback> append_entries_srv_;
|
uint8_t next_server_index_; ///< Next server to query for AE or RV RPC
|
||||||
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
|
uint8_t num_votes_received_in_this_campaign_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transport
|
||||||
|
*/
|
||||||
|
ServiceServer<AppendEntries, AppendEntriesCallback> append_entries_srv_;
|
||||||
ServiceClient<AppendEntries, AppendEntriesResponseCallback> append_entries_client_;
|
ServiceClient<AppendEntries, AppendEntriesResponseCallback> append_entries_client_;
|
||||||
|
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
|
||||||
ServiceClient<RequestVote, RequestVoteResponseCallback> request_vote_client_;
|
ServiceClient<RequestVote, RequestVoteResponseCallback> request_vote_client_;
|
||||||
|
|
||||||
virtual void handleTimerEvent(const TimerEvent&);
|
/**
|
||||||
|
* This constant defines the rate at which internal state updates happen.
|
||||||
|
* It also defines timeouts for AppendEntries and RequestVote RPCs.
|
||||||
|
*/
|
||||||
|
static MonotonicDuration getUpdateInterval() { return MonotonicDuration::fromMSec(50); }
|
||||||
|
|
||||||
|
void trace(TraceEvent event, int64_t argument) { tracer_.onEvent(event, argument); }
|
||||||
|
|
||||||
|
INode& getNode() { return append_entries_srv_.getNode(); }
|
||||||
|
|
||||||
|
void updateFollower(const MonotonicTime& current_time);
|
||||||
|
void updateCandidate(const MonotonicTime& current_time);
|
||||||
|
void updateLeader(const MonotonicTime& current_time);
|
||||||
|
|
||||||
|
void switchState(ServerState new_state);
|
||||||
|
|
||||||
|
void handleAppendEntriesRequest(const ReceivedDataStructure<AppendEntries::Request>& request,
|
||||||
|
ServiceResponseDataStructure<AppendEntries::Response>& response);
|
||||||
|
|
||||||
|
void handleAppendEntriesResponse(const ServiceCallResult<AppendEntries>& result);
|
||||||
|
|
||||||
|
void handleRequestVoteRequest(const ReceivedDataStructure<RequestVote::Request>& request,
|
||||||
|
ServiceResponseDataStructure<RequestVote::Response>& response);
|
||||||
|
|
||||||
|
void handleRequestVoteResponse(const ServiceCallResult<RequestVote>& result);
|
||||||
|
|
||||||
|
virtual void handleTimerEvent(const TimerEvent& event);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RaftCore(INode& node, IDynamicNodeIDStorageBackend& storage)
|
RaftCore(INode& node, IDynamicNodeIDStorageBackend& storage, IDynamicNodeIDAllocationServerEventTracer& tracer)
|
||||||
: TimerBase(node)
|
: TimerBase(node)
|
||||||
, persistent_state_(storage)
|
, tracer_(tracer)
|
||||||
, commit_index_(0) // Per Raft paper, commitIndex must be initialized to zero
|
, persistent_state_(storage, tracer)
|
||||||
, cluster_(node, storage, persistent_state_.getLog())
|
, cluster_(node, storage, persistent_state_.getLog(), tracer)
|
||||||
|
, commit_index_(0) // Per Raft paper, commitIndex must be initialized to zero
|
||||||
, last_activity_timestamp_(node.getMonotonicTime())
|
, last_activity_timestamp_(node.getMonotonicTime())
|
||||||
, active_mode_(true)
|
, active_mode_(true)
|
||||||
, server_state_(ServerStateFollower)
|
, server_state_(ServerStateFollower)
|
||||||
|
, next_server_index_(0)
|
||||||
|
, num_votes_received_in_this_campaign_(0)
|
||||||
, append_entries_srv_(node)
|
, append_entries_srv_(node)
|
||||||
, request_vote_srv_(node)
|
|
||||||
, append_entries_client_(node)
|
, append_entries_client_(node)
|
||||||
|
, request_vote_srv_(node)
|
||||||
, request_vote_client_(node)
|
, request_vote_client_(node)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Once started, the logic runs in the background until destructor is called.
|
* Once started, the logic runs in the background until destructor is called.
|
||||||
|
* @param cluster_size If set, this value will be used and stored in the persistent storage. If not set,
|
||||||
|
* value from the persistent storage will be used. If not set and there's no such key
|
||||||
|
* in the persistent storage, initialization will fail.
|
||||||
*/
|
*/
|
||||||
int init();
|
int init(uint8_t cluster_size = ClusterManager::ClusterSizeUnknown);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts one entry into log. This operation may fail, which will not be reported.
|
* Only the leader can call @ref appendLog().
|
||||||
* Failures are tolerble because all operations are idempotent.
|
|
||||||
*/
|
*/
|
||||||
void appendLog(const Entry& entry);
|
bool isLeader() const { return server_state_ == ServerStateLeader; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts one entry into log.
|
||||||
|
* Failures are tolerble because all operations are idempotent.
|
||||||
|
* This method will trigger an assertion failure and return error if the current node is not the leader.
|
||||||
|
*/
|
||||||
|
int appendLog(const Entry& entry);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is used to perform log searches.
|
* This class is used to perform log searches.
|
||||||
|
|||||||
@@ -286,6 +286,8 @@ int Log::init()
|
|||||||
last_index_ = Index(value);
|
last_index_ = Index(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceLogLastIndexRestored, last_index_);
|
||||||
|
|
||||||
// Restoring log entries - note that index 0 always exists
|
// Restoring log entries - note that index 0 always exists
|
||||||
for (Index index = 0; index <= last_index_; index++)
|
for (Index index = 0; index <= last_index_; index++)
|
||||||
{
|
{
|
||||||
@@ -309,6 +311,8 @@ int Log::append(const Entry& entry)
|
|||||||
return -ErrLogic;
|
return -ErrLogic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceLogAppend, last_index_ + 1U);
|
||||||
|
|
||||||
// If next operations fail, we'll get a dangling entry, but it's absolutely OK.
|
// If next operations fail, we'll get a dangling entry, but it's absolutely OK.
|
||||||
int res = writeEntryToStorage(Index(last_index_ + 1), entry);
|
int res = writeEntryToStorage(Index(last_index_ + 1), entry);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
@@ -345,6 +349,8 @@ int Log::removeEntriesWhereIndexGreaterOrEqual(Index index)
|
|||||||
return -ErrLogic;
|
return -ErrLogic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceLogRemove, index - 1U);
|
||||||
|
|
||||||
MarshallingStorageDecorator io(storage_);
|
MarshallingStorageDecorator io(storage_);
|
||||||
uint32_t new_last_index = index - 1U;
|
uint32_t new_last_index = index - 1U;
|
||||||
int res = io.setAndGetBack(getLastIndexKey(), new_last_index);
|
int res = io.setAndGetBack(getLastIndexKey(), new_last_index);
|
||||||
@@ -437,6 +443,8 @@ int PersistentState::init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceCurrentTermRestored, current_term_);
|
||||||
|
|
||||||
if (current_term_ < last_entry->term)
|
if (current_term_ < last_entry->term)
|
||||||
{
|
{
|
||||||
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState",
|
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState",
|
||||||
@@ -481,6 +489,8 @@ int PersistentState::init()
|
|||||||
voted_for_ = NodeID(uint8_t(stored_voted_for));
|
voted_for_ = NodeID(uint8_t(stored_voted_for));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceVotedForRestored, voted_for_.get());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,6 +502,8 @@ int PersistentState::setCurrentTerm(const Term term)
|
|||||||
return -ErrInvalidParam;
|
return -ErrInvalidParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceCurrentTermUpdate, term);
|
||||||
|
|
||||||
MarshallingStorageDecorator io(storage_);
|
MarshallingStorageDecorator io(storage_);
|
||||||
|
|
||||||
Term tmp = term;
|
Term tmp = term;
|
||||||
@@ -518,6 +530,8 @@ int PersistentState::setVotedFor(const NodeID node_id)
|
|||||||
return -ErrInvalidParam;
|
return -ErrInvalidParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceVotedForUpdate, node_id.get());
|
||||||
|
|
||||||
MarshallingStorageDecorator io(storage_);
|
MarshallingStorageDecorator io(storage_);
|
||||||
|
|
||||||
uint32_t tmp = node_id.get();
|
uint32_t tmp = node_id.get();
|
||||||
@@ -586,6 +600,7 @@ void ClusterManager::addServer(NodeID node_id)
|
|||||||
UAVCAN_ASSERT((num_known_servers_ + 1) < (MaxServers - 2));
|
UAVCAN_ASSERT((num_known_servers_ + 1) < (MaxServers - 2));
|
||||||
if (!isKnownServer(node_id) && node_id.isUnicast())
|
if (!isKnownServer(node_id) && node_id.isUnicast())
|
||||||
{
|
{
|
||||||
|
tracer_.onEvent(TraceNewServerDiscovered, node_id.get());
|
||||||
servers_[num_known_servers_].node_id = node_id;
|
servers_[num_known_servers_].node_id = node_id;
|
||||||
servers_[num_known_servers_].resetIndices(log_);
|
servers_[num_known_servers_].resetIndices(log_);
|
||||||
num_known_servers_ = static_cast<uint8_t>(num_known_servers_ + 1U);
|
num_known_servers_ = static_cast<uint8_t>(num_known_servers_ + 1U);
|
||||||
@@ -600,6 +615,8 @@ void ClusterManager::handleTimerEvent(const TimerEvent&)
|
|||||||
{
|
{
|
||||||
UAVCAN_ASSERT(num_known_servers_ < cluster_size_);
|
UAVCAN_ASSERT(num_known_servers_ < cluster_size_);
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceDiscoveryBroadcast, num_known_servers_);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Filling the message
|
* Filling the message
|
||||||
*/
|
*/
|
||||||
@@ -641,6 +658,8 @@ void ClusterManager::handleTimerEvent(const TimerEvent&)
|
|||||||
|
|
||||||
void ClusterManager::handleDiscovery(const ReceivedDataStructure<Discovery>& msg)
|
void ClusterManager::handleDiscovery(const ReceivedDataStructure<Discovery>& msg)
|
||||||
{
|
{
|
||||||
|
tracer_.onEvent(TraceDiscoveryReceived, msg.getSrcNodeID().get());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Validating cluster configuration
|
* Validating cluster configuration
|
||||||
* If there's a case of misconfiguration, the message will be ignored.
|
* If there's a case of misconfiguration, the message will be ignored.
|
||||||
@@ -730,6 +749,8 @@ int ClusterManager::init(const uint8_t init_cluster_size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracer_.onEvent(TraceClusterSizeInited, cluster_size_);
|
||||||
|
|
||||||
UAVCAN_ASSERT(cluster_size_ > 0);
|
UAVCAN_ASSERT(cluster_size_ > 0);
|
||||||
UAVCAN_ASSERT(cluster_size_ <= MaxServers);
|
UAVCAN_ASSERT(cluster_size_ <= MaxServers);
|
||||||
|
|
||||||
@@ -836,6 +857,158 @@ void ClusterManager::resetAllServerIndices()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RaftCore
|
||||||
|
*/
|
||||||
|
void RaftCore::updateFollower(const MonotonicTime& current_time)
|
||||||
|
{
|
||||||
|
(void)current_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::updateCandidate(const MonotonicTime& current_time)
|
||||||
|
{
|
||||||
|
(void)current_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::updateLeader(const MonotonicTime& current_time)
|
||||||
|
{
|
||||||
|
(void)current_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::switchState(ServerState new_state)
|
||||||
|
{
|
||||||
|
if (server_state_ != new_state)
|
||||||
|
{
|
||||||
|
trace(TraceRaftStateSwitch, new_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::handleAppendEntriesRequest(const ReceivedDataStructure<AppendEntries::Request>& request,
|
||||||
|
ServiceResponseDataStructure<AppendEntries::Response>& response)
|
||||||
|
{
|
||||||
|
(void)request;
|
||||||
|
(void)response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::handleAppendEntriesResponse(const ServiceCallResult<AppendEntries>& result)
|
||||||
|
{
|
||||||
|
(void)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::handleRequestVoteRequest(const ReceivedDataStructure<RequestVote::Request>& request,
|
||||||
|
ServiceResponseDataStructure<RequestVote::Response>& response)
|
||||||
|
{
|
||||||
|
(void)request;
|
||||||
|
(void)response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::handleRequestVoteResponse(const ServiceCallResult<RequestVote>& result)
|
||||||
|
{
|
||||||
|
(void)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaftCore::handleTimerEvent(const TimerEvent& event)
|
||||||
|
{
|
||||||
|
switch (server_state_)
|
||||||
|
{
|
||||||
|
case ServerStateFollower:
|
||||||
|
{
|
||||||
|
updateFollower(event.real_time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ServerStateCandidate:
|
||||||
|
{
|
||||||
|
updateCandidate(event.real_time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ServerStateLeader:
|
||||||
|
{
|
||||||
|
updateLeader(event.real_time);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
UAVCAN_ASSERT(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int RaftCore::init(uint8_t cluster_size)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Initializing state variables
|
||||||
|
*/
|
||||||
|
last_activity_timestamp_ = getNode().getMonotonicTime();
|
||||||
|
active_mode_ = true;
|
||||||
|
server_state_ = ServerStateFollower;
|
||||||
|
next_server_index_ = 0;
|
||||||
|
num_votes_received_in_this_campaign_ = 0;
|
||||||
|
commit_index_ = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initializing internals
|
||||||
|
*/
|
||||||
|
int res = persistent_state_.init();
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = cluster_.init(cluster_size);
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append_entries_srv_.start(AppendEntriesCallback(this, &RaftCore::handleAppendEntriesRequest));
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = request_vote_srv_.start(RequestVoteCallback(this, &RaftCore::handleRequestVoteRequest));
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append_entries_client_.init();
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
append_entries_client_.setRequestTimeout(getUpdateInterval());
|
||||||
|
|
||||||
|
res = request_vote_client_.init();
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
request_vote_client_.setRequestTimeout(getUpdateInterval());
|
||||||
|
|
||||||
|
startPeriodic(getUpdateInterval());
|
||||||
|
|
||||||
|
trace(TraceRaftCoreInited, getUpdateInterval().toUSec());
|
||||||
|
|
||||||
|
UAVCAN_ASSERT(res >= 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int RaftCore::appendLog(const Entry& entry)
|
||||||
|
{
|
||||||
|
if (isLeader())
|
||||||
|
{
|
||||||
|
trace(TraceRaftNewLogEntry, entry.node_id);
|
||||||
|
return persistent_state_.getLog().append(entry);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UAVCAN_ASSERT(0);
|
||||||
|
return -ErrLogic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // dynamic_node_id_server_impl
|
} // dynamic_node_id_server_impl
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,15 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class EventTracer : public uavcan::IDynamicNodeIDAllocationServerEventTracer
|
||||||
|
{
|
||||||
|
virtual void onEvent(uavcan::uint16_t event_code, uavcan::int64_t event_argument)
|
||||||
|
{
|
||||||
|
std::cout << "Event\t" << event_code << "\t" << event_argument << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static const unsigned NumEntriesInStorageWithEmptyLog = 4; // last index + 3 items per log entry
|
static const unsigned NumEntriesInStorageWithEmptyLog = 4; // last index + 3 items per log entry
|
||||||
|
|
||||||
|
|
||||||
@@ -140,10 +149,11 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator)
|
|||||||
|
|
||||||
TEST(DynamicNodeIDAllocationServer, LogInitialization)
|
TEST(DynamicNodeIDAllocationServer, LogInitialization)
|
||||||
{
|
{
|
||||||
|
EventTracer tracer;
|
||||||
// No log data in the storage - initializing empty log
|
// No log data in the storage - initializing empty log
|
||||||
{
|
{
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
|
|
||||||
ASSERT_EQ(0, storage.getNumKeys());
|
ASSERT_EQ(0, storage.getNumKeys());
|
||||||
ASSERT_LE(0, log.init());
|
ASSERT_LE(0, log.init());
|
||||||
@@ -157,7 +167,7 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
|
|||||||
// Nonempty storage, one item
|
// Nonempty storage, one item
|
||||||
{
|
{
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
|
|
||||||
storage.set("log_last_index", "0");
|
storage.set("log_last_index", "0");
|
||||||
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Expected one entry, none found
|
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Expected one entry, none found
|
||||||
@@ -174,7 +184,7 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
|
|||||||
// Nonempty storage, broken data
|
// Nonempty storage, broken data
|
||||||
{
|
{
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
|
|
||||||
storage.set("log_last_index", "foobar");
|
storage.set("log_last_index", "foobar");
|
||||||
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Bad value
|
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Bad value
|
||||||
@@ -197,7 +207,7 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
|
|||||||
// Nonempty storage, many items
|
// Nonempty storage, many items
|
||||||
{
|
{
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
|
|
||||||
storage.set("log_last_index", "1"); // 2 items - 0, 1
|
storage.set("log_last_index", "1"); // 2 items - 0, 1
|
||||||
storage.set("log0_term", "0");
|
storage.set("log0_term", "0");
|
||||||
@@ -235,8 +245,9 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
|
|||||||
|
|
||||||
TEST(DynamicNodeIDAllocationServer, LogAppend)
|
TEST(DynamicNodeIDAllocationServer, LogAppend)
|
||||||
{
|
{
|
||||||
|
EventTracer tracer;
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
|
|
||||||
ASSERT_EQ(0, storage.getNumKeys());
|
ASSERT_EQ(0, storage.getNumKeys());
|
||||||
ASSERT_LE(0, log.init());
|
ASSERT_LE(0, log.init());
|
||||||
@@ -307,8 +318,9 @@ TEST(DynamicNodeIDAllocationServer, LogAppend)
|
|||||||
|
|
||||||
TEST(DynamicNodeIDAllocationServer, LogRemove)
|
TEST(DynamicNodeIDAllocationServer, LogRemove)
|
||||||
{
|
{
|
||||||
|
EventTracer tracer;
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Filling the log fully
|
* Filling the log fully
|
||||||
@@ -359,12 +371,13 @@ TEST(DynamicNodeIDAllocationServer, LogRemove)
|
|||||||
|
|
||||||
TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
||||||
{
|
{
|
||||||
|
EventTracer tracer;
|
||||||
/*
|
/*
|
||||||
* First initialization
|
* First initialization
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage, tracer);
|
||||||
|
|
||||||
ASSERT_EQ(0, storage.getNumKeys());
|
ASSERT_EQ(0, storage.getNumKeys());
|
||||||
ASSERT_LE(0, pers.init());
|
ASSERT_LE(0, pers.init());
|
||||||
@@ -382,12 +395,12 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
|||||||
|
|
||||||
{
|
{
|
||||||
// This log is used to initialize the storage
|
// This log is used to initialize the storage
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
ASSERT_LE(0, log.init());
|
ASSERT_LE(0, log.init());
|
||||||
}
|
}
|
||||||
ASSERT_LE(1, storage.getNumKeys());
|
ASSERT_LE(1, storage.getNumKeys());
|
||||||
|
|
||||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage, tracer);
|
||||||
|
|
||||||
ASSERT_LE(0, pers.init());
|
ASSERT_LE(0, pers.init());
|
||||||
|
|
||||||
@@ -404,14 +417,14 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
|||||||
|
|
||||||
{
|
{
|
||||||
// This log is used to initialize the storage
|
// This log is used to initialize the storage
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
ASSERT_LE(0, log.init());
|
ASSERT_LE(0, log.init());
|
||||||
}
|
}
|
||||||
ASSERT_LE(1, storage.getNumKeys());
|
ASSERT_LE(1, storage.getNumKeys());
|
||||||
|
|
||||||
storage.set("current_term", "1");
|
storage.set("current_term", "1");
|
||||||
|
|
||||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage, tracer);
|
||||||
|
|
||||||
ASSERT_GT(0, pers.init()); // Fails because current term is not zero
|
ASSERT_GT(0, pers.init()); // Fails because current term is not zero
|
||||||
|
|
||||||
@@ -432,7 +445,7 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
|||||||
|
|
||||||
{
|
{
|
||||||
// This log is used to initialize the storage
|
// This log is used to initialize the storage
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
ASSERT_LE(0, log.init());
|
ASSERT_LE(0, log.init());
|
||||||
|
|
||||||
uavcan::protocol::dynamic_node_id::server::Entry entry;
|
uavcan::protocol::dynamic_node_id::server::Entry entry;
|
||||||
@@ -443,7 +456,7 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
|||||||
}
|
}
|
||||||
ASSERT_LE(4, storage.getNumKeys());
|
ASSERT_LE(4, storage.getNumKeys());
|
||||||
|
|
||||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage, tracer);
|
||||||
|
|
||||||
ASSERT_GT(0, pers.init()); // Fails because log is not empty
|
ASSERT_GT(0, pers.init()); // Fails because log is not empty
|
||||||
|
|
||||||
@@ -468,8 +481,9 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
|||||||
|
|
||||||
TEST(DynamicNodeIDAllocationServer, PersistentStorage)
|
TEST(DynamicNodeIDAllocationServer, PersistentStorage)
|
||||||
{
|
{
|
||||||
|
EventTracer tracer;
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage, tracer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initializing
|
* Initializing
|
||||||
@@ -549,15 +563,17 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerInitialization)
|
|||||||
uavcan::GlobalDataTypeRegistry::instance().reset();
|
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||||
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
|
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
|
||||||
|
|
||||||
|
EventTracer tracer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple initialization
|
* Simple initialization
|
||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
InterlinkedTestNodesWithSysClock nodes;
|
InterlinkedTestNodesWithSysClock nodes;
|
||||||
|
|
||||||
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log);
|
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log, tracer);
|
||||||
|
|
||||||
// Too big
|
// Too big
|
||||||
ASSERT_GT(0, mgr.init(MaxClusterSize + 1));
|
ASSERT_GT(0, mgr.init(MaxClusterSize + 1));
|
||||||
@@ -579,10 +595,10 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerInitialization)
|
|||||||
*/
|
*/
|
||||||
{
|
{
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
InterlinkedTestNodesWithSysClock nodes;
|
InterlinkedTestNodesWithSysClock nodes;
|
||||||
|
|
||||||
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log);
|
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log, tracer);
|
||||||
|
|
||||||
// Not configured
|
// Not configured
|
||||||
ASSERT_GT(0, mgr.init());
|
ASSERT_GT(0, mgr.init());
|
||||||
@@ -601,11 +617,12 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerOneServer)
|
|||||||
uavcan::GlobalDataTypeRegistry::instance().reset();
|
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||||
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
|
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
|
||||||
|
|
||||||
|
EventTracer tracer;
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
InterlinkedTestNodesWithSysClock nodes;
|
InterlinkedTestNodesWithSysClock nodes;
|
||||||
|
|
||||||
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log);
|
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log, tracer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pub and sub
|
* Pub and sub
|
||||||
@@ -675,11 +692,12 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerThreeServers)
|
|||||||
uavcan::GlobalDataTypeRegistry::instance().reset();
|
uavcan::GlobalDataTypeRegistry::instance().reset();
|
||||||
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
|
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
|
||||||
|
|
||||||
|
EventTracer tracer;
|
||||||
StorageBackend storage;
|
StorageBackend storage;
|
||||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
|
||||||
InterlinkedTestNodesWithSysClock nodes;
|
InterlinkedTestNodesWithSysClock nodes;
|
||||||
|
|
||||||
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log);
|
uavcan::dynamic_node_id_server_impl::ClusterManager mgr(nodes.a, storage, log, tracer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pub and sub
|
* Pub and sub
|
||||||
|
|||||||
Reference in New Issue
Block a user