mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-30 05:53:35 +08:00
Raft log - basic tests of state recovery
This commit is contained in:
@@ -137,6 +137,7 @@ private:
|
|||||||
protocol::dynamic_node_id::server::Entry entries_[Capacity];
|
protocol::dynamic_node_id::server::Entry entries_[Capacity];
|
||||||
Index last_index_; // Index zero always contains an empty entry
|
Index last_index_; // Index zero always contains an empty entry
|
||||||
|
|
||||||
|
static IDynamicNodeIDStorageBackend::String getLastIndexKey() { return "log_last_index"; }
|
||||||
static IDynamicNodeIDStorageBackend::String makeEntryKey(Index index, const char* postfix);
|
static IDynamicNodeIDStorageBackend::String makeEntryKey(Index index, const char* postfix);
|
||||||
|
|
||||||
int readEntryFromStorage(Index index, protocol::dynamic_node_id::server::Entry& out_entry);
|
int readEntryFromStorage(Index index, protocol::dynamic_node_id::server::Entry& out_entry);
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ int MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String&
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Per MISRA C++ recommendations, checking the inputs instead of relying solely on errno.
|
|
||||||
* The value must contain only hexadecimal numbers.
|
* The value must contain only hexadecimal numbers.
|
||||||
*/
|
*/
|
||||||
val.convertToLowerCaseASCII();
|
val.convertToLowerCaseASCII();
|
||||||
@@ -157,10 +156,10 @@ int MarshallingStorageDecorator::get(const IDynamicNodeIDStorageBackend::String&
|
|||||||
IDynamicNodeIDStorageBackend::String Log::makeEntryKey(Index index, const char* postfix)
|
IDynamicNodeIDStorageBackend::String Log::makeEntryKey(Index index, const char* postfix)
|
||||||
{
|
{
|
||||||
IDynamicNodeIDStorageBackend::String str;
|
IDynamicNodeIDStorageBackend::String str;
|
||||||
// "log0.foobar"
|
// "log0_foobar"
|
||||||
str += "log";
|
str += "log";
|
||||||
str.appendFormatted("%d", int(index));
|
str.appendFormatted("%d", int(index));
|
||||||
str += ".";
|
str += "_";
|
||||||
str += postfix;
|
str += postfix;
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
@@ -187,6 +186,10 @@ int Log::readEntryFromStorage(Index index, protocol::dynamic_node_id::server::En
|
|||||||
{
|
{
|
||||||
return -ErrFailure;
|
return -ErrFailure;
|
||||||
}
|
}
|
||||||
|
if (node_id > NodeID::Max)
|
||||||
|
{
|
||||||
|
return -ErrFailure;
|
||||||
|
}
|
||||||
out_entry.node_id = static_cast<uint8_t>(node_id);
|
out_entry.node_id = static_cast<uint8_t>(node_id);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -227,12 +230,21 @@ int Log::init()
|
|||||||
|
|
||||||
// Reading max index
|
// Reading max index
|
||||||
{
|
{
|
||||||
IDynamicNodeIDStorageBackend::String key;
|
|
||||||
key = "log_last_index";
|
|
||||||
uint32_t value = 0;
|
uint32_t value = 0;
|
||||||
if (io.get(key, value) < 0)
|
if (io.get(getLastIndexKey(), value) < 0)
|
||||||
{
|
{
|
||||||
return -ErrFailure;
|
if (storage_.get(getLastIndexKey()).empty())
|
||||||
|
{
|
||||||
|
// It appears like there's no log in the storage - initializing empty log then
|
||||||
|
last_index_ = 0;
|
||||||
|
UAVCAN_TRACE("dynamic_node_id_server_impl::Log", "Initializing empty log");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There's some data in the storage, but it cannot be parsed - reporting an error
|
||||||
|
return -ErrFailure;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (value >= Capacity)
|
if (value >= Capacity)
|
||||||
{
|
{
|
||||||
@@ -251,28 +263,81 @@ int Log::init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UAVCAN_TRACE("dynamic_node_id_server_impl::Log", "Restored %u log entries", unsigned(last_index_));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Log::append(const protocol::dynamic_node_id::server::Entry& entry)
|
int Log::append(const protocol::dynamic_node_id::server::Entry& entry)
|
||||||
{
|
{
|
||||||
(void)entry;
|
if ((last_index_ + 1) >= Capacity)
|
||||||
return -1;
|
{
|
||||||
|
return -ErrLogic;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updating the last index
|
||||||
|
MarshallingStorageDecorator io(storage_);
|
||||||
|
uint32_t new_last_index = last_index_ + 1U;
|
||||||
|
res = io.setAndGetBack(getLastIndexKey(), new_last_index);
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (new_last_index != last_index_ + 1U)
|
||||||
|
{
|
||||||
|
return -ErrFailure;
|
||||||
|
}
|
||||||
|
entries_[new_last_index] = entry;
|
||||||
|
last_index_ = Index(new_last_index);
|
||||||
|
|
||||||
|
UAVCAN_TRACE("dynamic_node_id_server_impl::Log", "New entry, index %u, node ID %u, term %u",
|
||||||
|
unsigned(last_index_), unsigned(entry.node_id), unsigned(entry.term));
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Log::removeEntriesWhereIndexGreaterOrEqual(Index index)
|
int Log::removeEntriesWhereIndexGreaterOrEqual(Index index)
|
||||||
{
|
{
|
||||||
(void)index;
|
UAVCAN_ASSERT(last_index_ < Capacity);
|
||||||
return -1;
|
|
||||||
|
if (((index) >= Capacity) || (index == 0))
|
||||||
|
{
|
||||||
|
return -ErrLogic;
|
||||||
|
}
|
||||||
|
|
||||||
|
MarshallingStorageDecorator io(storage_);
|
||||||
|
uint32_t new_last_index = index - 1U;
|
||||||
|
int res = io.setAndGetBack(getLastIndexKey(), new_last_index);
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
if (new_last_index != index - 1U)
|
||||||
|
{
|
||||||
|
return -ErrFailure;
|
||||||
|
}
|
||||||
|
UAVCAN_TRACE("dynamic_node_id_server_impl::Log", "Entries removed, last index %u --> %u",
|
||||||
|
unsigned(last_index_), unsigned(new_last_index));
|
||||||
|
last_index_ = Index(new_last_index);
|
||||||
|
|
||||||
|
// Removal operation leaves dangling entries in storage, it's OK
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocol::dynamic_node_id::server::Entry* Log::getEntryAtIndex(Index index) const
|
const protocol::dynamic_node_id::server::Entry* Log::getEntryAtIndex(Index index) const
|
||||||
{
|
{
|
||||||
|
UAVCAN_ASSERT(last_index_ < Capacity);
|
||||||
return (index <= last_index_) ? &entries_[index] : NULL;
|
return (index <= last_index_) ? &entries_[index] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Log::isOtherLogUpToDate(Log::Index other_last_index, Term other_last_term) const
|
bool Log::isOtherLogUpToDate(Log::Index other_last_index, Term other_last_term) const
|
||||||
{
|
{
|
||||||
|
UAVCAN_ASSERT(last_index_ < Capacity);
|
||||||
// Terms are different - the one with higher term is more up-to-date
|
// Terms are different - the one with higher term is more up-to-date
|
||||||
if (other_last_term != entries_[last_index_].term)
|
if (other_last_term != entries_[last_index_].term)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator)
|
TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator)
|
||||||
{
|
{
|
||||||
StorageBackend st;
|
StorageBackend st;
|
||||||
@@ -117,3 +118,91 @@ TEST(DynamicNodeIDAllocationServer, MarshallingStorageDecorator)
|
|||||||
key = "the_cake_is_a_lie";
|
key = "the_cake_is_a_lie";
|
||||||
ASSERT_GT(0, marshaler.get(key, array));
|
ASSERT_GT(0, marshaler.get(key, array));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST(DynamicNodeIDAllocationServer, LogInitialization)
|
||||||
|
{
|
||||||
|
// No log data in the storage - initializing empty log
|
||||||
|
{
|
||||||
|
StorageBackend storage;
|
||||||
|
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
||||||
|
|
||||||
|
ASSERT_LE(0, log.init());
|
||||||
|
ASSERT_EQ(0, log.getLastIndex());
|
||||||
|
ASSERT_EQ(0, log.getEntryAtIndex(0)->term);
|
||||||
|
ASSERT_EQ(0, log.getEntryAtIndex(0)->node_id);
|
||||||
|
ASSERT_EQ(uavcan::protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id(),
|
||||||
|
log.getEntryAtIndex(0)->unique_id);
|
||||||
|
}
|
||||||
|
// Nonempty storage, one item
|
||||||
|
{
|
||||||
|
StorageBackend storage;
|
||||||
|
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
||||||
|
|
||||||
|
storage.set("log_last_index", "0");
|
||||||
|
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Expected one entry, none found
|
||||||
|
|
||||||
|
storage.set("log0_term", "0");
|
||||||
|
storage.set("log0_unique_id", "00000000000000000000000000000000");
|
||||||
|
storage.set("log0_node_id", "0");
|
||||||
|
ASSERT_LE(0, log.init()); // OK now
|
||||||
|
ASSERT_EQ(0, log.getLastIndex());
|
||||||
|
ASSERT_EQ(0, log.getEntryAtIndex(0)->term);
|
||||||
|
}
|
||||||
|
// Nonempty storage, broken data
|
||||||
|
{
|
||||||
|
StorageBackend storage;
|
||||||
|
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
||||||
|
|
||||||
|
storage.set("log_last_index", "foobar");
|
||||||
|
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Bad value
|
||||||
|
|
||||||
|
storage.set("log_last_index", "128");
|
||||||
|
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Bad value
|
||||||
|
|
||||||
|
storage.set("log_last_index", "0");
|
||||||
|
ASSERT_LE(-uavcan::ErrFailure, log.init()); // OK
|
||||||
|
|
||||||
|
storage.set("log0_term", "0");
|
||||||
|
storage.set("log0_unique_id", "00000000000000000000000000000000");
|
||||||
|
storage.set("log0_node_id", "128"); // Bad value (127 max)
|
||||||
|
ASSERT_LE(-uavcan::ErrFailure, log.init()); // Failed
|
||||||
|
ASSERT_EQ(0, log.getLastIndex());
|
||||||
|
ASSERT_EQ(0, log.getEntryAtIndex(0)->term);
|
||||||
|
}
|
||||||
|
// Nonempty storage, many items
|
||||||
|
{
|
||||||
|
StorageBackend storage;
|
||||||
|
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
||||||
|
|
||||||
|
storage.set("log_last_index", "1"); // 2 items - 0, 1
|
||||||
|
storage.set("log0_term", "0");
|
||||||
|
storage.set("log0_unique_id", "00000000000000000000000000000000");
|
||||||
|
storage.set("log0_node_id", "0");
|
||||||
|
storage.set("log1_term", "1");
|
||||||
|
storage.set("log1_unique_id", "0123456789abcdef0123456789abcdef");
|
||||||
|
storage.set("log1_node_id", "127");
|
||||||
|
|
||||||
|
ASSERT_LE(0, log.init()); // OK now
|
||||||
|
ASSERT_EQ(1, log.getLastIndex());
|
||||||
|
|
||||||
|
ASSERT_EQ(0, log.getEntryAtIndex(0)->term);
|
||||||
|
ASSERT_EQ(0, log.getEntryAtIndex(0)->node_id);
|
||||||
|
ASSERT_EQ(uavcan::protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id(),
|
||||||
|
log.getEntryAtIndex(0)->unique_id);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, log.getEntryAtIndex(1)->term);
|
||||||
|
ASSERT_EQ(127, log.getEntryAtIndex(1)->node_id);
|
||||||
|
uavcan::protocol::dynamic_node_id::server::Entry::FieldTypes::unique_id uid;
|
||||||
|
uid[0] = 0x01;
|
||||||
|
uid[1] = 0x23;
|
||||||
|
uid[2] = 0x45;
|
||||||
|
uid[3] = 0x67;
|
||||||
|
uid[4] = 0x89;
|
||||||
|
uid[5] = 0xab;
|
||||||
|
uid[6] = 0xcd;
|
||||||
|
uid[7] = 0xef;
|
||||||
|
uavcan::copy(uid.begin(), uid.begin() + 8, uid.begin() + 8);
|
||||||
|
ASSERT_EQ(uid, log.getEntryAtIndex(1)->unique_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user