mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-13 22:33:33 +08:00
Raft active state extension removed
This commit is contained in:
@@ -3,16 +3,25 @@
|
||||
# Please refer to the specification for details.
|
||||
#
|
||||
|
||||
uint16 DEFAULT_REQUEST_TIMEOUT_MS = 200
|
||||
|
||||
uint16 DEFAULT_BASE_ELECTION_TIMEOUT_MS = 1600 # request timeout * (max cluster size - 1) * 2 requests
|
||||
#
|
||||
# Given min election timeout and cluster size, the maximum request interval can be derived as follows:
|
||||
# max request interval = (min election timeout) / 2 requests / (cluster size - 1)
|
||||
# Obviously, request interval can be lower than that if needed, but not higher.
|
||||
#
|
||||
# Real timeout is randomized in the range (MIN, MAX].
|
||||
#
|
||||
uint16 MIN_ELECTION_TIMEOUT_MS = 4000
|
||||
uint16 MAX_ELECTION_TIMEOUT_MS = 6000
|
||||
|
||||
uint32 term
|
||||
uint32 prev_log_term
|
||||
uint8 prev_log_index
|
||||
uint8 leader_commit
|
||||
|
||||
# Full replication: 127 requests * 0.2 sec interval * 4 followers * 2 trips of next_index value ~ 3.5 min
|
||||
#
|
||||
# Worst-case replication time can be computed as:
|
||||
# worst replication time = (127 log entries) * (2 trips of next_index) * (cluster size - 1) * (request interval)
|
||||
#
|
||||
Entry[<=1] entries
|
||||
|
||||
---
|
||||
|
||||
-18
@@ -68,8 +68,6 @@ private:
|
||||
uint8_t cluster_size_;
|
||||
uint8_t num_known_servers_;
|
||||
|
||||
bool had_discovery_activity_;
|
||||
|
||||
static IStorageBackend::String getStorageKeyForClusterSize() { return "cluster_size"; }
|
||||
|
||||
INode& getNode() { return discovery_sub_.getNode(); }
|
||||
@@ -151,8 +149,6 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
had_discovery_activity_ = true;
|
||||
|
||||
/*
|
||||
* Updating the set of known servers
|
||||
*/
|
||||
@@ -204,7 +200,6 @@ public:
|
||||
, discovery_pub_(node)
|
||||
, cluster_size_(0)
|
||||
, num_known_servers_(0)
|
||||
, had_discovery_activity_(false)
|
||||
{ }
|
||||
|
||||
/**
|
||||
@@ -417,19 +412,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns true if there was at least one Discovery message received since last call.
|
||||
*/
|
||||
bool hadDiscoveryActivity()
|
||||
{
|
||||
if (had_discovery_activity_)
|
||||
{
|
||||
had_discovery_activity_ = false;
|
||||
return true;
|
||||
}
|
||||
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.
|
||||
|
||||
+55
-145
@@ -5,6 +5,7 @@
|
||||
#ifndef UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_DISTRIBUTED_RAFT_CORE_HPP_INCLUDED
|
||||
#define UAVCAN_PROTOCOL_DYNAMIC_NODE_ID_SERVER_DISTRIBUTED_RAFT_CORE_HPP_INCLUDED
|
||||
|
||||
#include <cstdlib>
|
||||
#include <uavcan/build_config.hpp>
|
||||
#include <uavcan/debug.hpp>
|
||||
#include <uavcan/util/method_binder.hpp>
|
||||
@@ -51,26 +52,14 @@ public:
|
||||
* It does not implement client-server interaction at all; instead it just exposes a public method for adding
|
||||
* allocation entries.
|
||||
*
|
||||
* Note that this class uses std::rand(), so the RNG must be properly seeded by the application.
|
||||
*
|
||||
* Activity registration:
|
||||
* - persistent state update error
|
||||
* - switch to candidate (this defines timeout between reelections)
|
||||
* - newer term in response (also switch to follower)
|
||||
* - append entries request with term >= currentTerm
|
||||
* - vote granted
|
||||
*
|
||||
* Active state switch logic:
|
||||
* Activation (this is default state):
|
||||
* - vote request
|
||||
* - allocation request at any stage
|
||||
* - only if leader:
|
||||
* - discovery activity detected
|
||||
* - log is not fully replicated or there are uncommitted entries
|
||||
*
|
||||
* Deactivation:
|
||||
* - switch to follower state
|
||||
* - persistent state update error
|
||||
* - only if leader:
|
||||
* - all log entries are fully replicated and committed
|
||||
*/
|
||||
class RaftCore : private TimerBase
|
||||
{
|
||||
@@ -111,8 +100,7 @@ private:
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
const MonotonicDuration update_interval_; ///< AE requests will be issued at this rate
|
||||
const MonotonicDuration base_activity_timeout_;
|
||||
enum { MaxNumFollowers = ClusterManager::MaxClusterSize - 1 };
|
||||
|
||||
IEventTracer& tracer_;
|
||||
IRaftLeaderMonitor& leader_monitor_;
|
||||
@@ -125,7 +113,7 @@ private:
|
||||
Log::Index commit_index_;
|
||||
|
||||
MonotonicTime last_activity_timestamp_;
|
||||
bool active_mode_;
|
||||
MonotonicDuration randomized_activity_timeout_;
|
||||
ServerState server_state_;
|
||||
|
||||
uint8_t next_server_index_; ///< Next server to query AE from
|
||||
@@ -139,9 +127,7 @@ private:
|
||||
ServiceServer<AppendEntries, AppendEntriesCallback> append_entries_srv_;
|
||||
ServiceClient<AppendEntries, AppendEntriesResponseCallback> append_entries_client_;
|
||||
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
|
||||
|
||||
enum { NumRequestVoteCalls = ClusterManager::MaxClusterSize - 1 };
|
||||
ServiceClient<RequestVote, RequestVoteResponseCallback, NumRequestVoteCalls> request_vote_client_;
|
||||
ServiceClient<RequestVote, RequestVoteResponseCallback, MaxNumFollowers> request_vote_client_;
|
||||
|
||||
/*
|
||||
* Methods
|
||||
@@ -167,19 +153,33 @@ private:
|
||||
UAVCAN_ASSERT(num_votes_received_in_this_campaign_ <= cluster_.getClusterSize());
|
||||
|
||||
// Transport
|
||||
UAVCAN_ASSERT(append_entries_client_.getNumPendingCalls() <= 1);
|
||||
UAVCAN_ASSERT(request_vote_client_.getNumPendingCalls() <= cluster_.getNumKnownServers());
|
||||
UAVCAN_ASSERT(server_state_ != ServerStateCandidate || !append_entries_client_.hasPendingCalls());
|
||||
UAVCAN_ASSERT(server_state_ != ServerStateLeader || !request_vote_client_.hasPendingCalls());
|
||||
UAVCAN_ASSERT(server_state_ != ServerStateFollower ||
|
||||
(!append_entries_client_.hasPendingCalls() && !request_vote_client_.hasPendingCalls()));
|
||||
}
|
||||
|
||||
void registerActivity() { last_activity_timestamp_ = getNode().getMonotonicTime(); }
|
||||
void registerActivity()
|
||||
{
|
||||
last_activity_timestamp_ = getNode().getMonotonicTime();
|
||||
|
||||
static const int32_t randomization_range_msec =
|
||||
AppendEntries::Request::MAX_ELECTION_TIMEOUT_MS - AppendEntries::Request::MIN_ELECTION_TIMEOUT_MS;
|
||||
|
||||
const int32_t random_msec = (std::rand() % randomization_range_msec) + 1;
|
||||
|
||||
randomized_activity_timeout_ =
|
||||
MonotonicDuration::fromMSec(AppendEntries::Request::MIN_ELECTION_TIMEOUT_MS + random_msec);
|
||||
|
||||
UAVCAN_ASSERT(randomized_activity_timeout_.toMSec() > AppendEntries::Request::MIN_ELECTION_TIMEOUT_MS);
|
||||
UAVCAN_ASSERT(randomized_activity_timeout_.toMSec() <= AppendEntries::Request::MAX_ELECTION_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
bool isActivityTimedOut() const
|
||||
{
|
||||
const int multiplier = static_cast<int>(getNode().getNodeID().get()) - 1;
|
||||
const MonotonicDuration activity_timeout = base_activity_timeout_ + update_interval_ * multiplier;
|
||||
return getNode().getMonotonicTime() > (last_activity_timestamp_ + activity_timeout);
|
||||
return getNode().getMonotonicTime() > (last_activity_timestamp_ + randomized_activity_timeout_);
|
||||
}
|
||||
|
||||
void handlePersistentStateUpdateError(int error)
|
||||
@@ -187,13 +187,12 @@ private:
|
||||
UAVCAN_ASSERT(error < 0);
|
||||
trace(TraceRaftPersistStateUpdateError, error);
|
||||
switchState(ServerStateFollower);
|
||||
setActiveMode(false); // Goodnight sweet prince
|
||||
registerActivity(); // Deferring reelections
|
||||
}
|
||||
|
||||
void updateFollower()
|
||||
{
|
||||
if (active_mode_ && isActivityTimedOut())
|
||||
if (isActivityTimedOut())
|
||||
{
|
||||
switchState(ServerStateCandidate);
|
||||
registerActivity();
|
||||
@@ -202,8 +201,6 @@ private:
|
||||
|
||||
void updateCandidate()
|
||||
{
|
||||
UAVCAN_ASSERT(active_mode_);
|
||||
|
||||
if (num_votes_received_in_this_campaign_ > 0)
|
||||
{
|
||||
trace(TraceRaftElectionComplete, num_votes_received_in_this_campaign_);
|
||||
@@ -238,7 +235,7 @@ private:
|
||||
req.last_log_term = persistent_state_.getLog().getEntryAtIndex(req.last_log_index)->term;
|
||||
req.term = persistent_state_.getCurrentTerm();
|
||||
|
||||
for (uint8_t i = 0; i < NumRequestVoteCalls; i++)
|
||||
for (uint8_t i = 0; i < MaxNumFollowers; i++)
|
||||
{
|
||||
const NodeID node_id = cluster_.getRemoteServerNodeIDAtIndex(i);
|
||||
if (!node_id.isUnicast())
|
||||
@@ -261,17 +258,12 @@ private:
|
||||
|
||||
void updateLeader()
|
||||
{
|
||||
if (cluster_.getClusterSize() == 1)
|
||||
{
|
||||
setActiveMode(false); // Haha
|
||||
}
|
||||
|
||||
if (append_entries_client_.hasPendingCalls())
|
||||
{
|
||||
append_entries_client_.cancelAllCalls(); // Refer to the response callback to learn why
|
||||
}
|
||||
|
||||
if (active_mode_ || (next_server_index_ > 0))
|
||||
if (cluster_.getClusterSize() > 1)
|
||||
{
|
||||
const NodeID node_id = cluster_.getRemoteServerNodeIDAtIndex(next_server_index_);
|
||||
UAVCAN_ASSERT(node_id.isUnicast());
|
||||
@@ -364,18 +356,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void setActiveMode(bool new_active)
|
||||
{
|
||||
if (active_mode_ != new_active)
|
||||
{
|
||||
UAVCAN_TRACE("dynamic_node_id_server::distributed::RaftCore", "Active switch: %d --> %d",
|
||||
int(active_mode_), int(new_active));
|
||||
trace(TraceRaftActiveSwitch, new_active);
|
||||
|
||||
active_mode_ = new_active;
|
||||
}
|
||||
}
|
||||
|
||||
void tryIncrementCurrentTermFromResponse(Term new_term)
|
||||
{
|
||||
trace(TraceRaftNewerTermInResponse, new_term);
|
||||
@@ -386,7 +366,6 @@ private:
|
||||
}
|
||||
registerActivity(); // Deferring future elections
|
||||
switchState(ServerStateFollower);
|
||||
setActiveMode(false);
|
||||
}
|
||||
|
||||
void propagateCommitIndex()
|
||||
@@ -395,63 +374,12 @@ private:
|
||||
UAVCAN_ASSERT(server_state_ == ServerStateLeader);
|
||||
UAVCAN_ASSERT(commit_index_ <= persistent_state_.getLog().getLastIndex());
|
||||
|
||||
if (commit_index_ == persistent_state_.getLog().getLastIndex())
|
||||
{
|
||||
/*
|
||||
* All local entries are committed.
|
||||
* Deciding if it is safe to go into passive mode now.
|
||||
*
|
||||
* We can go into passive mode if the log is known to be fully replicated and all entries are committed.
|
||||
* The high-level conditions above are guaranteed to be met if all of the following lower-level conditions
|
||||
* are met:
|
||||
* - All local entries are committed (already checked here).
|
||||
* - Match index on all nodes equals local commit index.
|
||||
* - Next index on all nodes is strictly greater than the local commit index.
|
||||
*
|
||||
* The following code checks if the last two conditions are met.
|
||||
*/
|
||||
bool match_index_equals_commit_index = true;
|
||||
bool next_index_greater_than_commit_index = true;
|
||||
|
||||
for (uint8_t i = 0; i < cluster_.getNumKnownServers(); i++)
|
||||
{
|
||||
const NodeID server_node_id = cluster_.getRemoteServerNodeIDAtIndex(i);
|
||||
|
||||
const Log::Index match_index = cluster_.getServerMatchIndex(server_node_id);
|
||||
if (match_index != commit_index_)
|
||||
{
|
||||
match_index_equals_commit_index = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const Log::Index next_index = cluster_.getServerNextIndex(server_node_id);
|
||||
if (next_index <= commit_index_)
|
||||
{
|
||||
next_index_greater_than_commit_index = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now we know whether the log is replicated and whether all entries are committed, so we can make
|
||||
* the decision. Remember that since we ended up in this branch, it is already known that all local
|
||||
* log entries are committed.
|
||||
*/
|
||||
const bool all_done =
|
||||
match_index_equals_commit_index &&
|
||||
next_index_greater_than_commit_index &&
|
||||
cluster_.isClusterDiscovered();
|
||||
|
||||
setActiveMode(!all_done);
|
||||
}
|
||||
else
|
||||
if (commit_index_ < persistent_state_.getLog().getLastIndex())
|
||||
{
|
||||
/*
|
||||
* Not all local entries are committed.
|
||||
* Deciding if it is safe to increment commit index.
|
||||
*/
|
||||
setActiveMode(true);
|
||||
|
||||
uint8_t num_nodes_with_next_log_entry_available = 1; // Local node
|
||||
for (uint8_t i = 0; i < cluster_.getNumKnownServers(); i++)
|
||||
{
|
||||
@@ -536,7 +464,6 @@ private:
|
||||
|
||||
registerActivity();
|
||||
switchState(ServerStateFollower);
|
||||
setActiveMode(false);
|
||||
|
||||
/*
|
||||
* Step 2
|
||||
@@ -613,14 +540,6 @@ private:
|
||||
|
||||
if (!result.isSuccessful())
|
||||
{
|
||||
/*
|
||||
* This callback WILL NEVER be invoked by timeout, because:
|
||||
* - Any pending request will be cancelled on the next update of the Leader.
|
||||
* - When the state switches (i.e. the node is not Leader anymore), all pending calls will be cancelled.
|
||||
* Also note that 'pending_append_entries_fields_' invalidates after every update of the Leader, so
|
||||
* if there were timeout callbacks, they would be using outdated state.
|
||||
*/
|
||||
UAVCAN_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -664,8 +583,6 @@ private:
|
||||
|
||||
UAVCAN_ASSERT(response.isResponseEnabled()); // This is default
|
||||
|
||||
setActiveMode(true);
|
||||
|
||||
/*
|
||||
* Checking if our current state is up to date.
|
||||
* The request will be ignored if persistent state cannot be updated.
|
||||
@@ -732,12 +649,6 @@ private:
|
||||
|
||||
if (!result.isSuccessful())
|
||||
{
|
||||
/*
|
||||
* This callback WILL NEVER be invoked by timeout, because all pending calls will be cancelled on
|
||||
* state switch, which ALWAYS happens 100ms after elections (either to Follower or to Leader, depending
|
||||
* on the number of votes collected).
|
||||
*/
|
||||
UAVCAN_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -762,11 +673,6 @@ private:
|
||||
{
|
||||
checkInvariants();
|
||||
|
||||
if (cluster_.hadDiscoveryActivity() && isLeader())
|
||||
{
|
||||
setActiveMode(true);
|
||||
}
|
||||
|
||||
switch (server_state_)
|
||||
{
|
||||
case ServerStateFollower:
|
||||
@@ -796,21 +702,15 @@ public:
|
||||
RaftCore(INode& node,
|
||||
IStorageBackend& storage,
|
||||
IEventTracer& tracer,
|
||||
IRaftLeaderMonitor& leader_monitor,
|
||||
MonotonicDuration update_interval =
|
||||
MonotonicDuration::fromMSec(AppendEntries::Request::DEFAULT_REQUEST_TIMEOUT_MS),
|
||||
MonotonicDuration base_activity_timeout =
|
||||
MonotonicDuration::fromMSec(AppendEntries::Request::DEFAULT_BASE_ELECTION_TIMEOUT_MS))
|
||||
IRaftLeaderMonitor& leader_monitor)
|
||||
: TimerBase(node)
|
||||
, update_interval_(update_interval)
|
||||
, base_activity_timeout_(base_activity_timeout)
|
||||
, tracer_(tracer)
|
||||
, leader_monitor_(leader_monitor)
|
||||
, persistent_state_(storage, tracer)
|
||||
, cluster_(node, storage, persistent_state_.getLog(), tracer)
|
||||
, commit_index_(0) // Per Raft paper, commitIndex must be initialized to zero
|
||||
, last_activity_timestamp_(node.getMonotonicTime())
|
||||
, active_mode_(true)
|
||||
, randomized_activity_timeout_(MonotonicDuration::fromMSec(AppendEntries::Request::MAX_ELECTION_TIMEOUT_MS))
|
||||
, server_state_(ServerStateFollower)
|
||||
, next_server_index_(0)
|
||||
, num_votes_received_in_this_campaign_(0)
|
||||
@@ -831,13 +731,13 @@ public:
|
||||
/*
|
||||
* Initializing state variables
|
||||
*/
|
||||
last_activity_timestamp_ = getNode().getMonotonicTime() + update_interval_;
|
||||
active_mode_ = true;
|
||||
server_state_ = ServerStateFollower;
|
||||
next_server_index_ = 0;
|
||||
num_votes_received_in_this_campaign_ = 0;
|
||||
commit_index_ = 0;
|
||||
|
||||
registerActivity();
|
||||
|
||||
/*
|
||||
* Initializing internals
|
||||
*/
|
||||
@@ -872,7 +772,6 @@ public:
|
||||
}
|
||||
append_entries_client_.setCallback(AppendEntriesResponseCallback(this,
|
||||
&RaftCore::handleAppendEntriesResponse));
|
||||
append_entries_client_.setRequestTimeout(update_interval_);
|
||||
|
||||
res = request_vote_client_.init();
|
||||
if (res < 0)
|
||||
@@ -880,24 +779,34 @@ public:
|
||||
return res;
|
||||
}
|
||||
request_vote_client_.setCallback(RequestVoteResponseCallback(this, &RaftCore::handleRequestVoteResponse));
|
||||
request_vote_client_.setRequestTimeout(update_interval_);
|
||||
|
||||
startPeriodic(update_interval_);
|
||||
/*
|
||||
* Initializing timing constants
|
||||
* Refer to the specification for the formula
|
||||
*/
|
||||
const uint8_t num_followers = static_cast<uint8_t>(cluster_.getClusterSize() - 1);
|
||||
|
||||
trace(TraceRaftCoreInited, update_interval_.toUSec());
|
||||
const MonotonicDuration update_interval =
|
||||
MonotonicDuration::fromMSec(AppendEntries::Request::MIN_ELECTION_TIMEOUT_MS /
|
||||
2 / max(static_cast<uint8_t>(3), num_followers));
|
||||
|
||||
UAVCAN_TRACE("dynamic_node_id_server::distributed::RaftCore",
|
||||
"Update interval: %ld msec", static_cast<long>(update_interval.toMSec()));
|
||||
|
||||
append_entries_client_.setRequestTimeout(min(append_entries_client_.getDefaultRequestTimeout(),
|
||||
update_interval));
|
||||
|
||||
request_vote_client_.setRequestTimeout(min(request_vote_client_.getDefaultRequestTimeout(),
|
||||
update_interval));
|
||||
|
||||
startPeriodic(update_interval);
|
||||
|
||||
trace(TraceRaftCoreInited, update_interval.toUSec());
|
||||
|
||||
UAVCAN_ASSERT(res >= 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normally should be called when there's allocation activity on the bus.
|
||||
*/
|
||||
void forceActiveMode()
|
||||
{
|
||||
setActiveMode(true); // If the current state was Follower, active mode may be toggling quickly for some time
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is mostly needed for testing.
|
||||
*/
|
||||
@@ -994,8 +903,9 @@ public:
|
||||
const PersistentState& getPersistentState() const { return persistent_state_; }
|
||||
const ClusterManager& getClusterManager() const { return cluster_; }
|
||||
MonotonicTime getLastActivityTimestamp() const { return last_activity_timestamp_; }
|
||||
bool isInActiveMode() const { return active_mode_; }
|
||||
ServerState getServerState() const { return server_state_; }
|
||||
MonotonicDuration getUpdateInterval() const { return getPeriod(); }
|
||||
MonotonicDuration getRandomizedTimeout() const { return randomized_activity_timeout_; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -135,12 +135,9 @@ class UAVCAN_EXPORT Server : IAllocationRequestHandler
|
||||
}
|
||||
}
|
||||
|
||||
virtual void handleAllocationActivityDetection(const ReceivedDataStructure<Allocation>& msg)
|
||||
virtual void handleAllocationActivityDetection(const ReceivedDataStructure<Allocation>&)
|
||||
{
|
||||
if (msg.isAnonymousTransfer())
|
||||
{
|
||||
raft_core_.forceActiveMode();
|
||||
}
|
||||
// TODO: remove this method
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -315,7 +312,6 @@ struct StateReport
|
||||
uint8_t cluster_size;
|
||||
|
||||
RaftCore::ServerState state;
|
||||
bool is_active;
|
||||
|
||||
Log::Index last_log_index;
|
||||
Log::Index commit_index;
|
||||
@@ -326,6 +322,7 @@ struct StateReport
|
||||
NodeID voted_for;
|
||||
|
||||
MonotonicTime last_activity_timestamp;
|
||||
MonotonicDuration randomized_timeout;
|
||||
|
||||
uint8_t num_unknown_nodes;
|
||||
|
||||
@@ -344,13 +341,13 @@ struct StateReport
|
||||
StateReport(const Server& s)
|
||||
: cluster_size (s.getRaftCore().getClusterManager().getClusterSize())
|
||||
, state (s.getRaftCore().getServerState())
|
||||
, is_active (s.getRaftCore().isInActiveMode())
|
||||
, last_log_index (s.getRaftCore().getPersistentState().getLog().getLastIndex())
|
||||
, commit_index (s.getRaftCore().getCommitIndex())
|
||||
, last_log_term (0) // See below
|
||||
, current_term (s.getRaftCore().getPersistentState().getCurrentTerm())
|
||||
, voted_for (s.getRaftCore().getPersistentState().getVotedFor())
|
||||
, last_activity_timestamp(s.getRaftCore().getLastActivityTimestamp())
|
||||
, randomized_timeout (s.getRaftCore().getRandomizedTimeout())
|
||||
, num_unknown_nodes (s.getNodeDiscoverer().getNumUnknownNodes())
|
||||
{
|
||||
const Entry* const e = s.getRaftCore().getPersistentState().getLog().getEntryAtIndex(last_log_index);
|
||||
|
||||
@@ -68,11 +68,10 @@ TEST(dynamic_node_id_server_RaftCore, Basic)
|
||||
/*
|
||||
* Running and trying not to fall
|
||||
*/
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(5000));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(9000));
|
||||
|
||||
// The one with lower node ID must become a leader
|
||||
ASSERT_TRUE(raft_a->isLeader());
|
||||
ASSERT_FALSE(raft_b->isLeader());
|
||||
// Either must become a leader
|
||||
ASSERT_TRUE(raft_a->isLeader() || raft_b->isLeader());
|
||||
|
||||
ASSERT_EQ(0, raft_a->getCommitIndex());
|
||||
ASSERT_EQ(0, raft_b->getCommitIndex());
|
||||
@@ -83,19 +82,19 @@ TEST(dynamic_node_id_server_RaftCore, Basic)
|
||||
Entry::FieldTypes::unique_id unique_id;
|
||||
uavcan::fill_n(unique_id.begin(), 16, uint8_t(0xAA));
|
||||
|
||||
raft_a->appendLog(unique_id, uavcan::NodeID(1));
|
||||
(raft_a->isLeader() ? raft_a : raft_b)->appendLog(unique_id, uavcan::NodeID(1));
|
||||
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2000));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(6000));
|
||||
|
||||
ASSERT_EQ(1, raft_a->getCommitIndex());
|
||||
ASSERT_EQ(1, raft_b->getCommitIndex());
|
||||
|
||||
/*
|
||||
* Terminating the leader - the Follower will continue to sleep
|
||||
* Terminating the leader
|
||||
*/
|
||||
raft_a.reset();
|
||||
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(2000));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(6000));
|
||||
|
||||
/*
|
||||
* Reinitializing the leader - current Follower will become the new Leader
|
||||
@@ -106,7 +105,7 @@ TEST(dynamic_node_id_server_RaftCore, Basic)
|
||||
ASSERT_LE(0, raft_a->init(2));
|
||||
ASSERT_EQ(0, raft_a->getCommitIndex());
|
||||
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(5000));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(9000));
|
||||
|
||||
ASSERT_FALSE(raft_a->isLeader());
|
||||
ASSERT_TRUE(raft_b->isLeader());
|
||||
@@ -166,7 +165,7 @@ TEST(dynamic_node_id_server_Server, Basic)
|
||||
/*
|
||||
* Fire
|
||||
*/
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(6000));
|
||||
nodes.spinBoth(uavcan::MonotonicDuration::fromMSec(9000));
|
||||
|
||||
ASSERT_TRUE(client.isAllocationComplete());
|
||||
ASSERT_EQ(PreferredNodeID, client.getAllocatedNodeID());
|
||||
|
||||
@@ -360,10 +360,6 @@ void redraw(const uavcan_linux::NodePtr& node,
|
||||
(report.state == RaftCore::ServerStateLeader) ? CLIColor::Green :
|
||||
CLIColor::Default);
|
||||
|
||||
render_top_str("Mode",
|
||||
report.is_active ? "Active" : "Passive",
|
||||
colorize_if(report.is_active, CLIColor::Magenta));
|
||||
|
||||
render_top_int("Last log index",
|
||||
report.last_log_index,
|
||||
CLIColor::Default);
|
||||
@@ -388,6 +384,10 @@ void redraw(const uavcan_linux::NodePtr& node,
|
||||
duration_to_string(time_since_last_activity).c_str(),
|
||||
CLIColor::Default);
|
||||
|
||||
render_top_str("Random timeout",
|
||||
duration_to_string(report.randomized_timeout).c_str(),
|
||||
CLIColor::Default);
|
||||
|
||||
render_top_int("Unknown nodes",
|
||||
report.num_unknown_nodes,
|
||||
colorize_if(report.num_unknown_nodes != 0, CLIColor::Magenta));
|
||||
@@ -630,6 +630,8 @@ int main(int argc, const char** argv)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::srand(std::time(nullptr));
|
||||
|
||||
if (isatty(STDOUT_FILENO) != 1)
|
||||
{
|
||||
std::cerr << "This application cannot run if stdout is not associated with a terminal" << std::endl;
|
||||
|
||||
Reference in New Issue
Block a user