Skip to content

Commit 829f660

Browse files
jimwwalkerowend74
authored andcommitted
[BP] MB-41255: Don't bgfetch for a replica delete of an xattr
MB-36087 identified and fixed an issue with "del_with_meta" where a delete against an evicted xattr crashed because we skipped doing a bgfetch, KV crashed because the deleteWithMeta function went on to try and prune the xattrs of the existing key, but had a null value. An unexpected outcome of this fix is that DCP can now be exposed to "would block" errors, this is because "del_with_meta" and DCP delete share the same function. MB-41255 demonstrates what happens when the consumer is told "would block" for a delete. The DCP consumer waits for the bgfetch and then retries the delete, but the consumer state machine was already moved along by the initial delete, hence we see 'out of order' seqno errors and the delete failing. Note for backport: unit-test is rewritten as the original was using test harness code that is not part of the alice branch Change-Id: I6d9c52a0fca0ce7ab0cb201025c8f6057b681481 Reviewed-on: http://review.couchbase.org/c/kv_engine/+/136003 Well-Formed: Build Bot <[email protected]> Tested-by: Build Bot <[email protected]> Reviewed-by: Daniel Owen <[email protected]>
1 parent c4ede32 commit 829f660

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

engines/ep/src/vbucket.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1480,7 +1480,8 @@ ENGINE_ERROR_CODE VBucket::deleteWithMeta(
14801480
} else {
14811481
delrv = MutationStatus::NotFound;
14821482
}
1483-
} else if (mcbp::datatype::is_xattr(v->getDatatype()) && !v->isResident()) {
1483+
} else if (!isReplication &&
1484+
mcbp::datatype::is_xattr(v->getDatatype()) && !v->isResident()) {
14841485
// MB-25671: A temp deleted xattr with no value must be fetched before
14851486
// the deleteWithMeta can be applied.
14861487
delrv = MutationStatus::NeedBgFetch;

engines/ep/tests/module_tests/dcp_test.cc

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4380,6 +4380,82 @@ TEST_P(SingleThreadedActiveStreamTest, CompleteBackfillRaceNoStreamEnd) {
43804380
EXPECT_TRUE(producer->getReadyQueue().empty());
43814381
}
43824382

4383+
// Version of MB_41255 from back-ported fix, original test was added to
4384+
// dcp_reflection_test.cc but used a significantly different version of that
4385+
// test code.
4386+
// Test ensures that a DCP deletion of an evicted mutation with xattr value
4387+
// does not error (ewouldblock seen in MB-41255)
4388+
TEST_F(SingleThreadedEPBucketTest, MB_41255) {
4389+
// Make vbucket replica so can add passive stream
4390+
setVBucketStateAndRunPersistTask(vbid, vbucket_state_replica);
4391+
4392+
const void* cookie = create_mock_cookie();
4393+
auto consumer =
4394+
std::make_shared<MockDcpConsumer>(*engine, cookie, "test_consumer");
4395+
4396+
// Add passive stream
4397+
ASSERT_EQ(ENGINE_SUCCESS,
4398+
consumer->addStream(/*opaque*/ 0,
4399+
vbid,
4400+
/*flags*/ 0));
4401+
4402+
consumer->snapshotMarker(/*opaque*/ 1,
4403+
/*vbucket*/ vbid,
4404+
/*start_seqno*/ 1,
4405+
/*end_seqno*/ 1,
4406+
/*flags set to MARKER_FLAG_MEMORY*/ 0x5);
4407+
4408+
// Store value with an xattr
4409+
StoredDocKey key{"k1", DocNamespace::DefaultCollection};
4410+
auto data = createXattrValue(R"({"json":"yes"})");
4411+
cb::const_byte_buffer value{reinterpret_cast<const uint8_t*>(data.data()),
4412+
data.size()};
4413+
consumer->mutation(1, // opaque
4414+
key,
4415+
value,
4416+
0, // priv bytes
4417+
PROTOCOL_BINARY_DATATYPE_XATTR,
4418+
1, // cas
4419+
vbid, // vbucket
4420+
0, // flags
4421+
1, // by_seqno
4422+
0, // rev seqno
4423+
0, // expiration
4424+
0, // lock time
4425+
{}, // meta
4426+
0); // nru
4427+
4428+
// flush and evict
4429+
flushVBucketToDiskIfPersistent(vbid, 1);
4430+
auto replicaVB = engine->getKVBucket()->getVBucket(vbid);
4431+
4432+
const char* msg;
4433+
EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS,
4434+
4435+
replicaVB->evictKey(key, &msg));
4436+
4437+
// now delete the key
4438+
consumer->snapshotMarker(/*opaque*/ 1,
4439+
/*vbucket*/ vbid,
4440+
/*start_seqno*/ 2,
4441+
/*end_seqno*/ 2,
4442+
/*flags*/ 5);
4443+
EXPECT_EQ(ENGINE_SUCCESS,
4444+
consumer->deletion(/*opaque*/ 1,
4445+
/*key*/ key,
4446+
/*value*/ {},
4447+
/*priv_bytes*/ 0,
4448+
/*datatype*/ PROTOCOL_BINARY_RAW_BYTES,
4449+
/*cas*/ 1,
4450+
/*vbucket*/ vbid,
4451+
/*bySeqno*/ 2,
4452+
/*revSeqno*/ 0,
4453+
/*meta*/ {}));
4454+
// Close stream
4455+
ASSERT_EQ(ENGINE_SUCCESS, consumer->closeStream(/*opaque*/ 0, vbid));
4456+
destroy_mock_cookie(cookie);
4457+
}
4458+
43834459
INSTANTIATE_TEST_CASE_P(AllBucketTypes,
43844460
SingleThreadedActiveStreamTest,
43854461
allConfigValues, );

0 commit comments

Comments
 (0)