mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-05-23 11:07:36 +08:00
Persistent storage implementation and tests
This commit is contained in:
@@ -152,7 +152,12 @@ public:
|
||||
{ }
|
||||
|
||||
/**
|
||||
* This method invokes storage IO.
|
||||
* Initialization is performed as follows (every step may fail and return an error):
|
||||
* 1. Log is restored or initialized.
|
||||
* 2. Current term is restored. If there was no current term stored and the log is empty, it will be initialized
|
||||
* with zero.
|
||||
* 3. VotedFor value is restored. If there was no VotedFor value stored, the log is empty, and the current term is
|
||||
* zero, the value will be initialized with zero.
|
||||
*/
|
||||
int init();
|
||||
|
||||
|
||||
@@ -269,6 +269,7 @@ int Log::init()
|
||||
else
|
||||
{
|
||||
// There's some data in the storage, but it cannot be parsed - reporting an error
|
||||
UAVCAN_TRACE("dynamic_node_id_server_impl::Log", "Failed to read last index");
|
||||
return -ErrFailure;
|
||||
}
|
||||
}
|
||||
@@ -285,6 +286,8 @@ int Log::init()
|
||||
const int result = readEntryFromStorage(index, entries_[index]);
|
||||
if (result < 0)
|
||||
{
|
||||
UAVCAN_TRACE("dynamic_node_id_server_impl::Log", "Failed to read entry at index %u: %d",
|
||||
unsigned(index), result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -378,10 +381,13 @@ bool Log::isOtherLogUpToDate(Log::Index other_last_index, Term other_last_term)
|
||||
*/
|
||||
int PersistentState::init()
|
||||
{
|
||||
// Reading log
|
||||
/*
|
||||
* Reading log
|
||||
*/
|
||||
int res = log_.init();
|
||||
if (res < 0)
|
||||
{
|
||||
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState", "Log init failed: %d", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -392,13 +398,37 @@ int PersistentState::init()
|
||||
return -ErrLogic;
|
||||
}
|
||||
|
||||
const bool log_is_empty = (log_.getLastIndex() == 0) && (last_entry->term == 0);
|
||||
|
||||
MarshallingStorageDecorator io(storage_);
|
||||
|
||||
// Reading current term
|
||||
res = io.get(getCurrentTermKey(), current_term_);
|
||||
if (res < 0)
|
||||
/*
|
||||
* Reading currentTerm
|
||||
*/
|
||||
if (storage_.get(getCurrentTermKey()).empty() && log_is_empty)
|
||||
{
|
||||
return res;
|
||||
// First initialization
|
||||
current_term_ = 0;
|
||||
res = io.setAndGetBack(getCurrentTermKey(), current_term_);
|
||||
if (res < 0)
|
||||
{
|
||||
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState", "Failed to init current term: %d", res);
|
||||
return res;
|
||||
}
|
||||
if (current_term_ != 0)
|
||||
{
|
||||
return -ErrFailure;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restoring
|
||||
res = io.get(getCurrentTermKey(), current_term_);
|
||||
if (res < 0)
|
||||
{
|
||||
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState", "Failed to read current term: %d", res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_term_ < last_entry->term)
|
||||
@@ -409,15 +439,39 @@ int PersistentState::init()
|
||||
return -ErrLogic;
|
||||
}
|
||||
|
||||
// Reading voted for
|
||||
uint32_t stored_voted_for = 0;
|
||||
res = io.get(getVotedForKey(), stored_voted_for);
|
||||
if ((res < 0) || (stored_voted_for > NodeID::Max))
|
||||
/*
|
||||
* Reading votedFor
|
||||
*/
|
||||
if (storage_.get(getVotedForKey()).empty() && log_is_empty && (current_term_ == 0))
|
||||
{
|
||||
return -ErrFailure;
|
||||
// First initialization
|
||||
voted_for_ = NodeID(0);
|
||||
uint32_t stored_voted_for = 0;
|
||||
res = io.setAndGetBack(getVotedForKey(), stored_voted_for);
|
||||
if (res < 0)
|
||||
{
|
||||
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState", "Failed to init votedFor: %d", res);
|
||||
return res;
|
||||
}
|
||||
if (stored_voted_for != 0)
|
||||
{
|
||||
return -ErrFailure;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restoring
|
||||
uint32_t stored_voted_for = 0;
|
||||
res = io.get(getVotedForKey(), stored_voted_for);
|
||||
if (res < 0)
|
||||
{
|
||||
UAVCAN_TRACE("dynamic_node_id_server_impl::PersistentState", "Failed to read votedFor: %d", res);
|
||||
return res;
|
||||
}
|
||||
if (stored_voted_for > NodeID::Max)
|
||||
{
|
||||
return -ErrFailure;
|
||||
}
|
||||
voted_for_ = NodeID(uint8_t(stored_voted_for));
|
||||
}
|
||||
|
||||
@@ -426,7 +480,12 @@ int PersistentState::init()
|
||||
|
||||
int PersistentState::setCurrentTerm(const Term term)
|
||||
{
|
||||
UAVCAN_ASSERT(current_term_ <= term);
|
||||
if (term < current_term_)
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return -ErrInvalidParam;
|
||||
}
|
||||
|
||||
MarshallingStorageDecorator io(storage_);
|
||||
|
||||
Term tmp = term;
|
||||
@@ -447,7 +506,12 @@ int PersistentState::setCurrentTerm(const Term term)
|
||||
|
||||
int PersistentState::setVotedFor(const NodeID node_id)
|
||||
{
|
||||
voted_for_ = node_id;
|
||||
if (!node_id.isValid())
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return -ErrInvalidParam;
|
||||
}
|
||||
|
||||
MarshallingStorageDecorator io(storage_);
|
||||
|
||||
uint32_t tmp = node_id.get();
|
||||
|
||||
@@ -355,3 +355,187 @@ TEST(DynamicNodeIDAllocationServer, LogRemove)
|
||||
|
||||
storage.print();
|
||||
}
|
||||
|
||||
|
||||
TEST(DynamicNodeIDAllocationServer, PersistentStorageInitialization)
|
||||
{
|
||||
/*
|
||||
* First initialization
|
||||
*/
|
||||
{
|
||||
StorageBackend storage;
|
||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
||||
|
||||
ASSERT_EQ(0, storage.getNumKeys());
|
||||
ASSERT_LE(0, pers.init());
|
||||
|
||||
ASSERT_LE(3, storage.getNumKeys());
|
||||
ASSERT_EQ("0", storage.get("log_last_index"));
|
||||
ASSERT_EQ("0", storage.get("current_term"));
|
||||
ASSERT_EQ("0", storage.get("voted_for"));
|
||||
}
|
||||
/*
|
||||
* Partial recovery - only empty log is recovered
|
||||
*/
|
||||
{
|
||||
StorageBackend storage;
|
||||
|
||||
{
|
||||
// This log is used to initialize the storage
|
||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
||||
ASSERT_LE(0, log.init());
|
||||
}
|
||||
ASSERT_LE(1, storage.getNumKeys());
|
||||
|
||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
||||
|
||||
ASSERT_LE(0, pers.init());
|
||||
|
||||
ASSERT_LE(3, storage.getNumKeys());
|
||||
ASSERT_EQ("0", storage.get("log_last_index"));
|
||||
ASSERT_EQ("0", storage.get("current_term"));
|
||||
ASSERT_EQ("0", storage.get("voted_for"));
|
||||
}
|
||||
/*
|
||||
* Partial recovery - log and current term are recovered
|
||||
*/
|
||||
{
|
||||
StorageBackend storage;
|
||||
|
||||
{
|
||||
// This log is used to initialize the storage
|
||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
||||
ASSERT_LE(0, log.init());
|
||||
}
|
||||
ASSERT_LE(1, storage.getNumKeys());
|
||||
|
||||
storage.set("current_term", "1");
|
||||
|
||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
||||
|
||||
ASSERT_GT(0, pers.init()); // Fails because current term is not zero
|
||||
|
||||
storage.set("current_term", "0");
|
||||
|
||||
ASSERT_LE(0, pers.init()); // OK now
|
||||
|
||||
ASSERT_LE(3, storage.getNumKeys());
|
||||
ASSERT_EQ("0", storage.get("log_last_index"));
|
||||
ASSERT_EQ("0", storage.get("current_term"));
|
||||
ASSERT_EQ("0", storage.get("voted_for"));
|
||||
}
|
||||
/*
|
||||
* Full recovery
|
||||
*/
|
||||
{
|
||||
StorageBackend storage;
|
||||
|
||||
{
|
||||
// This log is used to initialize the storage
|
||||
uavcan::dynamic_node_id_server_impl::Log log(storage);
|
||||
ASSERT_LE(0, log.init());
|
||||
|
||||
uavcan::protocol::dynamic_node_id::server::Entry entry;
|
||||
entry.term = 1;
|
||||
entry.node_id = 1;
|
||||
entry.unique_id[0] = 1;
|
||||
ASSERT_LE(0, log.append(entry));
|
||||
}
|
||||
ASSERT_LE(4, storage.getNumKeys());
|
||||
|
||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
||||
|
||||
ASSERT_GT(0, pers.init()); // Fails because log is not empty
|
||||
|
||||
storage.set("current_term", "0");
|
||||
storage.set("voted_for", "0");
|
||||
ASSERT_GT(0, pers.init()); // Fails because of bad currentTerm
|
||||
|
||||
storage.set("current_term", "1"); // OK
|
||||
storage.set("voted_for", "128"); // Invalid value
|
||||
ASSERT_GT(0, pers.init()); // Fails because of bad votedFor
|
||||
|
||||
storage.set("voted_for", "0"); // OK now
|
||||
ASSERT_LE(0, pers.init());
|
||||
|
||||
ASSERT_LE(3, storage.getNumKeys());
|
||||
ASSERT_EQ("1", storage.get("log_last_index"));
|
||||
ASSERT_EQ("1", storage.get("current_term"));
|
||||
ASSERT_EQ("0", storage.get("voted_for"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(DynamicNodeIDAllocationServer, PersistentStorage)
|
||||
{
|
||||
StorageBackend storage;
|
||||
uavcan::dynamic_node_id_server_impl::PersistentState pers(storage);
|
||||
|
||||
/*
|
||||
* Initializing
|
||||
*/
|
||||
ASSERT_LE(0, pers.init());
|
||||
|
||||
ASSERT_EQ("0", storage.get("log_last_index"));
|
||||
ASSERT_EQ("0", storage.get("current_term"));
|
||||
ASSERT_EQ("0", storage.get("voted_for"));
|
||||
|
||||
/*
|
||||
* Inserting some log entries
|
||||
*/
|
||||
uavcan::protocol::dynamic_node_id::server::Entry entry;
|
||||
entry.term = 1;
|
||||
entry.node_id = 1;
|
||||
entry.unique_id[0] = 1;
|
||||
ASSERT_LE(0, pers.getLog().append(entry));
|
||||
|
||||
ASSERT_EQ("1", storage.get("log_last_index"));
|
||||
ASSERT_EQ("0", storage.get("current_term"));
|
||||
ASSERT_EQ("0", storage.get("voted_for"));
|
||||
|
||||
/*
|
||||
* Changing current term
|
||||
*/
|
||||
ASSERT_EQ(0, pers.getCurrentTerm());
|
||||
ASSERT_LE(0, pers.setCurrentTerm(2));
|
||||
ASSERT_EQ(2, pers.getCurrentTerm());
|
||||
|
||||
ASSERT_EQ("1", storage.get("log_last_index"));
|
||||
ASSERT_EQ("2", storage.get("current_term"));
|
||||
ASSERT_EQ("0", storage.get("voted_for"));
|
||||
|
||||
/*
|
||||
* Changing votedFor
|
||||
*/
|
||||
ASSERT_EQ(0, pers.getVotedFor().get());
|
||||
ASSERT_LE(0, pers.setVotedFor(0));
|
||||
ASSERT_EQ(0, pers.getVotedFor().get());
|
||||
ASSERT_LE(0, pers.setVotedFor(45));
|
||||
ASSERT_EQ(45, pers.getVotedFor().get());
|
||||
|
||||
ASSERT_EQ("1", storage.get("log_last_index"));
|
||||
ASSERT_EQ("2", storage.get("current_term"));
|
||||
ASSERT_EQ("45", storage.get("voted_for"));
|
||||
|
||||
/*
|
||||
* Handling errors
|
||||
*/
|
||||
storage.failOnSetCalls(true);
|
||||
|
||||
ASSERT_EQ(2, pers.getCurrentTerm());
|
||||
ASSERT_GT(0, pers.setCurrentTerm(7893));
|
||||
ASSERT_EQ(2, pers.getCurrentTerm());
|
||||
|
||||
ASSERT_EQ(45, pers.getVotedFor().get());
|
||||
ASSERT_GT(0, pers.setVotedFor(78));
|
||||
ASSERT_EQ(45, pers.getVotedFor().get());
|
||||
|
||||
ASSERT_EQ("1", storage.get("log_last_index"));
|
||||
ASSERT_EQ("2", storage.get("current_term"));
|
||||
ASSERT_EQ("45", storage.get("voted_for"));
|
||||
|
||||
/*
|
||||
* Final checks
|
||||
*/
|
||||
ASSERT_GT(10, storage.getNumKeys()); // Making sure there's some sane number of keys in the storage
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user