|
51 | 51 | #include "tests/module_tests/thread_gate.h" |
52 | 52 | #include "tests/test_fileops.h" |
53 | 53 | #include "vbucket_state.h" |
| 54 | +#include "vbucket_bgfetch_item.h" |
54 | 55 |
|
55 | 56 | #include "../couchstore/src/internal.h" |
56 | 57 |
|
@@ -4911,6 +4912,83 @@ TEST_P(STParamPersistentBucketTest, |
4911 | 4912 | testAbortDoesNotIncrementOpsDelete(false /*flusherDedup*/); |
4912 | 4913 | } |
4913 | 4914 |
|
| 4915 | +// @TODO move to EPBucketFullEvictionTest on merge forward to master |
| 4916 | +TEST_P(STParamPersistentBucketTest, ExpiryFindsPrepareWithSameCas) { |
| 4917 | + if (!fullEviction()) { |
| 4918 | + return; |
| 4919 | + } |
| 4920 | + setVBucketStateAndRunPersistTask( |
| 4921 | + vbid, |
| 4922 | + vbucket_state_active, |
| 4923 | + {{"topology", nlohmann::json::array({{"active", "replica"}})}}); |
| 4924 | + |
| 4925 | + // 1) Store prepare with expiry |
| 4926 | + auto key = makeStoredDocKey("a"); |
| 4927 | + using namespace cb::durability; |
| 4928 | + auto pre = makePendingItem(key, "value", Requirements{Level::Majority, {}}); |
| 4929 | + pre->setVBucketId(vbid); |
| 4930 | + pre->setExpTime(1); |
| 4931 | + |
| 4932 | + EXPECT_EQ(ENGINE_SYNC_WRITE_PENDING, store->set(*pre, cookie)); |
| 4933 | + flushVBucketToDiskIfPersistent(vbid, 1); |
| 4934 | + |
| 4935 | + auto vb = store->getVBucket(vbid); |
| 4936 | + |
| 4937 | + // 2) Seqno ack and commit the prepare |
| 4938 | + vb->seqnoAcknowledged(folly::SharedMutex::ReadHolder(vb->getStateLock()), |
| 4939 | + "replica", |
| 4940 | + 1 /*prepareSeqno*/); |
| 4941 | + vb->processResolvedSyncWrites(); |
| 4942 | + |
| 4943 | + // 3) Fudge the current snapshot so that when we warmup we scan the entire |
| 4944 | + // snapshot for prepares (i.e. incomplete disk snapshot) |
| 4945 | + vb->checkpointManager->updateCurrentSnapshot(3, 3, CheckpointType::Disk); |
| 4946 | + flushVBucketToDiskIfPersistent(vbid, 1); |
| 4947 | + |
| 4948 | + // 4) Restart and warmup |
| 4949 | + vb.reset(); |
| 4950 | + resetEngineAndWarmup(); |
| 4951 | + vb = store->getVBucket(vbid); |
| 4952 | + |
| 4953 | + { |
| 4954 | + // Verify that the prepare is there and it's "MaybeVisible" |
| 4955 | + auto ret = vb->ht.findForUpdate(key); |
| 4956 | + ASSERT_TRUE(ret.pending); |
| 4957 | + ASSERT_TRUE(ret.pending->isPreparedMaybeVisible()); |
| 4958 | + |
| 4959 | + // And that the commit is there too |
| 4960 | + ASSERT_TRUE(ret.committed); |
| 4961 | + } |
| 4962 | + |
| 4963 | + // 5) Grab the item from disk just like the compactor would |
| 4964 | + vb_bgfetch_queue_t q; |
| 4965 | + vb_bgfetch_item_ctx_t ctx; |
| 4966 | + ctx.isMetaOnly = GetMetaOnly::No; |
| 4967 | + auto diskDocKey = makeDiskDocKey("a"); |
| 4968 | + q[diskDocKey] = std::move(ctx); |
| 4969 | + store->getRWUnderlying(vbid)->getMulti(vbid, q); |
| 4970 | + EXPECT_EQ(ENGINE_SUCCESS, q[diskDocKey].value.getStatus()); |
| 4971 | + |
| 4972 | + // 6) Callback from the "compactor" with the item to try and expire it. We |
| 4973 | + // could also pretend to be the pager here. |
| 4974 | + ASSERT_EQ(0, vb->numExpiredItems); |
| 4975 | + vb->deleteExpiredItem(*q[diskDocKey].value.item, 2, ExpireBy::Compactor); |
| 4976 | + |
| 4977 | + // Item expiry cannot take place if the MaybeVisible prepare exists. |
| 4978 | + EXPECT_EQ(0, vb->numExpiredItems); |
| 4979 | + { |
| 4980 | + // Verify that the prepare is there and it's "MaybeVisible". Before the |
| 4981 | + // fix deleteExpiredItem would select and replace the prepare which is |
| 4982 | + // incorrect and causes us to have two committed items in the HashTable. |
| 4983 | + auto ret = vb->ht.findForUpdate(key); |
| 4984 | + ASSERT_TRUE(ret.pending); |
| 4985 | + ASSERT_TRUE(ret.pending->isPreparedMaybeVisible()); |
| 4986 | + |
| 4987 | + // And that the commit is there too |
| 4988 | + ASSERT_TRUE(ret.committed); |
| 4989 | + } |
| 4990 | +} |
| 4991 | + |
4914 | 4992 | INSTANTIATE_TEST_CASE_P(Persistent, |
4915 | 4993 | STParamPersistentBucketTest, |
4916 | 4994 | STParameterizedBucketTest::persistentConfigValues(), |
|
0 commit comments