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>
This commit is contained in:
Ilya Dryomov 2023-11-29 12:28:51 +01:00
parent 0a1f633e02
commit f5e3f26372

View File

@ -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;