Skip to content

Commit 9203fba

Browse files
BenHuddlestondaverigby
authored andcommitted
MB-34412: Hoist isPending check to VBucket::process* methods
We need to be able to overwrite a prepare unconditionally on a replica so pull these functions up to processSet/processDelete so that we do not have to add new HashTable functions. Change-Id: If36a92f8ef2d13d251df7895d3280cf484b72244 Reviewed-on: http://review.couchbase.org/110145 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent 2c7452e commit 9203fba

File tree

5 files changed

+69
-47
lines changed

5 files changed

+69
-47
lines changed

engines/ep/src/ephemeral_vb.cc

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -392,16 +392,6 @@ EphemeralVBucket::updateStoredValue(const HashTable::HashBucketLock& hbl,
392392
const Item& itm,
393393
const VBQueueItemCtx& queueItmCtx,
394394
bool justTouch) {
395-
// Don't make any update if we know that this is a pending sync write that
396-
// has not yet been completed. We need to return early before we make any
397-
// modification to the seqList. This is typically handled in the EP case by
398-
// the HashTable functions, but for ephemeral we update the seqList first so
399-
// we need an earlier check.
400-
if (v.isPending()) {
401-
return std::make_tuple(
402-
nullptr, MutationStatus::IsPendingSyncWrite, VBNotifyCtx{});
403-
}
404-
405395
VBNotifyCtx notifyCtx;
406396
StoredValue* newSv = nullptr;
407397
MutationStatus status(MutationStatus::WasClean);

engines/ep/src/hash_table.cc

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,6 @@ HashTable::UpdateResult HashTable::unlocked_updateStoredValue(
346346
"call on a non-active HT object");
347347
}
348348

349-
if (v.isPending()) {
350-
return {MutationStatus::IsPendingSyncWrite, nullptr};
351-
}
352-
353349
// Logically /can/ update a non-Pending StoredValue with a Pending Item;
354350
// however internally this is implemented as a separate (new)
355351
// StoredValue object for the Pending item. We can replace a completed
@@ -548,16 +544,14 @@ HashTable::DeleteResult HashTable::unlocked_softDelete(
548544
bool onlyMarkDeleted,
549545
DeleteSource delSource) {
550546
switch (v.getCommitted()) {
551-
case CommittedState::Pending:
552-
case CommittedState::PreparedMaybeVisible:
553-
// Cannot update a SV if it's a Pending item.
554-
return {DeletionStatus::IsPendingSyncWrite, nullptr};
555547
case CommittedState::PrepareAborted:
556548
case CommittedState::PrepareCommitted:
557549
// We shouldn't be trying to use PrepareCompleted states yet
558550
throw std::logic_error(
559551
"HashTable::unlocked_softDelete attempting"
560552
" to delete a completed prepare");
553+
case CommittedState::Pending:
554+
case CommittedState::PreparedMaybeVisible:
561555
case CommittedState::CommittedViaMutation:
562556
case CommittedState::CommittedViaPrepare:
563557
const auto preProps = valueStats.prologue(&v);

engines/ep/src/vbucket.cc

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2956,6 +2956,10 @@ std::pair<MutationStatus, boost::optional<VBNotifyCtx>> VBucket::processSet(
29562956
getId().to_string());
29572957
}
29582958

2959+
if (v && v->isPending()) {
2960+
return {MutationStatus::IsPendingSyncWrite, {}};
2961+
}
2962+
29592963
if (!hasMemoryForStoredValue(stats, itm)) {
29602964
return {MutationStatus::NoMem, {}};
29612965
}
@@ -3140,6 +3144,10 @@ VBucket::processSoftDelete(const HashTable::HashBucketLock& hbl,
31403144
uint64_t bySeqno,
31413145
DeleteSource deleteSource) {
31423146
boost::optional<VBNotifyCtx> empty;
3147+
if (v.isPending()) {
3148+
return {MutationStatus::IsPendingSyncWrite, &v, empty};
3149+
}
3150+
31433151
if (v.isTempInitialItem() && eviction == EvictionPolicy::Full) {
31443152
return std::make_tuple(MutationStatus::NeedBgFetch, &v, empty);
31453153
}
@@ -3174,11 +3182,6 @@ VBucket::processSoftDelete(const HashTable::HashBucketLock& hbl,
31743182

31753183
// SyncDeletes are special cases. We actually want to add a new prepare.
31763184
if (queueItmCtx.durability) {
3177-
if (v.isPending()) {
3178-
return std::make_tuple(
3179-
MutationStatus::IsPendingSyncWrite, &v, empty);
3180-
}
3181-
31823185
auto requirements = boost::get<cb::durability::Requirements>(
31833186
queueItmCtx.durability->requirementsOrPreparedSeqno);
31843187

engines/ep/tests/module_tests/hash_table_perspective_test.cc

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -126,28 +126,6 @@ TEST_P(HashTablePerspectiveTest, CorrectItemForEachPersisective) {
126126
del(ht, key);
127127
}
128128

129-
// Test that the normal set() method cannot be used to change a pending item
130-
// to committed - commit() must be used.
131-
TEST_P(HashTablePerspectiveTest, DenyReplacePendingWithCommitted) {
132-
auto pending = makePendingItem(key, "pending"s);
133-
ASSERT_EQ(MutationStatus::WasClean, ht.set(*pending));
134-
135-
// Attempt setting the item again with a committed value.
136-
auto committed = makeCommittedItem(key, "committed"s);
137-
ASSERT_EQ(MutationStatus::IsPendingSyncWrite, ht.set(*committed));
138-
}
139-
140-
// Test that the normal set() method cannot be used to change a pending item
141-
// to another pending - commit() must be used.
142-
TEST_P(HashTablePerspectiveTest, DenyReplacePendingWithPending) {
143-
auto pending = makePendingItem(key, "pending"s);
144-
ASSERT_EQ(MutationStatus::WasClean, ht.set(*pending));
145-
146-
// Attempt setting the item again with a committed value.
147-
auto pending2 = makePendingItem(key, "pending2"s);
148-
ASSERT_EQ(MutationStatus::IsPendingSyncWrite, ht.set(*pending2));
149-
}
150-
151129
// Check that if a pending SyncWrite is added _before_ a Committed one (to the
152130
// same key), then findforWrite finds the pending one.
153131
// (While normally pending is added _after_ the existing Committed; during

engines/ep/tests/module_tests/vbucket_durability_test.cc

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1434,6 +1434,40 @@ void VBucketDurabilityTest::setupPendingDelete(StoredDocKey key) {
14341434
EXPECT_EQ(2, ht->getNumItems());
14351435
}
14361436

1437+
// Test that we cannot do a normal set on top of a pending SyncWrite
1438+
TEST_P(VBucketDurabilityTest, DenyReplacePendingWithCommitted) {
1439+
auto key = makeStoredDocKey("key");
1440+
auto pending = makePendingItem(key, "pending"s);
1441+
VBQueueItemCtx ctx;
1442+
ctx.durability = DurabilityItemCtx{
1443+
cb::durability::Requirements{cb::durability::Level::Majority, {}},
1444+
cookie};
1445+
ASSERT_EQ(MutationStatus::WasClean,
1446+
public_processSet(*pending, 0 /*cas*/, ctx));
1447+
1448+
// Attempt setting the item again with a committed value.
1449+
auto committed = makeCommittedItem(key, "committed"s);
1450+
ASSERT_EQ(MutationStatus::IsPendingSyncWrite,
1451+
public_processSet(*committed, 0 /*cas*/, ctx));
1452+
}
1453+
1454+
// Test that we cannot do a pending SyncWrite on top of a pending SyncWrite
1455+
TEST_P(VBucketDurabilityTest, DenyReplacePendingWithPending) {
1456+
auto key = makeStoredDocKey("key");
1457+
auto pending = makePendingItem(key, "pending"s);
1458+
VBQueueItemCtx ctx;
1459+
ctx.durability = DurabilityItemCtx{
1460+
cb::durability::Requirements{cb::durability::Level::Majority, {}},
1461+
cookie};
1462+
ASSERT_EQ(MutationStatus::WasClean,
1463+
public_processSet(*pending, 0 /*cas*/, ctx));
1464+
1465+
// Attempt setting the item again with a committed value.
1466+
auto pending2 = makePendingItem(key, "pending2"s);
1467+
ASSERT_EQ(MutationStatus::IsPendingSyncWrite,
1468+
public_processSet(*pending2, 0 /*cas*/, ctx));
1469+
}
1470+
14371471
// Positive test - check that an item can have a pending delete added
14381472
// (SyncDelete).
14391473
TEST_P(VBucketDurabilityTest, SyncDeletePending) {
@@ -1444,14 +1478,37 @@ TEST_P(VBucketDurabilityTest, SyncDeletePending) {
14441478

14451479
// Negative test - check that if a key has a pending SyncDelete it cannot
14461480
// otherwise be modified.
1447-
TEST_P(VBucketDurabilityTest, PendingSyncWriteToPendingDeleteFails) {
1481+
TEST_P(VBucketDurabilityTest, PendingSyncDeleteToPendingWriteFails) {
14481482
auto key = makeStoredDocKey("key");
14491483
setupPendingDelete(key);
14501484

14511485
// Test - attempt to mutate a key which has a pending SyncDelete against it
14521486
// with a pending SyncWrite.
14531487
auto pending = makePendingItem(key, "pending"s);
1454-
ASSERT_EQ(MutationStatus::IsPendingSyncWrite, ht->set(*pending));
1488+
VBQueueItemCtx ctx;
1489+
ctx.genBySeqno = GenerateBySeqno::No;
1490+
ctx.durability = DurabilityItemCtx{pending->getDurabilityReqs(), cookie};
1491+
ASSERT_EQ(MutationStatus::IsPendingSyncWrite,
1492+
public_processSet(*pending, 0 /*cas*/, ctx));
1493+
}
1494+
1495+
// Negative test - check that if a key has a pending SyncWrite it cannot
1496+
// be SyncDeleted
1497+
TEST_P(VBucketDurabilityTest, PendingSyncWriteToPendingDeleteFails) {
1498+
auto key = makeStoredDocKey("key");
1499+
1500+
// Test - attempt to mutate a key which has a pending SyncWrite against it
1501+
// with a pending SyncDelete.
1502+
auto pending = makePendingItem(key, "pending"s);
1503+
VBQueueItemCtx ctx;
1504+
ctx.durability = DurabilityItemCtx{
1505+
cb::durability::Requirements{cb::durability::Level::Majority, {}},
1506+
cookie};
1507+
ASSERT_EQ(MutationStatus::WasClean,
1508+
public_processSet(*pending, 0 /*cas*/, ctx));
1509+
1510+
ASSERT_EQ(MutationStatus::IsPendingSyncWrite,
1511+
public_processSoftDelete(key, ctx).first);
14551512
}
14561513

14571514
// Negative test - check that if a key has a pending SyncDelete it cannot

0 commit comments

Comments
 (0)