Skip to content

Commit a9f305b

Browse files
jameseh96daverigby
authored andcommitted
MB-35053: Set correct allowedDuplicatePrepareSeqnos
The allowedDuplicatePrepareSeqnos set should be populated with the range of seqnos `[hcs+1, hps]`, rather than just the two seqnos `{hcs+1, hps}`. Change-Id: If7e30679d53c765af8261eb1f80319929e5caa5b Reviewed-on: http://review.couchbase.org/111972 Tested-by: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]>
1 parent 4834fe3 commit a9f305b

File tree

3 files changed

+103
-11
lines changed

3 files changed

+103
-11
lines changed

engines/ep/src/vbucket.cc

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3892,18 +3892,14 @@ void VBucket::setUpAllowedDuplicatePrepareWindow() {
38923892
auto& dm = getDurabilityMonitor();
38933893
auto hcs = dm.getHighCompletedSeqno();
38943894
auto hps = dm.getHighPreparedSeqno();
3895+
Expects(hcs <= hps);
38953896

3896-
// If allowedDuplicatePrepares is empty then we just set it to the set of
3897-
// seqnos between HCS and HPS. If it is not, we take the union of the
3898-
// existing set and the new set. We could just do insert, but in the general
3899-
// case we should only do this once and the set may be large so a move will
3900-
// be faster.
3901-
std::unordered_set<int64_t> newDuplicates{hcs + 1, hps};
3902-
if (allowedDuplicatePrepareSeqnos.empty()) {
3903-
allowedDuplicatePrepareSeqnos = std::move(newDuplicates);
3904-
} else {
3905-
allowedDuplicatePrepareSeqnos.insert(newDuplicates.begin(),
3906-
newDuplicates.end());
3897+
int64_t newDuplicateCount = hps - hcs;
3898+
allowedDuplicatePrepareSeqnos.reserve(allowedDuplicatePrepareSeqnos.size() +
3899+
newDuplicateCount);
3900+
3901+
for (int64_t dupSeqno = hcs + 1; dupSeqno <= hps; dupSeqno++) {
3902+
allowedDuplicatePrepareSeqnos.insert(dupSeqno);
39073903
}
39083904
}
39093905

engines/ep/tests/module_tests/dcp_durability_stream_test.cc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,10 +1380,99 @@ void DurabilityPassiveStreamTest::testReceiveDuplicateDcpPrepare(
13801380
opaque, vbid, prepareSeqno, commitSeqno, key)));
13811381
}
13821382

1383+
void DurabilityPassiveStreamTest::testReceiveMultipleDuplicateDcpPrepares() {
1384+
// This simulates the state in which the active has:
1385+
// PRE1 PRE2 PRE3 CMT1 CMT2 CMT3 PRE1 PRE2 PRE3 CMT1 CMT2 CMT3
1386+
// the replica sees:
1387+
// PRE1 PRE2 PRE3 ||Disconnect|| PRE1 PRE2 PRE3 CMT1 CMT2 CMT3
1388+
// All 3 duplicate prepares should be accepted by
1389+
// allowedDuplicatePrepareSeqnos
1390+
const uint64_t cas = 999;
1391+
uint64_t seqno = 1;
1392+
std::vector<StoredDocKey> keys = {makeStoredDocKey("key1"),
1393+
makeStoredDocKey("key2"),
1394+
makeStoredDocKey("key3")};
1395+
1396+
// Do first prepare for each of three keys
1397+
// PRE1 PRE2 PRE3 CMT1 CMT2 CMT3 PRE1 PRE2 PRE3 CMT1 CMT2 CMT3
1398+
// ^^^^ ^^^^ ^^^^
1399+
std::vector<queued_item> queued_items;
1400+
for (const auto& key : keys) {
1401+
queued_items.push_back(makeAndReceiveDcpPrepare(key, cas, seqno++));
1402+
}
1403+
1404+
// The consumer now "disconnects" then "re-connects" and misses the commits
1405+
// at seqnos 4, 5, 6.
1406+
// PRE1 PRE2 PRE3 CMT1 CMT2 CMT3 PRE1 PRE2 PRE3 CMT1 CMT2 CMT3
1407+
// xxxx xxxx xxxx
1408+
// It instead receives the following snapshot [7, 9] containing prepares
1409+
// (for the same 3 keys), followed by a second snapshot [10, 12] with the
1410+
// corresponding commits.
1411+
uint32_t opaque = 0;
1412+
1413+
// Fake disconnect and reconnect, importantly, this sets up the valid window
1414+
// for replacing the old prepare.
1415+
consumer->closeAllStreams();
1416+
consumer->addStream(opaque, vbid, 0 /*flags*/);
1417+
stream = static_cast<MockPassiveStream*>(
1418+
(consumer->getVbucketStream(vbid)).get());
1419+
stream->acceptStream(cb::mcbp::Status::Success, opaque);
1420+
1421+
ASSERT_TRUE(stream->isActive());
1422+
// At Replica we don't expect multiple Durability items (for the same key)
1423+
// within the same snapshot. That is because the Active prevents that for
1424+
// avoiding de-duplication.
1425+
// So, we need to simulate a Producer sending another SnapshotMarker with
1426+
// the MARKER_FLAG_CHK set before the Consumer receives the Commit. That
1427+
// will force the Consumer closing the open checkpoint (which Contains the
1428+
// Prepare) and creating a new open one for queueing the Commit.
1429+
SnapshotMarker marker(
1430+
opaque,
1431+
vbid,
1432+
7 /*snapStart*/,
1433+
9 /*snapEnd*/,
1434+
dcp_marker_flag_t::MARKER_FLAG_MEMORY | MARKER_FLAG_CHK,
1435+
{} /*streamId*/);
1436+
stream->processMarker(&marker);
1437+
1438+
// Do second prepare for each of three keys
1439+
// PRE1 PRE2 PRE3 CMT1 CMT2 CMT3 PRE1 PRE2 PRE3 CMT1 CMT2 CMT3
1440+
// ^^^^ ^^^^ ^^^^
1441+
seqno = 7;
1442+
for (const auto& key : keys) {
1443+
queued_items.push_back(makeAndReceiveDcpPrepare(key, cas, seqno++));
1444+
}
1445+
1446+
marker = SnapshotMarker(
1447+
opaque,
1448+
vbid,
1449+
10 /*snapStart*/,
1450+
12 /*snapEnd*/,
1451+
dcp_marker_flag_t::MARKER_FLAG_MEMORY | MARKER_FLAG_CHK,
1452+
{} /*streamId*/);
1453+
stream->processMarker(&marker);
1454+
1455+
// Commit each of the keys
1456+
// PRE1 PRE2 PRE3 CMT1 CMT2 CMT3 PRE1 PRE2 PRE3 CMT1 CMT2 CMT3
1457+
// ^^^^ ^^^^ ^^^^
1458+
1459+
uint64_t prepareSeqno = 7;
1460+
seqno = 10;
1461+
for (const auto& key : keys) {
1462+
ASSERT_EQ(ENGINE_SUCCESS,
1463+
stream->messageReceived(std::make_unique<CommitSyncWrite>(
1464+
opaque, vbid, prepareSeqno++, seqno++, key)));
1465+
}
1466+
}
1467+
13831468
TEST_P(DurabilityPassiveStreamTest, ReceiveDuplicateDcpPrepare) {
13841469
testReceiveDuplicateDcpPrepare(3);
13851470
}
13861471

1472+
TEST_P(DurabilityPassiveStreamTest, ReceiveMultipleDuplicateDcpPrepares) {
1473+
testReceiveMultipleDuplicateDcpPrepares();
1474+
}
1475+
13871476
TEST_P(DurabilityPassiveStreamTest, ReceiveDuplicateDcpPrepareRemoveFromSet) {
13881477
testReceiveDuplicateDcpPrepare(3);
13891478

engines/ep/tests/module_tests/dcp_durability_stream_test.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ class DurabilityPassiveStreamTest : public SingleThreadedPassiveStreamTest {
7979
*/
8080
void testReceiveDuplicateDcpPrepare(uint64_t prepareSeqno);
8181

82+
/**
83+
* Simulates a Replica receiving multiple DCP_PREPAREs followed by another
84+
* set of DCP_PREPAREs for the same keys after disconnecting and
85+
* re-connecting.
86+
*/
87+
void testReceiveMultipleDuplicateDcpPrepares();
88+
8289
/**
8390
* Simulates a Replica receiving a DCP_PREPARE followed by DCP_COMMIT and
8491
* checks they are queued correctly for persistence.

0 commit comments

Comments
 (0)