diff --git a/src/osd/ECBackend.cc b/src/osd/ECBackend.cc index 4378eec3930..64b60fc3b86 100644 --- a/src/osd/ECBackend.cc +++ b/src/osd/ECBackend.cc @@ -1808,11 +1808,7 @@ int ECBackend::omap_get( } // Remove keys in removed_ranges - for (auto out_it = out->begin(); out_it != out->end(); ++out_it) { - if (should_be_removed(removed_ranges, out_it->first)) { - out->erase(out_it->first); - } - } + remove_keys_in_ranges(removed_ranges, out); // Apply updates in update_map for (const auto &[key, val_opt] : update_map) { @@ -1887,3 +1883,12 @@ bool ECBackend::should_be_removed( // No ranges contain the key, return false return false; } + +void ECBackend::remove_keys_in_ranges( + const std::map>& removed_ranges, + std::map* out) { + for (const auto& [start, end] : removed_ranges) { + out->erase(out->lower_bound(start), + end ? out->lower_bound(*end) : out->end()); + } +} diff --git a/src/osd/ECBackend.h b/src/osd/ECBackend.h index a5f013a0bb3..30e1c2db537 100644 --- a/src/osd/ECBackend.h +++ b/src/osd/ECBackend.h @@ -456,4 +456,9 @@ public: const std::map>& removed_ranges, const std::string_view key ); + + static void remove_keys_in_ranges( + const std::map>& removed_ranges, + std::map* out + ); }; diff --git a/src/test/osd/test_ec_omap_journal.cc b/src/test/osd/test_ec_omap_journal.cc index 58cb193c0cf..45d4425aba4 100644 --- a/src/test/osd/test_ec_omap_journal.cc +++ b/src/test/osd/test_ec_omap_journal.cc @@ -17,6 +17,7 @@ #include "test/unit.cc" #include "osd/ECOmapJournal.h" +#include "osd/ECBackend.h" #include "common/dout.h" class MockDoutPrefixProvider : public DoutPrefixProvider { @@ -1450,4 +1451,30 @@ TEST(ecomapjournal, has_omap_updates_after_remove_entry) // Should not have updates after removing all entries ASSERT_FALSE(journal.has_omap_updates(test_hoid)); -} \ No newline at end of file +} + +// A removed range spanning several adjacent keys erases all of them. +TEST(ecbackend_remove_keys_in_ranges, removes_contiguous_block) +{ + std::map out; + for (std::string_view k : {"k01", "k02", "k03", "k04", "k05", "k06"}) { + out[std::string(k)].append(k); + } + std::map> removed = { + {"k02", "k05"}, // -> erases k02, k03, k04 + }; + + ECBackend::remove_keys_in_ranges(removed, &out); + + std::vector remaining; + for (const auto &[k, v] : out) { + remaining.push_back(k); + } + EXPECT_EQ((std::vector{"k01", "k05", "k06"}), remaining); + + // Surviving values must be untouched. + ASSERT_TRUE(out.contains("k05")); + ceph::buffer::list k05_bl; + k05_bl.append("k05"); + EXPECT_TRUE(out["k05"].contents_equal(k05_bl)); +}