Skip to content

Commit e696593

Browse files
committed
MB-34367 [SR]: Correctly propogate IO-complete status from subdoc mutation
If the bucket_store() step of a sub-doc update operation returns EWOULDBLOCK and then the subsequent notify_IO_complete has a non-success status (for example a SyncWrite which timed out and notify returns sync_write_ambiguous), the non-zero status is ignored. Change-Id: I22dc3e5ea7dadc48e5cffead3aac01466b5f236f Reviewed-on: http://review.couchbase.org/109911 Reviewed-by: Jim Walker <[email protected]> Tested-by: Build Bot <[email protected]>
1 parent 1320848 commit e696593

File tree

4 files changed

+53
-21
lines changed

4 files changed

+53
-21
lines changed

daemon/subdocument.cc

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,27 +1252,30 @@ static ENGINE_ERROR_CODE subdoc_update(SubdocCmdContext& context,
12521252
uint64_t new_cas;
12531253
mutation_descr_t mdt;
12541254
auto new_op = context.needs_new_doc ? OPERATION_ADD : OPERATION_CAS;
1255-
if (context.do_delete_doc && context.no_sys_xattrs) {
1256-
new_cas = context.in_cas;
1257-
auto docKey = connection.makeDocKey(key);
1258-
ret = bucket_remove(cookie,
1259-
docKey,
1260-
new_cas,
1261-
vbucket,
1262-
cookie.getRequest(Cookie::PacketContent::Full)
1263-
.getDurabilityRequirements(),
1264-
mdt);
1265-
} else {
1266-
ret = bucket_store(cookie,
1267-
context.out_doc.get(),
1268-
new_cas,
1269-
new_op,
1270-
cookie.getRequest(Cookie::PacketContent::Full)
1271-
.getDurabilityRequirements(),
1272-
context.do_delete_doc ? DocumentState::Deleted
1273-
: context.in_document_state);
1274-
}
1275-
ret = connection.remapErrorCode(ret);
1255+
if (ret == ENGINE_SUCCESS) {
1256+
if (context.do_delete_doc && context.no_sys_xattrs) {
1257+
new_cas = context.in_cas;
1258+
auto docKey = connection.makeDocKey(key);
1259+
ret = bucket_remove(cookie,
1260+
docKey,
1261+
new_cas,
1262+
vbucket,
1263+
cookie.getRequest(Cookie::PacketContent::Full)
1264+
.getDurabilityRequirements(),
1265+
mdt);
1266+
} else {
1267+
ret = bucket_store(cookie,
1268+
context.out_doc.get(),
1269+
new_cas,
1270+
new_op,
1271+
cookie.getRequest(Cookie::PacketContent::Full)
1272+
.getDurabilityRequirements(),
1273+
context.do_delete_doc
1274+
? DocumentState::Deleted
1275+
: context.in_document_state);
1276+
}
1277+
ret = connection.remapErrorCode(ret);
1278+
}
12761279
switch (ret) {
12771280
case ENGINE_SUCCESS:
12781281
// Record the UUID / Seqno if MUTATION_SEQNO feature is enabled so

tests/testapp/testapp.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,10 @@ void set_mutation_seqno_feature(bool enable) {
968968
set_feature(cb::mcbp::Feature::MUTATION_SEQNO, enable);
969969
}
970970

971+
void set_xerror_feature(bool enable) {
972+
set_feature(cb::mcbp::Feature::XERROR, enable);
973+
}
974+
971975
void TestappTest::waitForShutdown(bool killed) {
972976
#ifdef WIN32
973977
ASSERT_EQ(WAIT_OBJECT_0,

tests/testapp/testapp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ void set_json_feature(bool enable);
435435
// Enables / disables the MUTATION_SEQNO feature.
436436
void set_mutation_seqno_feature(bool enable);
437437

438+
// Enables / disables the XERROR feature.
439+
void set_xerror_feature(bool enable);
440+
438441
/* Send the specified buffer+len to memcached. */
439442
void safe_send(const void* buf, size_t len, bool hickup);
440443

tests/testapp/testapp_subdoc.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,6 +2097,28 @@ TEST_P(SubdocTestappTest, MB_30278_SubdocBacktickDictAdd) {
20972097
validate_json_document("doc", R"({"key`":"value","key2`":"value2"})");
20982098
}
20992099

2100+
// MB-34367: If a multi-mutation with durability requirements times out,
2101+
// ensure the status is reported correctly.
2102+
TEST_P(SubdocTestappTest, SubdocDurabilityTimeout) {
2103+
/// Need XERORR given we are using sync_write_ambiguous
2104+
set_xerror_feature(true);
2105+
2106+
// Setup ewouldblock_engine to simulate a timeout - first 3 calls should
2107+
// succeed, bucket_store should return ewouldblock and 'pending' IO return
2108+
// sync_write_ambiguous.
2109+
ewouldblock_engine_configure({cb::engine_errc(-1),
2110+
cb::engine_errc(-1),
2111+
cb::engine_errc::would_block,
2112+
cb::engine_errc::sync_write_ambiguous});
2113+
EXPECT_SD_ERR(BinprotSubdocCommand(cb::mcbp::ClientOpcode::SubdocDictUpsert,
2114+
"durability_timeout",
2115+
"element",
2116+
"4",
2117+
SUBDOC_FLAG_NONE,
2118+
mcbp::subdoc::doc_flag::Mkdoc),
2119+
cb::mcbp::Status::SyncWriteAmbiguous);
2120+
}
2121+
21002122
INSTANTIATE_TEST_CASE_P(
21012123
Subdoc,
21022124
SubdocTestappTest,

0 commit comments

Comments
 (0)