#include #include #include #include #include #include #include "common/utils/Coroutine.h" #include "fbs/meta/Common.h" #include "fbs/mgmtd/ChainRef.h" #include "fbs/mgmtd/MgmtdTypes.h" #include "gtest/gtest.h" #include "meta/components/ChainAllocator.h" #include "tests/meta/MetaTestBase.h" DEFINE_int64(chain_alloc_test, 50000, "files in test chain allocator"); namespace hf3fs::meta::server { template class TestChainAllocator : public MetaTestBase {}; using KVTypes = ::testing::Types; TYPED_TEST_SUITE(TestChainAllocator, KVTypes); TYPED_TEST(TestChainAllocator, basic) { folly::coro::blockingWait([&]() -> CoTask { auto cluster = this->createMockCluster(); auto mgmtd = cluster.mgmtdClient(); ChainAllocator alloc(mgmtd); auto empty = Layout::newEmpty(flat::ChainTableId(1), flat::ChainTableVersion(0), 512 << 10, 16); CO_ASSERT_OK(co_await alloc.checkLayoutValid(empty)); CO_ASSERT_OK(co_await alloc.allocateChainsForLayout(empty)); CO_ASSERT_TRUE(empty.valid(false)); }()); } TYPED_TEST(TestChainAllocator, perDirCounter) { folly::coro::blockingWait([&]() -> CoTask { auto cluster = this->createMockCluster(); auto &meta = cluster.meta().getOperator(); auto mgmtd = cluster.mgmtdClient(); ChainAllocator alloc(mgmtd); CO_ASSERT_OK( co_await meta.setAttr(SetAttrReq::setIFlags(SUPER_USER, InodeId::root(), IFlags(FS_CHAIN_ALLOCATION_FL)))); auto routing = mgmtd->getRoutingInfo(); auto chainCount = routing->raw()->getChainTable(flat::ChainTableId(1), flat::ChainTableVersion(0))->chains.size(); std::optional prevChain; for (size_t i = 0; i < 1000; i++) { auto result = co_await meta.create({SUPER_USER, folly::to(i), std::nullopt, OpenFlags(), p644}); CO_ASSERT_OK(result); auto layout = result->stat.asFile().layout; CO_ASSERT_OK(co_await alloc.checkLayoutValid(layout)); auto chain = std::get(layout.chains).baseIndex; if (prevChain) { CO_ASSERT_EQ(chain % chainCount, (*prevChain + layout.stripeSize) % chainCount); } prevChain = chain; } }()); } TYPED_TEST(TestChainAllocator, balance) { folly::coro::blockingWait([&]() -> CoTask { auto cluster = this->createMockCluster(); auto mgmtd = cluster.mgmtdClient(); ChainAllocator alloc(mgmtd); for (auto tableId : std::vector{flat::ChainTableId(1), flat::ChainTableId(2), flat::ChainTableId(3)}) { auto routing = mgmtd->getRoutingInfo(); auto chainCount = routing->raw()->getChainTable(tableId, flat::ChainTableVersion(0))->chains.size(); std::map map; std::vector stripeSizes{1, 2, 4, 8, 30, 32, 60, 64, 120, 128, 240, 256, 512}; for (int i = 0; i < FLAGS_chain_alloc_test; i++) { auto stripe = 1000000u; while (stripe > chainCount) { stripe = stripeSizes.at(folly::Random::rand64(stripeSizes.size())); } auto empty = Layout::newEmpty(tableId, 512 << 10, stripe); auto emptyCheck = co_await alloc.checkLayoutValid(empty); CO_ASSERT_OK(emptyCheck); CO_ASSERT_OK(co_await alloc.allocateChainsForLayout(empty)); CO_ASSERT_TRUE(empty.valid(false)); CO_ASSERT_OK(co_await alloc.checkLayoutValid(empty)); size_t begin = chainCount; std::set set; for (auto chain : empty.getChainIndexList()) { auto index = chain % chainCount ? chain % chainCount : chainCount; auto ref = flat::ChainRef(empty.tableId, empty.tableVersion, index); auto id = routing->raw()->getChainId(ref); CO_ASSERT_TRUE(id.has_value()); CO_ASSERT_FALSE(set.contains(*id)); set.emplace(*id); map[*id]++; begin = std::min(index, begin); } if (chainCount % stripe == 0 && stripe != 1) { CO_ASSERT_EQ(begin % stripe, 1) << fmt::format("{} {} {}", stripe, begin, fmt::join(set.begin(), set.end(), ",")); } } size_t min = map.begin()->second, max = map.begin()->second; for (auto [id, cnt] : map) { (void)id; min = std::min(cnt, min); max = std::max(cnt, max); } CO_ASSERT_EQ(map.size(), chainCount); CO_ASSERT_GE(min + stripeSizes.size(), max); } }()); } } // namespace hf3fs::meta::server