Skip to content

Commit a3cc422

Browse files
committed
MB-41321: 2/4 Clean-up if collection exists in old and new drop containers
Collections::Flush was erasing a collection from the new drop 'map' when it was found to be in both 'old' and 'new' - this is so the flatbuffer output (which is a vector) only had the dropped collection once. However this leads to code executing later in the flush to not see the collection when iterating through the 'droppecCollections' map. For example notifyManifestOfAnyDroppedCollections would not get the VB::Manifest to clean-up (effectively leaking memory). This fix ensures we still generate the collection once in the flatbuffers output, but also keep it in the list and now clean-up. Change-Id: I4f05d8728bb169bce309c5331ed41fb41c1a7ecd Reviewed-on: http://review.couchbase.org/c/kv_engine/+/137497 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent 5e5d224 commit a3cc422

File tree

2 files changed

+49
-19
lines changed

2 files changed

+49
-19
lines changed

engines/ep/src/collections/flush.cc

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -448,29 +448,44 @@ flatbuffers::DetachedBuffer Flush::encodeOpenCollections(
448448

449449
flatbuffers::DetachedBuffer Flush::encodeDroppedCollections(
450450
std::vector<Collections::KVStore::DroppedCollection>& existingDropped) {
451+
flatbuffers::FlatBufferBuilder builder;
452+
std::vector<flatbuffers::Offset<Collections::KVStore::Dropped>> output;
453+
454+
// Add Collection's to this set only if they are in both old and new
455+
// sets of dropped collections - this ensures we generate it once into
456+
// the new output.
457+
std::unordered_set<CollectionID> skip;
458+
451459
// Iterate through the existing dropped collections and look each up in the
452460
// commit metadata. If the collection is in both lists, we will just update
453461
// the existing data (adjusting the endSeqno) and then erase the collection
454462
// from the commit meta's dropped collections.
455463
for (auto& collection : existingDropped) {
456464
if (auto itr = droppedCollections.find(collection.collectionId);
457465
itr != droppedCollections.end()) {
466+
// Collection is in both old and new 'sets' of dropped collections
467+
// we only want it in the output once - update the end-seqno here
468+
// and add to skip set
458469
collection.endSeqno = itr->second.endSeqno;
459-
460-
// Now kick the collection out of collectionsMeta, its contribution
461-
// to the final output is complete
462-
droppedCollections.erase(itr);
470+
skip.emplace(collection.collectionId);
463471
}
472+
auto newEntry = Collections::KVStore::CreateDropped(
473+
builder,
474+
collection.startSeqno,
475+
collection.endSeqno,
476+
uint32_t(collection.collectionId));
477+
output.push_back(newEntry);
464478
}
465479

466-
flatbuffers::FlatBufferBuilder builder;
467-
std::vector<flatbuffers::Offset<Collections::KVStore::Dropped>> output;
468-
469-
// Now merge, first the newly dropped collections
470-
// Iterate through the list collections dropped in the commit batch and
480+
// Now add the newly dropped collections
481+
// Iterate through the set of collections dropped in the commit batch and
471482
// and create flatbuffer versions of each one
472483
for (const auto& [cid, dropped] : droppedCollections) {
473484
(void)cid;
485+
if (skip.count(cid) > 0) {
486+
// This collection is already in output
487+
continue;
488+
}
474489
auto newEntry = Collections::KVStore::CreateDropped(
475490
builder,
476491
dropped.startSeqno,
@@ -479,16 +494,6 @@ flatbuffers::DetachedBuffer Flush::encodeDroppedCollections(
479494
output.push_back(newEntry);
480495
}
481496

482-
// and now copy across the existing dropped collections
483-
for (const auto& entry : existingDropped) {
484-
auto newEntry = Collections::KVStore::CreateDropped(
485-
builder,
486-
entry.startSeqno,
487-
entry.endSeqno,
488-
uint32_t(entry.collectionId));
489-
output.push_back(newEntry);
490-
}
491-
492497
auto vector = builder.CreateVector(output);
493498
auto final =
494499
Collections::KVStore::CreateDroppedCollections(builder, vector);

engines/ep/tests/module_tests/collections/evp_store_collections_test.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2858,6 +2858,31 @@ TEST_P(CollectionsParameterizedTest, OneScopeStatsByIdParsing) {
28582858
EXPECT_EQ(cm.getUid(), result.getManifestId());
28592859
}
28602860

2861+
// Test a specific issue spotted, in the case when a collection exists
2862+
// as dropped in storage and is newly dropped - we clean-up
2863+
TEST_P(CollectionsPersistentParameterizedTest, FlushDropCreateDropCleansUp) {
2864+
CollectionsManifest cm;
2865+
cm.add(CollectionEntry::fruit); // seq:1
2866+
setCollections(cookie, std::string{cm});
2867+
flushVBucketToDiskIfPersistent(vbid, 1);
2868+
cm.remove(CollectionEntry::fruit); // seq:2
2869+
setCollections(cookie, std::string{cm});
2870+
flushVBucketToDiskIfPersistent(vbid, 1);
2871+
cm.add(CollectionEntry::fruit); // seq:3
2872+
setCollections(cookie, std::string{cm});
2873+
flushVBucketToDiskIfPersistent(vbid, 1);
2874+
cm.remove(CollectionEntry::fruit); // seq:4
2875+
setCollections(cookie, std::string{cm});
2876+
flushVBucketToDiskIfPersistent(vbid, 1);
2877+
2878+
VBucketPtr vb = store->getVBucket(vbid);
2879+
2880+
// Final flush should of removed making this invalid and we get an exception
2881+
EXPECT_THROW(vb->getManifest().lock().getStatsForFlush(
2882+
CollectionEntry::fruit, 4),
2883+
std::logic_error);
2884+
}
2885+
28612886
INSTANTIATE_TEST_SUITE_P(CollectionsExpiryLimitTests,
28622887
CollectionsExpiryLimitTest,
28632888
::testing::Bool(),

0 commit comments

Comments
 (0)