Skip to content

Commit 9917007

Browse files
daverigbytrondn
authored andcommitted
MB-34393: Subdoc: Correctly handle NOT_STORED race when adding doc
During a subdoc mutation, if the user specified doc_flag::Add, and the key didn't exist at the start of the command processing, but did exist at the point we finally store the new value (i.e. another connection added the key just before us), then the engine update fails with ENGINE_NOT_STORED (as expected). However, this status isn't correctly handled by the subdocument state machine, resulting in an exception being thrown and the user connection being terminated: 2019-05-30T16:14:39.675236+01:00 WARNING 47: exception occurred in runloop during packet execution. <cut> conn_execute: Should leave conn_execute for !EWOULDBLOCK Issue is in checking the status code from bucket_store in subdoc_update() - if NOT_STORED and we _don't_ remap to KEY_EEXISTS (for a retry), return a response to the client. Change-Id: I14e93a2397e0f955b4de1cf99cf62a3d7d905cb9 Reviewed-on: http://review.couchbase.org/109909 Tested-by: Build Bot <[email protected]> Reviewed-by: Trond Norbye <[email protected]>
1 parent 9eba8d0 commit 9917007

File tree

2 files changed

+9
-6
lines changed

2 files changed

+9
-6
lines changed

daemon/subdocument.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,9 @@ static ENGINE_ERROR_CODE subdoc_update(SubdocCmdContext& context,
13131313
if (new_op == OPERATION_ADD &&
13141314
context.mutationSemantics == MutationSemantics::Set) {
13151315
ret = ENGINE_KEY_EEXISTS;
1316+
} else {
1317+
// Otherwise ENGINE_NOT_STORED is terminal - return to client.
1318+
cookie.sendResponse(cb::engine_errc(ret));
13161319
}
13171320
break;
13181321

tests/testapp/testapp_subdoc.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -647,14 +647,14 @@ TEST_P(SubdocTestappTest, SubdocDictUpsert_CasCompressed) {
647647
cb::mcbp::ClientOpcode::SubdocDictUpsert);
648648
}
649649

650+
// Check that if an item is set by another client after the fetch but
651+
// before the store then we return NotStored.
652+
// (MB-34393)
650653
TEST_P(SubdocTestappTest, SubdocAddFlag_BucketStoreCas) {
651-
// Check that if an item is set by another client after the fetch but
652-
// before the store then we return EEXISTS.
653-
654654
// Setup ewouldblock_engine - first two calls succeed, 3rd (engine->store)
655-
// fails. Any further calls should return EEXISTS.
655+
// fails with NOT_STORED (key already exists).
656656
ewouldblock_engine_configure(
657-
ENGINE_KEY_EEXISTS,
657+
ENGINE_NOT_STORED,
658658
EWBEngineMode::Sequence,
659659
0xfffffffc /* <3 MSBytes all-ones>, 0b11,111,100 */);
660660
EXPECT_SD_ERR(BinprotSubdocCommand(cb::mcbp::ClientOpcode::SubdocDictUpsert,
@@ -664,7 +664,7 @@ TEST_P(SubdocTestappTest, SubdocAddFlag_BucketStoreCas) {
664664
SUBDOC_FLAG_NONE,
665665
mcbp::subdoc::doc_flag::Add,
666666
0),
667-
cb::mcbp::Status::KeyEexists);
667+
cb::mcbp::Status::NotStored);
668668
}
669669

670670
void SubdocTestappTest::test_subdoc_dict_add_upsert_deep(

0 commit comments

Comments
 (0)