3FS/tests/mgmtd/MgmtdTestHelper.cpp
2025-02-27 21:53:53 +08:00

164 lines
6.2 KiB
C++

#include "MgmtdTestHelper.h"
namespace hf3fs::mgmtd::testing {
CoTryTask<void> MgmtdTestHelper::setRoutingInfo(std::function<void(flat::RoutingInfo &)> callback) {
co_await extendLease();
auto handler = [&]() -> CoTryTask<void> {
auto writerLock = co_await op_.state_.coScopedLock<"test">();
std::optional<flat::RoutingInfo> tmpRoutingInfo;
{
auto dataPtr = co_await op_.state_.data_.coSharedLock();
tmpRoutingInfo = dataPtr->getRoutingInfo(flat::RoutingInfoVersion(0), op_.state_.config_);
XLOGF_IF(FATAL, !tmpRoutingInfo, "Empty routingInfo!");
CO_RETURN_ON_ERROR(checkDataIntegrity(*tmpRoutingInfo));
auto rv = tmpRoutingInfo->routingInfoVersion;
callback(*tmpRoutingInfo);
CO_RETURN_ON_ERROR(checkDataIntegrity(*tmpRoutingInfo));
tmpRoutingInfo->routingInfoVersion =
std::max(tmpRoutingInfo->routingInfoVersion, flat::RoutingInfoVersion(rv + 1));
}
auto storeHandler = [&](kv::IReadWriteTransaction &txn) -> CoTryTask<void> {
CO_RETURN_ON_ERROR(co_await op_.state_.store_.storeRoutingInfo(txn, *tmpRoutingInfo));
co_return Void{};
};
CO_RETURN_ON_ERROR(co_await kv::WithTransaction(op_.state_.createRetryStrategy())
.run(op_.state_.env_->kvEngine()->createReadWriteTransaction(), std::move(storeHandler)));
{
auto dataPtr = co_await op_.state_.data_.coLock();
auto &ri = dataPtr->routingInfo;
ri.nodeMap.clear();
ri.chainTables.clear();
ri.targets.clear();
ri.newBornChains.clear();
++ri.routingInfoVersion.toUnderType();
for ([[maybe_unused]] const auto &[nodeId, node] : tmpRoutingInfo->nodes) {
ri.nodeMap.try_emplace(nodeId, node);
}
ri.chainTables = tmpRoutingInfo->chainTables;
ri.chains = tmpRoutingInfo->chains;
for ([[maybe_unused]] const auto &[tid, ti] : tmpRoutingInfo->targets) {
ri.targets[tid].base() = ti;
}
}
co_return Void{};
};
co_return co_await doAsPrimary(op_.state_, std::move(handler));
}
Result<Void> MgmtdTestHelper::checkDataIntegrity(const flat::RoutingInfo &ri) {
using PS = flat::PublicTargetState;
using LS = flat::LocalTargetState;
// check all nodes valid
{
for ([[maybe_unused]] const auto &[nodeId, node] : ri.nodes) {
if (nodeId == 0) {
return makeError(StatusCode::kDataCorruption, "NodeId is 0");
}
}
}
// check all chaintables
{
for (const auto &[chainTableId, chainTables] : ri.chainTables) {
if (chainTableId == 0) {
return makeError(StatusCode::kDataCorruption, "ChainTableId is 0");
}
if (chainTables.empty()) {
return makeError(StatusCode::kDataCorruption, fmt::format("Empty versions of {}", chainTableId));
}
for (const auto &[v, table] : chainTables) {
if (v == 0) {
return makeError(StatusCode::kDataCorruption, fmt::format("ChainTableVersion of {} is 0", chainTableId));
}
if (table.chains.empty()) {
return makeError(StatusCode::kDataCorruption, fmt::format("Empty chains of {} with {}", chainTableId, v));
}
robin_hood::unordered_set<flat::ChainId> s;
size_t targets = 0;
for (auto cid : table.chains) {
if (cid == 0) {
return makeError(StatusCode::kDataCorruption,
fmt::format("ChainId(0) found in {} with {}", chainTableId, v));
}
if (!ri.chains.contains(cid)) {
return makeError(StatusCode::kDataCorruption,
fmt::format("{} in {} with {} not existed", cid, chainTableId, v));
}
if (!s.insert(cid).second) {
return makeError(StatusCode::kDataCorruption,
fmt::format("{} duplicated in {} with {}", cid, chainTableId, v));
}
const auto &chain = ri.chains.at(cid);
if (targets == 0) {
targets = chain.targets.size();
} else if (targets != chain.targets.size()) {
return makeError(StatusCode::kDataCorruption,
fmt::format("Multiple replica count found in {} with {}", chainTableId, v));
}
}
}
}
}
// check all chains
for ([[maybe_unused]] const auto &[_, chain] : ri.chains) {
if (chain.chainId == 0) {
return makeError(StatusCode::kDataCorruption, "ChainId is 0");
}
if (chain.targets.empty()) {
return makeError(StatusCode::kDataCorruption, fmt::format("Empty targets of {}", chain.chainId));
}
auto lastPs = PS::INVALID;
for (const auto &cti : chain.targets) {
if (cti.targetId == 0) {
return makeError(StatusCode::kDataCorruption, fmt::format("TargetId of {} is 0", chain.chainId));
}
if (cti.publicState == flat::PublicTargetState::INVALID) {
return makeError(StatusCode::kDataCorruption,
fmt::format("PublicTargetState of {} of {} is INVALID", cti.targetId, chain.chainId));
}
if (cti.publicState < lastPs) {
return makeError(
StatusCode::kDataCorruption,
fmt::format("PublicTargetState order inversion: {} after {}", toString(cti.publicState), toString(lastPs)));
}
lastPs = cti.publicState;
}
}
// check all targets valid
{
for (const auto &[tid, ti] : ri.targets) {
if (tid == 0) {
return makeError(StatusCode::kDataCorruption, "TargetId is 0");
}
if (ti.publicState == PS::INVALID) {
return makeError(StatusCode::kDataCorruption, fmt::format("PublicTargetState of {} is INVALID", tid));
}
if (ti.localState == LS::INVALID) {
return makeError(StatusCode::kDataCorruption, fmt::format("LocalTargetState of {} is INVALID", tid));
}
if (ti.chainId == 0) {
return makeError(StatusCode::kDataCorruption, fmt::format("ChainId of {} is 0", tid));
}
if (!ri.getChain(ti.chainId)) {
return makeError(StatusCode::kDataCorruption, fmt::format("ChainId {} of {} not found", ti.chainId, tid));
}
if (ti.nodeId && !ri.getNode(*ti.nodeId)) {
return makeError(StatusCode::kDataCorruption, fmt::format("NodeId {} of {} not found", *ti.nodeId, tid));
}
}
}
return Void{};
}
} // namespace hf3fs::mgmtd::testing