Skip to content

Commit 8aa650c

Browse files
paolococchidaverigby
authored andcommitted
MB-42568: [Ephe] Correctly update num-deleted-item in updateStoredValue
In EphemeralVBucket::updateStoredValue we try to update an existing OSV and move it to the end of the SeqList if possible. That may be prevented by range-reads that cover that OSV. In that case we add a new stored value at the end of the SeqList and leave the old OSV at its place. Before this patch we used to always update the SeqList::numDeletedItems counter based on the semantic of an "update SV", also for when the "add SV" logic is executed. That led to over-decrementing the num of deletions, which underflows the counter in some scenarios. Change-Id: Ib5c8910d68a4edcc0da1bf16e62e0acb51c70894 Reviewed-on: http://review.couchbase.org/c/kv_engine/+/143512 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent e6296af commit 8aa650c

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed

engines/ep/src/ephemeral_vb.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,9 @@ EphemeralVBucket::updateStoredValue(const HashTable::HashBucketLock& hbl,
437437
auto result = ht.unlocked_updateStoredValue(hbl, v, itm);
438438
status = result.status;
439439
newSv = result.storedValue;
440+
441+
seqList->updateNumDeletedItems(oldValueDeleted, itm.isDeleted());
442+
440443
} break;
441444

442445
case SequenceList::UpdateStatus::Append: {
@@ -456,6 +459,11 @@ EphemeralVBucket::updateStoredValue(const HashTable::HashBucketLock& hbl,
456459

457460
seqList->appendToList(
458461
lh, listWriteLg, *(newSv->toOrderedStoredValue()));
462+
463+
// We couldn't overwrite any existing OVS due to a range-read, we
464+
// just update with the same logic of an "add new stored value"
465+
seqList->updateNumDeletedItems(false, itm.isDeleted());
466+
459467
} break;
460468
}
461469

@@ -497,8 +505,6 @@ EphemeralVBucket::updateStoredValue(const HashTable::HashBucketLock& hbl,
497505
}
498506
}
499507

500-
seqList->updateNumDeletedItems(oldValueDeleted, itm.isDeleted());
501-
502508
return std::make_tuple(newSv, status, notifyCtx);
503509
}
504510

engines/ep/src/ephemeral_vb.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,14 @@ class EphemeralVBucket : public VBucket {
280280
return {};
281281
}
282282

283+
size_t getSeqListNumItems() const {
284+
return seqList->getNumItems();
285+
}
286+
287+
size_t getSeqListNumDeletedItems() const {
288+
return seqList->getNumDeletedItems();
289+
}
290+
283291
protected:
284292
/* Data structure for in-memory sequential storage */
285293
std::unique_ptr<SequenceList> seqList;

engines/ep/tests/module_tests/ephemeral_bucket_test.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,3 +745,81 @@ TEST_F(SingleThreadedEphemeralPurgerTest, HTCleanerSkipsPrepares) {
745745
ASSERT_FALSE(res.committed);
746746
}
747747
}
748+
749+
TEST_F(SingleThreadedEphemeralPurgerTest, MB_42568) {
750+
// Test relies on that the HTCleaner does its work when it runs
751+
ASSERT_EQ(0, engine->getConfiguration().getEphemeralMetadataPurgeAge());
752+
753+
auto& vb = dynamic_cast<EphemeralVBucket&>(*store->getVBucket(vbid));
754+
ASSERT_EQ(0, vb.getHighSeqno());
755+
ASSERT_EQ(0, vb.getSeqListNumItems());
756+
ASSERT_EQ(0, vb.getSeqListNumDeletedItems());
757+
758+
const auto runHTCleaner = [this]() -> void {
759+
auto* bucket = dynamic_cast<EphemeralBucket*>(store);
760+
bucket->enableTombstonePurgerTask();
761+
auto& queue = *task_executor->getLpTaskQ()[NONIO_TASK_IDX];
762+
bucket->attemptToFreeMemory(); // This wakes up the HTCleaner
763+
runNextTask(queue, "Eph tombstone hashtable cleaner");
764+
// Scheduled by HTCleaner
765+
runNextTask(queue, "Eph tombstone stale item deleter");
766+
};
767+
768+
// keyA - SyncDelete and Commit
769+
const auto keyA = makeStoredDocKey("keyA");
770+
const std::string value = "value";
771+
store_item(vbid,
772+
keyA,
773+
value,
774+
0 /*exptime*/,
775+
{cb::engine_errc::sync_write_pending},
776+
PROTOCOL_BINARY_RAW_BYTES,
777+
cb::durability::Requirements(),
778+
true /*deleted*/);
779+
EXPECT_EQ(1, vb.getHighSeqno());
780+
EXPECT_EQ(1, vb.getSeqListNumItems());
781+
EXPECT_EQ(1, vb.getSeqListNumDeletedItems());
782+
EXPECT_EQ(
783+
ENGINE_SUCCESS,
784+
vb.commit(keyA, 1 /*prepareSeqno*/, {}, vb.lockCollections(keyA)));
785+
EXPECT_EQ(2, vb.getHighSeqno());
786+
EXPECT_EQ(2, vb.getSeqListNumItems());
787+
EXPECT_EQ(2, vb.getSeqListNumDeletedItems());
788+
789+
// Cover seqno 1 and 2 with a range-read, then queue a SyncWrite for keyA.
790+
// Seqno 1 is PrepareCommitted and we could just move that OSV to the end of
791+
// the SeqList and update it to store the new Pending. But we cannot touch
792+
// seqno:1 because of the range-read, so we append a new OSV to the SeqList.
793+
{
794+
const auto rangeIt = vb.makeRangeIterator(true /*isBackfill*/);
795+
ASSERT_TRUE(rangeIt);
796+
797+
// keyA - SyncWrite
798+
store_item(vbid,
799+
keyA,
800+
value,
801+
0 /*exptime*/,
802+
{cb::engine_errc::sync_write_pending},
803+
PROTOCOL_BINARY_RAW_BYTES,
804+
cb::durability::Requirements(),
805+
false /*deleted*/);
806+
}
807+
EXPECT_EQ(3, vb.getHighSeqno());
808+
// Note: This would be 2 with no range-read
809+
EXPECT_EQ(3, vb.getSeqListNumItems());
810+
811+
// Core check: We have not updated the deleted at seqno:1 with the alive at
812+
// seqno:3, we have just appended seqno:3. So, num-deleted-items must not
813+
// change. Before the fix this was decremented to 1.
814+
EXPECT_EQ(2, vb.getSeqListNumDeletedItems());
815+
816+
// As a side effect, before the fix this step throws with
817+
// ThrowExceptionUnderflowPolicy, as we try to remove seqno 1 and 2 (ie,
818+
// (two deleted items) from the SeqList and we try to decrement
819+
// num-deleted-items to -1.
820+
runHTCleaner();
821+
822+
EXPECT_EQ(3, vb.getHighSeqno());
823+
EXPECT_EQ(1, vb.getSeqListNumItems());
824+
EXPECT_EQ(0, vb.getSeqListNumDeletedItems());
825+
}

0 commit comments

Comments
 (0)