Skip to content

Commit 250d307

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: I3f9f91ee34b82a748e8145b70a4ce47089ad3687 Reviewed-on: http://review.couchbase.org/c/kv_engine/+/137698 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent 5a707cd commit 250d307

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
@@ -475,29 +475,44 @@ flatbuffers::DetachedBuffer Flush::encodeOpenCollections(
475475

476476
flatbuffers::DetachedBuffer Flush::encodeDroppedCollections(
477477
std::vector<Collections::KVStore::DroppedCollection>& existingDropped) {
478+
flatbuffers::FlatBufferBuilder builder;
479+
std::vector<flatbuffers::Offset<Collections::KVStore::Dropped>> output;
480+
481+
// Add Collection's to this set only if they are in both old and new
482+
// sets of dropped collections - this ensures we generate it once into
483+
// the new output.
484+
std::unordered_set<CollectionID> skip;
485+
478486
// Iterate through the existing dropped collections and look each up in the
479487
// commit metadata. If the collection is in both lists, we will just update
480488
// the existing data (adjusting the endSeqno) and then erase the collection
481489
// from the commit meta's dropped collections.
482490
for (auto& collection : existingDropped) {
483491
if (auto itr = droppedCollections.find(collection.collectionId);
484492
itr != droppedCollections.end()) {
493+
// Collection is in both old and new 'sets' of dropped collections
494+
// we only want it in the output once - update the end-seqno here
495+
// and add to skip set
485496
collection.endSeqno = itr->second.endSeqno;
486-
487-
// Now kick the collection out of collectionsMeta, its contribution
488-
// to the final output is complete
489-
droppedCollections.erase(itr);
497+
skip.emplace(collection.collectionId);
490498
}
499+
auto newEntry = Collections::KVStore::CreateDropped(
500+
builder,
501+
collection.startSeqno,
502+
collection.endSeqno,
503+
uint32_t(collection.collectionId));
504+
output.push_back(newEntry);
491505
}
492506

493-
flatbuffers::FlatBufferBuilder builder;
494-
std::vector<flatbuffers::Offset<Collections::KVStore::Dropped>> output;
495-
496-
// Now merge, first the newly dropped collections
497-
// Iterate through the list collections dropped in the commit batch and
507+
// Now add the newly dropped collections
508+
// Iterate through the set of collections dropped in the commit batch and
498509
// and create flatbuffer versions of each one
499510
for (const auto& [cid, dropped] : droppedCollections) {
500511
(void)cid;
512+
if (skip.count(cid) > 0) {
513+
// This collection is already in output
514+
continue;
515+
}
501516
auto newEntry = Collections::KVStore::CreateDropped(
502517
builder,
503518
dropped.startSeqno,
@@ -506,16 +521,6 @@ flatbuffers::DetachedBuffer Flush::encodeDroppedCollections(
506521
output.push_back(newEntry);
507522
}
508523

509-
// and now copy across the existing dropped collections
510-
for (const auto& entry : existingDropped) {
511-
auto newEntry = Collections::KVStore::CreateDropped(
512-
builder,
513-
entry.startSeqno,
514-
entry.endSeqno,
515-
uint32_t(entry.collectionId));
516-
output.push_back(newEntry);
517-
}
518-
519524
auto vector = builder.CreateVector(output);
520525
auto final =
521526
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)