From f5e3f263724d04be181225d9e24cbe2f4669e8bc Mon Sep 17 00:00:00 2001 From: Ilya Dryomov <idryomov@gmail.com> Date: Wed, 29 Nov 2023 12:28:51 +0100 Subject: [PATCH] test/librbd: add DiffIterateTest.DiffIterateDeterministic{,PP} scribble()-based DiffIterate tests are too weak: at least two regressions that should been caught by DiffIterate.DiffIterate or DiffIterate.DiffIterateStress were missed [1][2]. Aside from the randomness which can be both a good and a bad thing, asserts there ensure only that the returned diff covers all changes that were made. If the returned diff is too excessive or otherwise bogus, this isn't detected [3]. Add a deterministic test to systematically cover the most common cases that don't involve discards. A similar test for discards will be added with the fix for [4]. Comment out debug log in vector_iterate_cb() like it's done in iterate_cb(). [1] https://tracker.ceph.com/issues/50787 [2] https://tracker.ceph.com/issues/63654 [3] https://tracker.ceph.com/issues/63719 [4] https://tracker.ceph.com/issues/53897 Signed-off-by: Ilya Dryomov <idryomov@gmail.com> --- src/test/librbd/test_librbd.cc | 263 ++++++++++++++++++++++++++++++++- 1 file changed, 262 insertions(+), 1 deletion(-) diff --git a/src/test/librbd/test_librbd.cc b/src/test/librbd/test_librbd.cc index f4bb74fe397..372340c7b8e 100644 --- a/src/test/librbd/test_librbd.cc +++ b/src/test/librbd/test_librbd.cc @@ -7501,12 +7501,273 @@ ostream& operator<<(ostream & o, const diff_extent& e) { int vector_iterate_cb(uint64_t off, size_t len, int exists, void *arg) { - cout << "iterate_cb " << off << "~" << len << std::endl; + //cout << "iterate_cb " << off << "~" << len << std::endl; vector<diff_extent> *diff = static_cast<vector<diff_extent> *>(arg); diff->push_back(diff_extent(off, len, exists, 0)); return 0; } +TYPED_TEST(DiffIterateTest, DiffIterateDeterministic) +{ + REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2)); + + rados_ioctx_t ioctx; + ASSERT_EQ(0, rados_ioctx_create(this->_cluster, this->m_pool_name.c_str(), + &ioctx)); + + rbd_image_t image; + int order = 22; + std::string name = this->get_temp_image_name(); + uint64_t size = 20 << 20; + + ASSERT_EQ(0, create_image(ioctx, name.c_str(), size, &order)); + ASSERT_EQ(0, rbd_open(ioctx, name.c_str(), &image, NULL)); + + uint64_t object_size = 0; + if (this->whole_object) { + object_size = 1 << order; + } + + std::vector<diff_extent> extents; + ASSERT_EQ(0, rbd_diff_iterate2(image, NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(0u, extents.size()); + + ASSERT_EQ(0, rbd_snap_create(image, "snap1")); + + std::string buf(256, '1'); + ASSERT_EQ(256, rbd_write(image, 0, 256, buf.data())); + ASSERT_EQ(0, rbd_diff_iterate2(image, NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + extents.clear(); + + ASSERT_EQ(0, rbd_snap_create(image, "snap2")); + + ASSERT_EQ(256, rbd_write(image, 1 << order, 256, buf.data())); + ASSERT_EQ(0, rbd_diff_iterate2(image, NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + ASSERT_EQ(0, rbd_snap_create(image, "snap3")); + + // 1. beginning of time -> HEAD + ASSERT_EQ(0, rbd_diff_iterate2(image, NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 2. snap1 -> HEAD + ASSERT_EQ(0, rbd_diff_iterate2(image, "snap1", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 3. snap2 -> HEAD + ASSERT_EQ(0, rbd_diff_iterate2(image, "snap2", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[0]); + extents.clear(); + + // 4. snap3 -> HEAD + ASSERT_EQ(0, rbd_diff_iterate2(image, "snap3", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(0u, extents.size()); + + ASSERT_PASSED(this->validate_object_map, image); + ASSERT_EQ(0, rbd_snap_set(image, "snap3")); + + // 5. beginning of time -> snap3 + ASSERT_EQ(0, rbd_diff_iterate2(image, NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 6. snap1 -> snap3 + ASSERT_EQ(0, rbd_diff_iterate2(image, "snap1", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 7. snap2 -> snap3 + ASSERT_EQ(0, rbd_diff_iterate2(image, "snap2", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[0]); + extents.clear(); + + ASSERT_PASSED(this->validate_object_map, image); + ASSERT_EQ(0, rbd_snap_set(image, "snap2")); + + // 8. beginning of time -> snap2 + ASSERT_EQ(0, rbd_diff_iterate2(image, NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + extents.clear(); + + // 9. snap1 -> snap2 + ASSERT_EQ(0, rbd_diff_iterate2(image, "snap1", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + extents.clear(); + + ASSERT_PASSED(this->validate_object_map, image); + ASSERT_EQ(0, rbd_snap_set(image, "snap1")); + + // 10. beginning of time -> snap1 + ASSERT_EQ(0, rbd_diff_iterate2(image, NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(0u, extents.size()); + + ASSERT_PASSED(this->validate_object_map, image); +} + +TYPED_TEST(DiffIterateTest, DiffIterateDeterministicPP) +{ + REQUIRE(!is_feature_enabled(RBD_FEATURE_STRIPINGV2)); + + librados::IoCtx ioctx; + ASSERT_EQ(0, this->_rados.ioctx_create(this->m_pool_name.c_str(), ioctx)); + + librbd::RBD rbd; + librbd::Image image; + int order = 22; + std::string name = this->get_temp_image_name(); + uint64_t size = 20 << 20; + + ASSERT_EQ(0, create_image_pp(rbd, ioctx, name.c_str(), size, &order)); + ASSERT_EQ(0, rbd.open(ioctx, image, name.c_str(), NULL)); + + uint64_t object_size = 0; + if (this->whole_object) { + object_size = 1 << order; + } + + std::vector<diff_extent> extents; + ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(0u, extents.size()); + + ASSERT_EQ(0, image.snap_create("snap1")); + + ceph::bufferlist bl; + bl.append(std::string(256, '1')); + ASSERT_EQ(256, image.write(0, 256, bl)); + ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + extents.clear(); + + ASSERT_EQ(0, image.snap_create("snap2")); + + ASSERT_EQ(256, image.write(1 << order, 256, bl)); + ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + ASSERT_EQ(0, image.snap_create("snap3")); + + // 1. beginning of time -> HEAD + ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 2. snap1 -> HEAD + ASSERT_EQ(0, image.diff_iterate2("snap1", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 3. snap2 -> HEAD + ASSERT_EQ(0, image.diff_iterate2("snap2", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[0]); + extents.clear(); + + // 4. snap3 -> HEAD + ASSERT_EQ(0, image.diff_iterate2("snap3", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(0u, extents.size()); + + ASSERT_PASSED(this->validate_object_map, image); + ASSERT_EQ(0, image.snap_set("snap3")); + + // 5. beginning of time -> snap3 + ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 6. snap1 -> snap3 + ASSERT_EQ(0, image.diff_iterate2("snap1", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(2u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[1]); + extents.clear(); + + // 7. snap2 -> snap3 + ASSERT_EQ(0, image.diff_iterate2("snap2", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(1 << order, 256, true, object_size), extents[0]); + extents.clear(); + + ASSERT_PASSED(this->validate_object_map, image); + ASSERT_EQ(0, image.snap_set("snap2")); + + // 8. beginning of time -> snap2 + ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + extents.clear(); + + // 9. snap1 -> snap2 + ASSERT_EQ(0, image.diff_iterate2("snap1", 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(1u, extents.size()); + ASSERT_EQ(diff_extent(0, 256, true, object_size), extents[0]); + extents.clear(); + + ASSERT_PASSED(this->validate_object_map, image); + ASSERT_EQ(0, image.snap_set("snap1")); + + // 10. beginning of time -> snap1 + ASSERT_EQ(0, image.diff_iterate2(NULL, 0, size, true, this->whole_object, + vector_iterate_cb, &extents)); + ASSERT_EQ(0u, extents.size()); + + ASSERT_PASSED(this->validate_object_map, image); +} + TYPED_TEST(DiffIterateTest, DiffIterateDiscard) { librados::IoCtx ioctx;