Skip to content

Commit 7b323f8

Browse files
committed
Merge branch '6.5.2' into mad-hatter
* 6.5.2: [BP] MB-42610: Do not expire prepares when committed items exist Change-Id: Ie4c8f88a34534a38ed4506c784b24888a613e795
2 parents 5a58019 + 43c3197 commit 7b323f8

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

engines/ep/src/vbucket.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2395,6 +2395,16 @@ void VBucket::deleteExpiredItem(const Item& it,
23952395
return;
23962396
}
23972397

2398+
if (v->isPending()) {
2399+
// If cas is the same (above statement) and the HashTable has
2400+
// returned a prepare then we must have loaded a logically complete
2401+
// prepare (as we remove them from the HashTable at completion) for
2402+
// some reason. The prepare should be in a maybe visible state but
2403+
// it probably isn't a good idea to assert that here. In this case
2404+
// we must do nothing as we MUST commit any maybe visible prepares.
2405+
return;
2406+
}
2407+
23982408
if (v->isTempNonExistentItem() || v->isTempDeletedItem()) {
23992409
bool deleted = deleteStoredValue(hbl, *v);
24002410
if (!deleted) {

engines/ep/tests/module_tests/evp_store_single_threaded_test.cc

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "tests/module_tests/test_task.h"
5151
#include "tests/module_tests/thread_gate.h"
5252
#include "tests/test_fileops.h"
53+
#include "vbucket_bgfetch_item.h"
5354
#include "vbucket_state.h"
5455

5556
#include "../couchstore/src/internal.h"
@@ -5359,6 +5360,83 @@ TEST_P(STParamPersistentBucketTest, FlusherMarksCleanBySeqno) {
53595360
EXPECT_EQ(2, it->getBySeqno());
53605361
}
53615362

5363+
// @TODO move to EPBucketFullEvictionTest on merge forward to master
5364+
TEST_P(STParamPersistentBucketTest, ExpiryFindsPrepareWithSameCas) {
5365+
if (!fullEviction()) {
5366+
return;
5367+
}
5368+
setVBucketStateAndRunPersistTask(
5369+
vbid,
5370+
vbucket_state_active,
5371+
{{"topology", nlohmann::json::array({{"active", "replica"}})}});
5372+
5373+
// 1) Store prepare with expiry
5374+
auto key = makeStoredDocKey("a");
5375+
using namespace cb::durability;
5376+
auto pre = makePendingItem(key, "value", Requirements{Level::Majority, {}});
5377+
pre->setVBucketId(vbid);
5378+
pre->setExpTime(1);
5379+
5380+
EXPECT_EQ(ENGINE_SYNC_WRITE_PENDING, store->set(*pre, cookie));
5381+
flushVBucketToDiskIfPersistent(vbid, 1);
5382+
5383+
auto vb = store->getVBucket(vbid);
5384+
5385+
// 2) Seqno ack and commit the prepare
5386+
vb->seqnoAcknowledged(folly::SharedMutex::ReadHolder(vb->getStateLock()),
5387+
"replica",
5388+
1 /*prepareSeqno*/);
5389+
vb->processResolvedSyncWrites();
5390+
5391+
// 3) Fudge the current snapshot so that when we warmup we scan the entire
5392+
// snapshot for prepares (i.e. incomplete disk snapshot)
5393+
vb->checkpointManager->updateCurrentSnapshot(3, 3, CheckpointType::Disk);
5394+
flushVBucketToDiskIfPersistent(vbid, 1);
5395+
5396+
// 4) Restart and warmup
5397+
vb.reset();
5398+
resetEngineAndWarmup();
5399+
vb = store->getVBucket(vbid);
5400+
5401+
{
5402+
// Verify that the prepare is there and it's "MaybeVisible"
5403+
auto ret = vb->ht.findForUpdate(key);
5404+
ASSERT_TRUE(ret.pending);
5405+
ASSERT_TRUE(ret.pending->isPreparedMaybeVisible());
5406+
5407+
// And that the commit is there too
5408+
ASSERT_TRUE(ret.committed);
5409+
}
5410+
5411+
// 5) Grab the item from disk just like the compactor would
5412+
vb_bgfetch_queue_t q;
5413+
vb_bgfetch_item_ctx_t ctx;
5414+
ctx.isMetaOnly = GetMetaOnly::No;
5415+
auto diskDocKey = makeDiskDocKey("a");
5416+
q[diskDocKey] = std::move(ctx);
5417+
store->getRWUnderlying(vbid)->getMulti(vbid, q);
5418+
EXPECT_EQ(ENGINE_SUCCESS, q[diskDocKey].value.getStatus());
5419+
5420+
// 6) Callback from the "compactor" with the item to try and expire it. We
5421+
// could also pretend to be the pager here.
5422+
ASSERT_EQ(0, vb->numExpiredItems);
5423+
vb->deleteExpiredItem(*q[diskDocKey].value.item, 2, ExpireBy::Compactor);
5424+
5425+
// Item expiry cannot take place if the MaybeVisible prepare exists.
5426+
EXPECT_EQ(0, vb->numExpiredItems);
5427+
{
5428+
// Verify that the prepare is there and it's "MaybeVisible". Before the
5429+
// fix deleteExpiredItem would select and replace the prepare which is
5430+
// incorrect and causes us to have two committed items in the HashTable.
5431+
auto ret = vb->ht.findForUpdate(key);
5432+
ASSERT_TRUE(ret.pending);
5433+
ASSERT_TRUE(ret.pending->isPreparedMaybeVisible());
5434+
5435+
// And that the commit is there too
5436+
ASSERT_TRUE(ret.committed);
5437+
}
5438+
}
5439+
53625440
INSTANTIATE_TEST_CASE_P(Persistent,
53635441
STParamPersistentBucketTest,
53645442
STParameterizedBucketTest::persistentConfigValues(),

0 commit comments

Comments
 (0)