@@ -1643,6 +1643,79 @@ TEST_P(STEphemeralAutoDeleteItemPagerTest, MB_59368) {
16431643 EXPECT_TRUE (softDeleteCalled) << " Nothing was deleted" ;
16441644}
16451645
1646+ // Test covers MB-60046. A pending and committed key must not find itself auto
1647+ // deleted, else the delete of the commit will be rejected by the DCP replica
1648+ // and cause disconnects.
1649+ TEST_P (STEphemeralAutoDeleteItemPagerTest, MB_60046) {
1650+ setVBucketState (
1651+ vbid,
1652+ vbucket_state_active,
1653+ {{" topology" , nlohmann::json::array ({{" active" , " replica" }})}});
1654+ auto setActive = [this ](int count) {
1655+ auto item =
1656+ make_item (vbid,
1657+ makeStoredDocKey (" active_" + std::to_string (count)),
1658+ std::string (512 , ' V' ));
1659+ item.setFreqCounterValue (0 );
1660+ storeItem (item);
1661+ };
1662+ int count = 0 ;
1663+ do {
1664+ setActive (++count);
1665+ } while (store->getPageableMemCurrent () <
1666+ store->getPageableMemHighWatermark ());
1667+ auto key = makeStoredDocKey (" active_" + std::to_string (count));
1668+ // Now make the last key written in the loop also pending.
1669+ ASSERT_EQ (cb::engine_errc::would_block,
1670+ storeItem (*makePendingItem (key, " pending-value" )));
1671+ std::vector<queued_item> items;
1672+ auto & vb = *store->getVBucket (vbid);
1673+ auto cursor = vb.checkpointManager ->registerCursorBySeqno (
1674+ " test" , 1 , CheckpointCursor::Droppable::No);
1675+ ASSERT_FALSE (cursor.tryBackfill ) << *vb.checkpointManager ;
1676+ vb.checkpointManager ->getNextItemsForCursor (cursor.cursor .lock ().get (),
1677+ items);
1678+ EXPECT_NE (0 , items.size ()) << *vb.checkpointManager ;
1679+ bool pendingFound{false };
1680+ bool committedFound{false };
1681+ for (const auto & item : items) {
1682+ if (item->getKey () == key) {
1683+ if (item->isPending ()) {
1684+ ASSERT_FALSE (pendingFound);
1685+ pendingFound = true ;
1686+ } else {
1687+ ASSERT_FALSE (committedFound);
1688+ committedFound = true ;
1689+ }
1690+ }
1691+ }
1692+ // Both pending and committed must be seen before we trigger auto-delete
1693+ ASSERT_TRUE (pendingFound);
1694+ ASSERT_TRUE (committedFound);
1695+ items.clear ();
1696+ // Now trigger the auto-delete paging
1697+ runHighMemoryPager ();
1698+ // Iterate through the checkpoint and check all deletes.
1699+ vb.checkpointManager ->getNextItemsForCursor (cursor.cursor .lock ().get (),
1700+ items);
1701+ // Should be some new mutations (deletes)
1702+ EXPECT_NE (0 , items.size ()) << *vb.checkpointManager ;
1703+ int deleteCount{0 };
1704+ for (const auto & item : items) {
1705+ if (item->getKey () == key) {
1706+ // The key should not be found in the new batch of items. The
1707+ // original commit+pending were in the first batch and not expected
1708+ // to see again. Prior to the fix, a delete(key) was seen here.
1709+ FAIL () << *item;
1710+ }
1711+ if (item->isDeleted ()) {
1712+ ++deleteCount;
1713+ }
1714+ }
1715+ // We certainly should see deletes.
1716+ ASSERT_NE (0 , deleteCount);
1717+ }
1718+
16461719/* *
16471720 * Test fixture for expiry pager tests - enables the Expiry Pager (in addition
16481721 * to what the parent class does).
0 commit comments