Raft event tracing interface, more states of RaftCore, basic functions of RaftCore

This commit is contained in:
Pavel Kirienko
2015-05-07 13:58:24 +03:00
parent 6ae48c25cb
commit 5e56c8a612
3 changed files with 346 additions and 43 deletions
@@ -70,6 +70,25 @@ public:
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.
*/
@@ -83,6 +102,37 @@ using namespace protocol::dynamic_node_id::server;
*/
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.
*/
@@ -137,6 +187,7 @@ public:
private:
IDynamicNodeIDStorageBackend& storage_;
IDynamicNodeIDAllocationServerEventTracer& tracer_;
Entry entries_[Capacity];
Index last_index_; // Index zero always contains an empty entry
@@ -149,8 +200,9 @@ private:
int initEmptyLogStorage();
public:
Log(IDynamicNodeIDStorageBackend& storage)
Log(IDynamicNodeIDStorageBackend& storage, IDynamicNodeIDAllocationServerEventTracer& tracer)
: storage_(storage)
, tracer_(tracer)
, last_index_(0)
{ }
@@ -195,6 +247,7 @@ public:
class PersistentState
{
IDynamicNodeIDStorageBackend& storage_;
IDynamicNodeIDAllocationServerEventTracer& tracer_;
Term current_term_;
NodeID voted_for_;
@@ -204,10 +257,11 @@ class PersistentState
static IDynamicNodeIDStorageBackend::String getVotedForKey() { return "voted_for"; }
public:
PersistentState(IDynamicNodeIDStorageBackend& storage)
PersistentState(IDynamicNodeIDStorageBackend& storage, IDynamicNodeIDAllocationServerEventTracer& tracer)
: storage_(storage)
, tracer_(tracer)
, current_term_(0)
, log_(storage)
, log_(storage, tracer)
{ }
int init();
@@ -257,6 +311,7 @@ class ClusterManager : private TimerBase
enum { MaxServers = Discovery::FieldTypes::known_nodes::MaxSize };
IDynamicNodeIDStorageBackend& storage_;
IDynamicNodeIDAllocationServerEventTracer& tracer_;
const Log& log_;
Subscriber<Discovery, DiscoveryCallback> discovery_sub_;
@@ -293,9 +348,11 @@ public:
* @param storage Needed to read the cluster size parameter from the storage
* @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)
, storage_(storage)
, tracer_(tracer)
, log_(log)
, discovery_sub_(node)
, discovery_pub_(node)
@@ -348,7 +405,15 @@ public:
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_; }
/**
* Cluster size and quorum size are constant.
*/
uint8_t getClusterSize() const { return cluster_size_; }
uint8_t getQuorumSize() const { return static_cast<uint8_t>(cluster_size_ / 2U + 1U); }
@@ -366,13 +431,13 @@ class RaftCore : private TimerBase
ServiceResponseDataStructure<AppendEntries::Response>&)>
AppendEntriesCallback;
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ServiceCallResult<AppendEntries>&)>
AppendEntriesResponseCallback;
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ReceivedDataStructure<RequestVote::Request>&,
ServiceResponseDataStructure<RequestVote::Response>&)>
RequestVoteCallback;
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ServiceCallResult<AppendEntries>&)>
AppendEntriesResponseCallback;
typedef MethodBinder<RaftCore*, void (RaftCore::*)(const ServiceCallResult<RequestVote>&)>
RequestVoteResponseCallback;
@@ -383,48 +448,95 @@ class RaftCore : private TimerBase
ServerStateLeader
};
IDynamicNodeIDAllocationServerEventTracer& tracer_;
/*
* States
*/
PersistentState persistent_state_;
Log::Index commit_index_;
ClusterManager cluster_;
Log::Index commit_index_;
MonotonicTime last_activity_timestamp_;
bool active_mode_;
ServerState server_state_;
ServiceServer<AppendEntries, AppendEntriesCallback> append_entries_srv_;
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
uint8_t next_server_index_; ///< Next server to query for AE or RV RPC
uint8_t num_votes_received_in_this_campaign_;
/*
* Transport
*/
ServiceServer<AppendEntries, AppendEntriesCallback> append_entries_srv_;
ServiceClient<AppendEntries, AppendEntriesResponseCallback> append_entries_client_;
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
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:
RaftCore(INode& node, IDynamicNodeIDStorageBackend& storage)
RaftCore(INode& node, IDynamicNodeIDStorageBackend& storage, IDynamicNodeIDAllocationServerEventTracer& tracer)
: TimerBase(node)
, persistent_state_(storage)
, commit_index_(0) // Per Raft paper, commitIndex must be initialized to zero
, cluster_(node, storage, persistent_state_.getLog())
, tracer_(tracer)
, 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)
, server_state_(ServerStateFollower)
, next_server_index_(0)
, num_votes_received_in_this_campaign_(0)
, append_entries_srv_(node)
, request_vote_srv_(node)
, append_entries_client_(node)
, request_vote_srv_(node)
, request_vote_client_(node)
{ }
/**
* 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.
* Failures are tolerble because all operations are idempotent.
* Only the leader can call @ref appendLog().
*/
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.
@@ -286,6 +286,8 @@ int Log::init()
last_index_ = Index(value);
}
tracer_.onEvent(TraceLogLastIndexRestored, last_index_);
// Restoring log entries - note that index 0 always exists
for (Index index = 0; index <= last_index_; index++)
{
@@ -309,6 +311,8 @@ int Log::append(const Entry& entry)
return -ErrLogic;
}
tracer_.onEvent(TraceLogAppend, last_index_ + 1U);
// If next operations fail, we'll get a dangling entry, but it's absolutely OK.
int res = writeEntryToStorage(Index(last_index_ + 1), entry);
if (res < 0)
@@ -345,6 +349,8 @@ int Log::removeEntriesWhereIndexGreaterOrEqual(Index index)
return -ErrLogic;
}
tracer_.onEvent(TraceLogRemove, index - 1U);
MarshallingStorageDecorator io(storage_);
uint32_t new_last_index = index - 1U;
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)
{
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState",
@@ -481,6 +489,8 @@ int PersistentState::init()
voted_for_ = NodeID(uint8_t(stored_voted_for));
}
tracer_.onEvent(TraceVotedForRestored, voted_for_.get());
return 0;
}
@@ -492,6 +502,8 @@ int PersistentState::setCurrentTerm(const Term term)
return -ErrInvalidParam;
}
tracer_.onEvent(TraceCurrentTermUpdate, term);
MarshallingStorageDecorator io(storage_);
Term tmp = term;
@@ -518,6 +530,8 @@ int PersistentState::setVotedFor(const NodeID node_id)
return -ErrInvalidParam;
}
tracer_.onEvent(TraceVotedForUpdate, node_id.get());
MarshallingStorageDecorator io(storage_);
uint32_t tmp = node_id.get();
@@ -586,6 +600,7 @@ void ClusterManager::addServer(NodeID node_id)
UAVCAN_ASSERT((num_known_servers_ + 1) < (MaxServers - 2));
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_].resetIndices(log_);
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_);
tracer_.onEvent(TraceDiscoveryBroadcast, num_known_servers_);
/*
* Filling the message
*/
@@ -641,6 +658,8 @@ void ClusterManager::handleTimerEvent(const TimerEvent&)
void ClusterManager::handleDiscovery(const ReceivedDataStructure<Discovery>& msg)
{
tracer_.onEvent(TraceDiscoveryReceived, msg.getSrcNodeID().get());
/*
* Validating cluster configuration
* 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_ <= 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
}
@@ -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
@@ -140,10 +149,11 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator)
TEST(DynamicNodeIDAllocationServer, LogInitialization)
{
EventTracer tracer;
// No log data in the storage - initializing empty log
{
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_LE(0, log.init());
@@ -157,7 +167,7 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
// Nonempty storage, one item
{
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");
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Expected one entry, none found
@@ -174,7 +184,7 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
// Nonempty storage, broken data
{
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");
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Bad value
@@ -197,7 +207,7 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
// Nonempty storage, many items
{
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("log0_term", "0");
@@ -235,8 +245,9 @@ TEST(DynamicNodeIDAllocationServer, LogInitialization)
TEST(DynamicNodeIDAllocationServer, LogAppend)
{
EventTracer tracer;
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_LE(0, log.init());
@@ -307,8 +318,9 @@ TEST(DynamicNodeIDAllocationServer, LogAppend)
TEST(DynamicNodeIDAllocationServer, LogRemove)
{
EventTracer tracer;
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
@@ -359,12 +371,13 @@ TEST(DynamicNodeIDAllocationServer, LogRemove)
TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
{
EventTracer tracer;
/*
* First initialization
*/
{
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_LE(0, pers.init());
@@ -382,12 +395,12 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
{
// 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(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());
@@ -404,14 +417,14 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
{
// 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(1, storage.getNumKeys());
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
@@ -432,7 +445,7 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
{
// 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());
uavcan::protocol::dynamic_node_id::server::Entry entry;
@@ -443,7 +456,7 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
}
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
@@ -468,8 +481,9 @@ TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
TEST(DynamicNodeIDAllocationServer, PersistentStorage)
{
EventTracer tracer;
StorageBackend storage;
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage, tracer);
/*
* Initializing
@@ -549,15 +563,17 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerInitialization)
uavcan::GlobalDataTypeRegistry::instance().reset();
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
EventTracer tracer;
/*
* Simple initialization
*/
{
StorageBackend storage;
uavcan::dynamic_node_id_server_impl::Log log(storage);
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
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
ASSERT_GT(0, mgr.init(MaxClusterSize + 1));
@@ -579,10 +595,10 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerInitialization)
*/
{
StorageBackend storage;
uavcan::dynamic_node_id_server_impl::Log log(storage);
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
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
ASSERT_GT(0, mgr.init());
@@ -601,11 +617,12 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerOneServer)
uavcan::GlobalDataTypeRegistry::instance().reset();
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
EventTracer tracer;
StorageBackend storage;
uavcan::dynamic_node_id_server_impl::Log log(storage);
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
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
@@ -675,11 +692,12 @@ TEST(DynamicNodeIDAllocationServer, ClusterManagerThreeServers)
uavcan::GlobalDataTypeRegistry::instance().reset();
uavcan::DefaultDataTypeRegistrator<uavcan::protocol::dynamic_node_id::server::Discovery> _reg1;
EventTracer tracer;
StorageBackend storage;
uavcan::dynamic_node_id_server_impl::Log log(storage);
uavcan::dynamic_node_id_server_impl::Log log(storage, tracer);
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