Skip to content

Commit 5a58019

Browse files
committed
Merge remote-tracking branch 'couchbase/6.5.2' into mad-hatter
* commit '0841dbcd6': MB-41300 [BP]: Reposition the HPS correctly in PDM at Prepare dedup Change-Id: Ic746769ef25b36c696e177e6d7ff2f9027d1313a
2 parents ca4695b + 0841dbc commit 5a58019

File tree

4 files changed

+149
-10
lines changed

4 files changed

+149
-10
lines changed

engines/ep/src/durability/durability_monitor_impl.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,14 @@ struct PassiveDurabilityMonitor::State {
832832
*/
833833
void checkForAndRemovePrepares();
834834

835+
/**
836+
* Erase the SyncWrite at the given iterator after fixing up the iterators
837+
* for the HCS and HPS values (if they point to the element to be erased)
838+
*
839+
* @return trackedWrites.erase(...) result
840+
*/
841+
Container::iterator safeEraseSyncWrite(Container::iterator toErase);
842+
835843
/// The container of pending Prepares.
836844
Container trackedWrites;
837845

engines/ep/src/durability/passive_durability_monitor.cc

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,8 @@ void PassiveDurabilityMonitor::addSyncWrite(
177177
}
178178
if (itr != s->trackedWrites.end()) {
179179
Expects(itr->getBySeqno() == overwritingPrepareSeqno);
180-
// We have found a trackedWrite with the same key to remove. Update
181-
// the HCS and HPS iterators and then remove the SyncWrite.
182-
if (itr == s->highCompletedSeqno.it) {
183-
s->highCompletedSeqno.it = s->trackedWrites.end();
184-
}
185-
if (itr == s->highPreparedSeqno.it) {
186-
s->highPreparedSeqno.it = s->trackedWrites.end();
187-
}
188-
189-
s->trackedWrites.erase(itr);
180+
// We have found a trackedWrite with the same key to remove.
181+
s->safeEraseSyncWrite(itr);
190182
}
191183
}
192184

@@ -635,6 +627,29 @@ void PassiveDurabilityMonitor::State::checkForAndRemovePrepares() {
635627
}
636628
}
637629

630+
PassiveDurabilityMonitor::Container::iterator
631+
PassiveDurabilityMonitor::State::safeEraseSyncWrite(
632+
Container::iterator toErase) {
633+
// Don't change HCS or HPS values, but we do need to ensure the iterators
634+
// are correct.
635+
// Find the correct iterator for setting HCS and HPS. We can't leave them
636+
// pointing to invalid elements. We always need to move the iterator back
637+
// to ensure that we never advance the HCS or HPS.
638+
Expects(toErase != trackedWrites.end());
639+
auto valid = toErase == trackedWrites.begin() ? trackedWrites.end()
640+
: std::prev(toErase);
641+
642+
if (toErase == highPreparedSeqno.it) {
643+
highPreparedSeqno.it = valid;
644+
}
645+
646+
if (toErase == highCompletedSeqno.it) {
647+
highCompletedSeqno.it = valid;
648+
}
649+
650+
return trackedWrites.erase(toErase);
651+
}
652+
638653
template <class exception>
639654
[[noreturn]] void PassiveDurabilityMonitor::throwException(
640655
const std::string& thrower, const std::string& error) const {

engines/ep/tests/module_tests/dcp_durability_stream_test.cc

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4083,6 +4083,113 @@ TEST_P(DurabilityPassiveStreamTest,
40834083
stream->processBufferedMessages(processedBytes, 1));
40844084
}
40854085

4086+
void DurabilityPassiveStreamTest::testPrepareDeduplicationCorrectlyResetsHPS(
4087+
cb::durability::Level level) {
4088+
using namespace cb::durability;
4089+
if (ephemeral()) {
4090+
ASSERT_EQ(Level::Majority, level);
4091+
}
4092+
4093+
auto keyA = makeStoredDocKey("keyA");
4094+
makeAndReceiveDcpPrepare(keyA, 1 /*cas*/, 1 /*prepareSeqno*/, level);
4095+
auto keyB = makeStoredDocKey("keyB");
4096+
makeAndReceiveDcpPrepare(keyB, 2 /*cas*/, 2 /*prepareSeqno*/, level);
4097+
flushVBucketToDiskIfPersistent(vbid, 2 /*expected_num_flushed*/);
4098+
4099+
// Expected state in PDM:
4100+
//
4101+
// P(keyA):1 P(keyB):2
4102+
// ^
4103+
// HPS:2
4104+
const auto& vb = *store->getVBucket(vbid);
4105+
const auto& pdm = dynamic_cast<const PassiveDurabilityMonitor&>(
4106+
vb.getDurabilityMonitor());
4107+
EXPECT_EQ(2, pdm.getNumTracked());
4108+
EXPECT_EQ(2, pdm.getHighestTrackedSeqno());
4109+
EXPECT_EQ(2, pdm.getHighPreparedSeqno());
4110+
4111+
// Replica receives a disk snapshot now, key duplicates legal in PDM
4112+
const uint32_t opaque = 0;
4113+
SnapshotMarker marker(opaque,
4114+
vbid,
4115+
3 /*snapStart*/,
4116+
3 /*snapEnd*/,
4117+
MARKER_FLAG_DISK | MARKER_FLAG_CHK /*flags*/,
4118+
0 /*HCS*/,
4119+
{} /*maxVisibleSeqno*/,
4120+
{} /*streamId*/);
4121+
stream->processMarker(&marker);
4122+
4123+
// Before the fix, the next steps will end up with the following invalid
4124+
// state, which is a pre-requirement for the next steps to fail:
4125+
//
4126+
// P(keyA):1 x P(keyB):3
4127+
// ^
4128+
// HPS:2
4129+
//
4130+
// At fix, this is the state:
4131+
//
4132+
// P(keyA):1 x P(keyB):3
4133+
// ^
4134+
// HPS:2
4135+
4136+
auto qi = makePendingItem(keyB, "value", Requirements(level, Timeout(60)));
4137+
qi->setBySeqno(3);
4138+
qi->setCas(3);
4139+
// Receiving the snap-end seqno. Before the fix, this is where we fail on
4140+
// Ephemeral, as we callback into the PDM for updating the HPS from
4141+
// PassiveStream::handleSnapshotEnd().
4142+
// The PDM throws as we break the monotonicity invariant on HPS (set to
4143+
// seqno:2) by trying to reset it to PDM::trackedWrites::begin (ie, seqno:1)
4144+
EXPECT_EQ(ENGINE_SUCCESS,
4145+
stream->messageReceived(std::make_unique<MutationConsumerMessage>(
4146+
qi,
4147+
opaque,
4148+
IncludeValue::Yes,
4149+
IncludeXattrs::Yes,
4150+
IncludeDeleteTime::No,
4151+
IncludeDeletedUserXattrs::Yes,
4152+
DocKeyEncodesCollectionId::No,
4153+
nullptr,
4154+
cb::mcbp::DcpStreamId{})));
4155+
4156+
if (persistent()) {
4157+
EXPECT_EQ(2, pdm.getNumTracked());
4158+
EXPECT_EQ(3, pdm.getHighestTrackedSeqno());
4159+
EXPECT_EQ(2, pdm.getHighPreparedSeqno());
4160+
4161+
// At flush we persist the full disk snapshot and we call back into the
4162+
// PDM for moving the HPS. Before the fix the PDM throws here the same
4163+
// as already described for Ephemeral.
4164+
flush_vbucket_to_disk(vbid, 1 /*expected_num_flushed*/);
4165+
}
4166+
4167+
// Final state at fix
4168+
//
4169+
// P(keyA):1 x P(keyB):3
4170+
// ^
4171+
// HPS:3
4172+
EXPECT_EQ(2, pdm.getNumTracked());
4173+
EXPECT_EQ(3, pdm.getHighestTrackedSeqno());
4174+
EXPECT_EQ(3, pdm.getHighPreparedSeqno());
4175+
}
4176+
4177+
TEST_P(DurabilityPassiveStreamTest, PrepareDedupCorrectlyResetsHPS_Majority) {
4178+
testPrepareDeduplicationCorrectlyResetsHPS(cb::durability::Level::Majority);
4179+
}
4180+
4181+
TEST_P(DurabilityPassiveStreamPersistentTest,
4182+
PrepareDedupCorrectlyResetsHPS_MajorityAndPersistOnMaster) {
4183+
testPrepareDeduplicationCorrectlyResetsHPS(
4184+
cb::durability::Level::MajorityAndPersistOnMaster);
4185+
}
4186+
4187+
TEST_P(DurabilityPassiveStreamPersistentTest,
4188+
PrepareDedupCorrectlyResetsHPS_PersistToMajority) {
4189+
testPrepareDeduplicationCorrectlyResetsHPS(
4190+
cb::durability::Level::PersistToMajority);
4191+
}
4192+
40864193
void DurabilityPromotionStreamTest::SetUp() {
40874194
// Set up as a replica
40884195
DurabilityPassiveStreamTest::SetUp();

engines/ep/tests/module_tests/dcp_durability_stream_test.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@ class DurabilityPassiveStreamTest
221221
void testPrepareCompletedAtAbort(cb::durability::Level level,
222222
Resolution res,
223223
bool flush = false);
224+
225+
/**
226+
* The test verifies that Replica repositions the HPS iterator correctly at
227+
* Prepare deduplication.
228+
*
229+
* @param level The level of the Prepares under testing
230+
*/
231+
void testPrepareDeduplicationCorrectlyResetsHPS(
232+
cb::durability::Level level);
224233
};
225234

226235
/**

0 commit comments

Comments
 (0)