From 4c3f4efa3409979d1d16b4e01d642013a0524013 Mon Sep 17 00:00:00 2001 From: dheart Date: Sun, 10 May 2026 09:53:35 +0800 Subject: [PATCH] os/bluestore: check unshare blobs during snapshot deletion and fix unshare logic. Signed-off-by: dheart --- src/os/bluestore/BlueStore.cc | 13 +++- src/test/objectstore/store_test.cc | 96 ++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/os/bluestore/BlueStore.cc b/src/os/bluestore/BlueStore.cc index 6e0b94f3abe..aa9c06ac202 100644 --- a/src/os/bluestore/BlueStore.cc +++ b/src/os/bluestore/BlueStore.cc @@ -18221,7 +18221,8 @@ int BlueStore::_do_remove( { set maybe_unshared_blobs; bool is_gen = !o->oid.is_no_gen(); - _do_truncate(txc, c, o, 0, is_gen ? &maybe_unshared_blobs : nullptr); + bool is_snap = o->oid.hobj.is_snap(); + _do_truncate(txc, c, o, 0, (is_gen || is_snap) ? &maybe_unshared_blobs : nullptr); if (o->onode.has_omap()) { o->flush(); _do_omap_clear(txc, o); @@ -18243,7 +18244,7 @@ int BlueStore::_do_remove( o->onode = bluestore_onode_t(); _debug_obj_on_delete(o->oid); - if (!is_gen || maybe_unshared_blobs.empty()) { + if (maybe_unshared_blobs.empty()) { return 0; } @@ -18251,12 +18252,18 @@ int BlueStore::_do_remove( dout(10) << __func__ << " gen and maybe_unshared_blobs " << maybe_unshared_blobs << dendl; ghobject_t nogen = o->oid; - nogen.generation = ghobject_t::NO_GEN; + if (is_gen) + nogen.generation = ghobject_t::NO_GEN; + else if (is_snap) + nogen.hobj.snap = CEPH_NOSNAP; OnodeRef h = c->get_onode(nogen, false); if (!h || !h->exists) { return 0; } + + //Populate the extent map structure from DB; required for shared blob processing below. + h->extent_map.fault_range(db, 0, h->onode.size); // Set maybe_unshared_blobs contains those shared blobs that have all nref=1. // Is .head object is using all those segments? // If it is using all, then no one else can use the shared blob, diff --git a/src/test/objectstore/store_test.cc b/src/test/objectstore/store_test.cc index 1ab4191dda1..fe399166e3a 100644 --- a/src/test/objectstore/store_test.cc +++ b/src/test/objectstore/store_test.cc @@ -4139,6 +4139,102 @@ TEST_P(StoreTest, BlueStoreUnshareBlobTest) { ASSERT_EQ(cnt, 0); } } + { + ghobject_t hoid(hobject_t(sobject_t("Object 2", CEPH_NOSNAP))); + ghobject_t coid1 = hoid; + coid1.hobj.snap = 1; + ghobject_t coid2 = hoid; + coid2.hobj.snap = 2; + + bufferlist data, newdata; + data.append(string(1<<20, 'a')); //Conditions for resharding are met. + + ObjectStore::Transaction t; + t.write(cid, hoid, 0, data.length(), data); + cerr << "Creating object and write 1M " << hoid << std::endl; + r = queue_transaction(store, ch, std::move(t)); + ASSERT_EQ(r, 0); + + ObjectStore::Transaction clone_t1; + clone_t1.clone(cid, hoid, coid1); + cerr << "Clone object" << std::endl; + r = queue_transaction(store, ch, std::move(clone_t1)); + ASSERT_EQ(r, 0); + + ObjectStore::Transaction clone_t2; + clone_t2.clone(cid, hoid, coid2); + cerr << "Clone object" << std::endl; + r = queue_transaction(store, ch, std::move(clone_t2)); + ASSERT_EQ(r, 0); + + data.clear(); + data.append(string(4096, 'b')); + + ObjectStore::Transaction t3; + t3.write(cid, hoid, 0, data.length(), data); + cerr << "Writing 4k to source object " << hoid << std::endl; + r = queue_transaction(store, ch, std::move(t3)); + ASSERT_EQ(r, 0); + ObjectStore::Transaction t4; + t4.write(cid, hoid, 8192, data.length(), data); + cerr << "Writing 4k to source object " << hoid << std::endl; + t4.write(cid, hoid, 16384, data.length(), data); + cerr << "Writing 4k to source object " << hoid << std::endl; + r = queue_transaction(store, ch, std::move(t4)); + ASSERT_EQ(r, 0); + + { + BlueStore* bstore = dynamic_cast (store.get()); + ch.reset(); + // this trims hoid one out of onode cache + EXPECT_EQ(bstore->umount(), 0); + bluestore_stats_t store_stats1; + EXPECT_EQ(bstore->fsck_with_stats(true, store_stats1), 0); + EXPECT_EQ(bstore->mount(), 0); + ch = bstore->open_collection(cid); + + ObjectStore::Transaction c1_remove_t; + c1_remove_t.remove(cid, coid1); + cerr << "Deleting dest object" << coid1 << std::endl; + r = queue_transaction(bstore, ch, std::move(c1_remove_t)); + ASSERT_EQ(r, 0); + + ch.reset(); + // this ensures remove operation submitted to kv store + EXPECT_EQ(bstore->umount(), 0); + bluestore_stats_t store_stats2; + EXPECT_EQ(bstore->fsck_with_stats(true, store_stats2), 0); + EXPECT_EQ(bstore->mount(), 0); + ch = bstore->open_collection(cid); + + ASSERT_EQ(store_stats1.num_objects-store_stats2.num_objects, 1); + ASSERT_EQ(store_stats1.num_shared_blobs, store_stats2.num_shared_blobs); + + ObjectStore::Transaction c2_remove_t; + c2_remove_t.remove(cid, coid2); + cerr << "Deleting dest object" << coid2 << std::endl; + r = queue_transaction(store, ch, std::move(c2_remove_t)); + ASSERT_EQ(r, 0); + + ch.reset(); + // this ensures remove operation submitted to kv store + EXPECT_EQ(bstore->umount(), 0); + bluestore_stats_t store_stats3; + EXPECT_EQ(bstore->fsck_with_stats(true, store_stats3), 0); + EXPECT_EQ(bstore->mount(), 0); + ch = bstore->open_collection(cid); + + ASSERT_EQ(store_stats2.num_objects-store_stats3.num_objects, 1); + ASSERT_EQ(store_stats3.num_shared_blobs, 0); + } + { + ObjectStore::Transaction t; + t.remove(cid, hoid); + cerr << "Cleaning" << std::endl; + r = queue_transaction(store, ch, std::move(t)); + ASSERT_EQ(r, 0); + } + } { ObjectStore::Transaction t; t.remove(cid, hoid); -- 2.47.3