From 3e88c6b49a3e125cfde6004958497eebf95824c2 Mon Sep 17 00:00:00 2001 From: Igor Fedotov Date: Mon, 23 Jan 2023 13:59:41 +0300 Subject: [PATCH] test/libcephfs: add cases for last dir testing. Signed-off-by: Igor Fedotov --- src/test/libcephfs/snapdiff.cc | 235 +++++++++++++++++++++++++++++++-- 1 file changed, 224 insertions(+), 11 deletions(-) diff --git a/src/test/libcephfs/snapdiff.cc b/src/test/libcephfs/snapdiff.cc index dc31193d112..2320bf58b6c 100644 --- a/src/test/libcephfs/snapdiff.cc +++ b/src/test/libcephfs/snapdiff.cc @@ -16,6 +16,7 @@ #include "include/stat.h" #include "include/ceph_assert.h" #include "include/object.h" +#include "include/stringify.h" #include #include #include @@ -51,6 +52,10 @@ public: ceph_shutdown(cmount); } + int conf_get(const char *option, char *buf, size_t len) { + return ceph_conf_get(cmount, option, buf, len); + } + string make_file_path(const char* relpath) { char path[PATH_MAX]; sprintf(path, "%s/%s", dir_path, relpath); @@ -143,7 +148,7 @@ public: } int for_each_readdir(const char* relpath, - std::function fn) + std::function fn) { auto subdir_path = make_file_path(relpath); struct ceph_dir_result* ls_dir; @@ -151,13 +156,28 @@ public: if (r != 0) { return r; } - struct dirent* result; - while( nullptr != (result = ceph_readdir(cmount, ls_dir))) { - if (strcmp(result->d_name, ".") == 0 || - strcmp(result->d_name, "..") == 0) { + + while (1) { + struct dirent result; + struct ceph_statx stx; + + r = ceph_readdirplus_r( + cmount, ls_dir, &result, &stx, CEPH_STATX_BASIC_STATS, + 0, + NULL); + if (!r) + break; + if (r < 0) { + std::cerr << "ceph_readdirplus_r failed, error: " + << r << std::endl; + return r; + } + + if (strcmp(result.d_name, ".") == 0 || + strcmp(result.d_name, "..") == 0) { continue; } - if (!fn(result)) { + if (!fn(&result, &stx)) { r = -EINTR; break; } @@ -171,7 +191,7 @@ public: vector expected(expected0); auto end = expected.end(); int r = for_each_readdir(relpath, - [&](const dirent* dire) { + [&](const dirent* dire, const struct ceph_statx* stx) { std::string name(dire->d_name); auto it = std::find(expected.begin(), end, name); @@ -276,13 +296,13 @@ public: { int r = for_each_readdir(relpath0, - [&] (const dirent* dire) { + [&](const dirent* dire, const struct ceph_statx* stx) { string relpath = concat_path(relpath0, dire->d_name); - if (dire->d_type == DT_REG) { - unlink(relpath.c_str()); - } else if (dire->d_type == DT_DIR) { + if (S_ISDIR(stx->stx_mode)) { purge_dir(relpath.c_str()); rmdir(relpath.c_str()); + } else { + unlink(relpath.c_str()); } return true; }); @@ -314,6 +334,11 @@ public: void prepareSnapDiffLib1Cases(); void prepareSnapDiffLib2Cases(); void prepareSnapDiffLib3Cases(); + void prepareHugeSnapDiff(const std::string& name_prefix_start, + const std::string& name_prefix_bulk, + const std::string& name_prefix_end, + size_t file_count, + bool bulk_diff); }; // Helper function to verify readdir_snapdiff returns expected results @@ -609,6 +634,72 @@ void TestMount::prepareSnapDiffLib2Cases() ASSERT_EQ(0, mksnap("snap3")); } +/* The following method creates a folder with tons of file + updated between two snapshots + We're to test SnapDiff readdir API against that structure. + +* where: + - xN denotes file 'x' version N. + - X denotes folder name + - * denotes no/removed file/folder + +# snap1 snap2 +* aaaaA1 | aaaaA1 | +* aaaaB1 | * | +* * | aaaaC2 | +* aaaaD1 | aaaaD2 | +# file1 | file2| +* fileZ1 | fileA1 | +* zzzzA1 | zzzzA1 | +* zzzzB1 | * | +* * | zzzzC2 | +* zzzzD1 | zzzzD2 | +*/ + +void TestMount::prepareHugeSnapDiff(const std::string& name_prefix_start, + const std::string& name_prefix_bulk, + const std::string& name_prefix_end, + size_t file_count, + bool bulk_diff) +{ + //************ snap1 ************* + std::string startA = name_prefix_start + "A"; + std::string startB = name_prefix_start + "B"; + std::string startC = name_prefix_start + "C"; + std::string startD = name_prefix_start + "D"; + std::string endA = name_prefix_end + "A"; + std::string endB = name_prefix_end + "B"; + std::string endC = name_prefix_end + "C"; + std::string endD = name_prefix_end + "D"; + + ASSERT_LE(0, write_full(startA.c_str(), "hello world")); + ASSERT_LE(0, write_full(startB.c_str(), "hello world")); + ASSERT_LE(0, write_full(startD.c_str(), "hello world")); + for(size_t i = 0; i < file_count; i++) { + auto s = name_prefix_bulk + stringify(i); + ASSERT_LE(0, write_full(s.c_str(), "hello world")); + } + ASSERT_LE(0, write_full(endA.c_str(), "hello world")); + ASSERT_LE(0, write_full(endB.c_str(), "hello world")); + ASSERT_LE(0, write_full(endD.c_str(), "hello world")); + + ASSERT_EQ(0, mksnap("snap1")); + + ASSERT_LE(0, unlink(startB.c_str())); + ASSERT_LE(0, write_full(startC.c_str(), "hello world2")); + ASSERT_LE(0, write_full(startD.c_str(), "hello world2")); + if (bulk_diff) { + for(size_t i = 0; i < file_count; i++) { + auto s = std::string(name_prefix_bulk) + stringify(i); + ASSERT_LE(0, write_full(s.c_str(), "hello world2")); + } + } + ASSERT_LE(0, unlink(endB.c_str())); + ASSERT_LE(0, write_full(endC.c_str(), "hello world2")); + ASSERT_LE(0, write_full(endD.c_str(), "hello world2")); + ASSERT_EQ(0, mksnap("snap2")); +} + /* * More versatile SnapDiff readdir API verification, * includes 3 different snapshots and interleaving/repetitive calls to make sure @@ -1469,3 +1560,125 @@ TEST(LibCephFS, SnapDiffCases1_3) test_mount.rmsnap("snap2"); test_mount.rmsnap("snap3"); } + +/* +* SnapDiff readdir API testing for huge dir +* when delta is minor. +*/ +TEST(LibCephFS, HugeSnapDiffSmallDelta) +{ + TestMount test_mount; + + long int file_count = 10000; + printf("Seeding %ld files...\n", file_count); + + // Create simple directory tree with a couple of snapshots + // to test against. + string name_prefix_start = "aaaa"; + string name_prefix_bulk = "file"; + string name_prefix_end = "zzzz"; + test_mount.prepareHugeSnapDiff(name_prefix_start, + name_prefix_bulk, + name_prefix_end, + file_count, + false); + + uint64_t snapid1; + uint64_t snapid2; + + // learn snapshot ids and do basic verification + ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); + ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); + ASSERT_GT(snapid1, 0); + ASSERT_GT(snapid2, 0); + ASSERT_GT(snapid2, snapid1); + std::cout << snapid1 << " vs. " << snapid2 << std::endl; + + // + // Make sure snap1 vs. snap2 delta for the root is as expected + // + { + vector> expected; + expected.emplace_back(name_prefix_start + "B", snapid1); + expected.emplace_back(name_prefix_start + "C", snapid2); + expected.emplace_back(name_prefix_start + "D", snapid2); + + expected.emplace_back(name_prefix_end + "B", snapid1); + expected.emplace_back(name_prefix_end + "C", snapid2); + expected.emplace_back(name_prefix_end + "D", snapid2); + test_mount.verify_snap_diff(expected, "", "snap1", "snap2"); + } + + std::cout << "------------- closing -------------" << std::endl; + ASSERT_EQ(0, test_mount.purge_dir("")); + ASSERT_EQ(0, test_mount.rmsnap("snap1")); + ASSERT_EQ(0, test_mount.rmsnap("snap2")); +} + +/* +* SnapDiff readdir API testing for huge dir +* when delta is large +*/ +TEST(LibCephFS, HugeSnapDiffLargeDelta) +{ + TestMount test_mount; + + // Calculate amount of files required to have multiple directory fragments + // using relevant config parameters. + // file_count = mds_bal_spli_size * mds_bal_fragment_fast_factor + 100 + char buf[256]; + int r = test_mount.conf_get("mds_bal_split_size", buf, sizeof(buf)); + ASSERT_TRUE(r >= 0); + long int file_count = strtol(buf, nullptr, 10); + r = test_mount.conf_get("mds_bal_fragment_fast_factor ", buf, sizeof(buf)); + ASSERT_TRUE(r >= 0); + double factor = strtod(buf, nullptr); + file_count *= factor; + file_count += 100; + printf("Seeding %ld files...\n", file_count); + + // Create simple directory tree with a couple of snapshots + // to test against. + + string name_prefix_start = "aaaa"; + string name_prefix_bulk = "file"; + string name_prefix_end = "zzzz"; + + test_mount.prepareHugeSnapDiff(name_prefix_start, + name_prefix_bulk, + name_prefix_end, + file_count, + true); + uint64_t snapid1; + uint64_t snapid2; + + // learn snapshot ids and do basic verification + ASSERT_EQ(0, test_mount.get_snapid("snap1", &snapid1)); + ASSERT_EQ(0, test_mount.get_snapid("snap2", &snapid2)); + ASSERT_GT(snapid1, 0); + ASSERT_GT(snapid2, 0); + ASSERT_GT(snapid2, snapid1); + std::cout << snapid1 << " vs. " << snapid2 << std::endl; + + // + // Make sure snap1 vs. snap2 delta for the root is as expected + // + { + vector> expected; + expected.emplace_back(name_prefix_start + "B", snapid1); + expected.emplace_back(name_prefix_start + "C", snapid2); + expected.emplace_back(name_prefix_start + "D", snapid2); + for (size_t i = 0; i < (size_t)file_count; i++) { + expected.emplace_back(name_prefix_bulk + stringify(i), snapid2); + } + expected.emplace_back(name_prefix_end + "B", snapid1); + expected.emplace_back(name_prefix_end + "C", snapid2); + expected.emplace_back(name_prefix_end + "D", snapid2); + test_mount.verify_snap_diff(expected, "", "snap1", "snap2"); + } + + std::cout << "------------- closing -------------" << std::endl; + ASSERT_EQ(0, test_mount.purge_dir("")); + ASSERT_EQ(0, test_mount.rmsnap("snap1")); + ASSERT_EQ(0, test_mount.rmsnap("snap2")); +} -- 2.39.5