Skip to content

Commit 86549bc

Browse files
authored
CXXCBC-683: Txn replace - Use CAS from given TransactionsGetResult when document is a staged insert (#763)
1 parent 3b4c159 commit 86549bc

File tree

3 files changed

+73
-1
lines changed

3 files changed

+73
-1
lines changed

core/transactions/attempt_context_impl.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ attempt_context_impl::replace(const transaction_get_result& document,
670670
self, "found existing INSERT of {} while replacing", document);
671671
self->create_staged_insert(document.id(),
672672
std::move(content),
673-
existing_sm->cas().value(),
673+
document.cas().value(),
674674
exp_delay(std::chrono::milliseconds(5),
675675
std::chrono::milliseconds(300),
676676
self->overall()->config().timeout),

test/test_transaction_public_blocking_api.cxx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,3 +765,70 @@ TEST_CASE("transactions public blocking API: can remove doc in bucket not yet op
765765
CHECK(get_err.ec() == couchbase::errc::key_value::document_not_found);
766766
});
767767
}
768+
769+
TEST_CASE("transactions public blocking API: insert then replace with illegal document "
770+
"modification in-between",
771+
"[transactions]")
772+
{
773+
test::utils::integration_test_guard integration;
774+
775+
if (!integration.cluster_version().supports_replace_body_with_xattr()) {
776+
// If replace_body_with_xattr is not supported, we have the staged insert's content in memory,
777+
// so the transactional get will not fetch the document from the server, which would give the
778+
// up-to-date CAS.
779+
SKIP("the server does not support replace_body_with_xattr");
780+
}
781+
782+
auto doc_id = test::utils::uniq_id("txn");
783+
auto txn_content_initial = tao::json::value{ { "num", 12 } };
784+
auto txn_content_updated = tao::json::value{ { "num", 20 } };
785+
auto illegal_content = tao::json::value{ { "illegal", "content" } };
786+
787+
auto cluster = integration.public_cluster();
788+
auto collection = cluster.bucket(integration.ctx.bucket).default_collection();
789+
790+
auto [tx_err, result] = cluster.transactions()->run(
791+
[&doc_id, &collection, &txn_content_initial, &txn_content_updated, &illegal_content](
792+
std::shared_ptr<couchbase::transactions::attempt_context> ctx) -> couchbase::error {
793+
// Stage an insert
794+
{
795+
auto [err, res] = ctx->insert(collection, doc_id, txn_content_initial);
796+
if (err) {
797+
return err;
798+
}
799+
REQUIRE(res.content_as<tao::json::value>() == txn_content_initial);
800+
}
801+
802+
// Do an illegal non-transactional insert that will override any staged content and txn
803+
// metadata
804+
{
805+
auto [err, res] = collection.insert(doc_id, illegal_content).get();
806+
REQUIRE_SUCCESS(err.ec());
807+
}
808+
809+
{
810+
// Now that we implement ExtReplaceBodyWithXattr, this will fetch the document from the
811+
// server (post-illegal mutation) as the staged content of the staged mutation is not stored
812+
// in memory.
813+
auto [get_err, get_res] = ctx->get(collection, doc_id);
814+
if (get_err) {
815+
return get_err;
816+
}
817+
REQUIRE(get_res.content_as<tao::json::value>() == illegal_content);
818+
819+
// This replace will use the CAS from the transaction_get_result, which should be the one
820+
// after the illegal insert. This means the operation will succeed, and will result in a
821+
// staged insert with the CAS from the transaction_get_result.
822+
// When committing, the replace_body_with_xattr op, and the transaction, will succeed.
823+
auto [replace_err, replace_res] = ctx->replace(get_res, txn_content_updated);
824+
if (replace_err) {
825+
return replace_err;
826+
}
827+
REQUIRE(replace_res.content_as<tao::json::value>() == txn_content_updated);
828+
}
829+
830+
return {};
831+
});
832+
833+
REQUIRE_SUCCESS(tx_err.ec());
834+
}

test/utils/server_version.hxx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,11 @@ struct server_version {
304304
// See MB-63870
305305
return (major == 7 && minor == 6 && micro >= 4) || (major == 7 && minor > 6) || major > 7;
306306
}
307+
308+
[[nodiscard]] auto supports_replace_body_with_xattr() const -> bool
309+
{
310+
return (major == 7 && minor == 1) || (major == 7 && minor > 1) || major > 7;
311+
}
307312
};
308313

309314
} // namespace test::utils

0 commit comments

Comments
 (0)