couchbase 3.5.7 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/cache/extconf_include.rb +3 -3
  4. data/ext/cache/mozilla-ca-bundle.crt +3 -165
  5. data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
  6. data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/CMakeLists.txt +14 -10
  7. data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy.cc +7 -4
  8. data/ext/couchbase/CMakeLists.txt +12 -1
  9. data/ext/couchbase/cmake/Profiler.cmake +15 -0
  10. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +2 -2
  11. data/ext/couchbase/cmake/couchbase_cxx_client.pc.in +1 -1
  12. data/ext/couchbase/core/app_telemetry_address.cxx +55 -0
  13. data/ext/couchbase/core/app_telemetry_address.hxx +39 -0
  14. data/ext/couchbase/core/app_telemetry_meter.cxx +753 -0
  15. data/ext/couchbase/core/app_telemetry_meter.hxx +198 -0
  16. data/ext/couchbase/core/app_telemetry_reporter.cxx +895 -0
  17. data/ext/couchbase/core/app_telemetry_reporter.hxx +59 -0
  18. data/ext/couchbase/core/bucket.cxx +77 -35
  19. data/ext/couchbase/core/bucket.hxx +17 -10
  20. data/ext/couchbase/core/cluster.cxx +54 -16
  21. data/ext/couchbase/core/cluster_credentials.cxx +27 -0
  22. data/ext/couchbase/core/cluster_credentials.hxx +36 -0
  23. data/ext/couchbase/core/cluster_options.hxx +12 -0
  24. data/ext/couchbase/core/collections_component.cxx +7 -5
  25. data/ext/couchbase/core/http_component.cxx +6 -0
  26. data/ext/couchbase/core/impl/binary_collection.cxx +4 -0
  27. data/ext/couchbase/core/impl/bucket_manager.cxx +2 -0
  28. data/ext/couchbase/core/impl/cluster.cxx +9 -0
  29. data/ext/couchbase/core/impl/collection.cxx +2 -0
  30. data/ext/couchbase/core/impl/error.cxx +1 -0
  31. data/ext/couchbase/core/impl/logger.cxx +51 -0
  32. data/ext/couchbase/core/impl/replica_utils.cxx +1 -1
  33. data/ext/couchbase/core/impl/transaction_get_multi_replicas_from_preferred_server_group_spec.cxx +32 -0
  34. data/ext/couchbase/core/impl/transaction_get_multi_spec.cxx +30 -0
  35. data/ext/couchbase/core/impl/transaction_op_error_category.cxx +2 -0
  36. data/ext/couchbase/core/io/config_tracker.cxx +6 -6
  37. data/ext/couchbase/core/io/http_command.hxx +35 -11
  38. data/ext/couchbase/core/io/http_session.cxx +10 -0
  39. data/ext/couchbase/core/io/http_session.hxx +4 -0
  40. data/ext/couchbase/core/io/http_session_manager.hxx +83 -34
  41. data/ext/couchbase/core/io/mcbp_command.hxx +41 -2
  42. data/ext/couchbase/core/io/mcbp_session.cxx +52 -19
  43. data/ext/couchbase/core/io/mcbp_session.hxx +3 -0
  44. data/ext/couchbase/core/logger/logger.cxx +46 -0
  45. data/ext/couchbase/core/logger/logger.hxx +41 -1
  46. data/ext/couchbase/core/management/bucket_settings.hxx +1 -0
  47. data/ext/couchbase/core/management/bucket_settings_json.hxx +4 -0
  48. data/ext/couchbase/core/meta/features.hxx +32 -0
  49. data/ext/couchbase/core/operations/document_analytics.cxx +9 -9
  50. data/ext/couchbase/core/operations/document_append.cxx +1 -0
  51. data/ext/couchbase/core/operations/document_append.hxx +1 -0
  52. data/ext/couchbase/core/operations/document_get_all_replicas.hxx +10 -2
  53. data/ext/couchbase/core/operations/document_lookup_in.cxx +4 -0
  54. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +14 -2
  55. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +4 -0
  56. data/ext/couchbase/core/operations/document_mutate_in.cxx +4 -0
  57. data/ext/couchbase/core/operations/document_mutate_in.hxx +1 -0
  58. data/ext/couchbase/core/operations/document_prepend.cxx +1 -0
  59. data/ext/couchbase/core/operations/document_prepend.hxx +1 -0
  60. data/ext/couchbase/core/operations/document_query.cxx +12 -10
  61. data/ext/couchbase/core/operations/http_noop.cxx +1 -0
  62. data/ext/couchbase/core/operations/management/bucket_create.cxx +3 -0
  63. data/ext/couchbase/core/operations/management/bucket_update.cxx +3 -0
  64. data/ext/couchbase/core/origin.cxx +0 -5
  65. data/ext/couchbase/core/origin.hxx +2 -11
  66. data/ext/couchbase/core/platform/random.cc +6 -3
  67. data/ext/couchbase/core/platform/random.h +2 -2
  68. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +9 -0
  69. data/ext/couchbase/core/timeout_defaults.hxx +4 -0
  70. data/ext/couchbase/core/topology/configuration.cxx +10 -13
  71. data/ext/couchbase/core/topology/configuration.hxx +14 -15
  72. data/ext/couchbase/core/topology/configuration_json.hxx +6 -0
  73. data/ext/couchbase/core/transactions/async_attempt_context.hxx +22 -2
  74. data/ext/couchbase/core/transactions/attempt_context.hxx +25 -7
  75. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +688 -238
  76. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +91 -12
  77. data/ext/couchbase/core/transactions/exceptions.cxx +5 -0
  78. data/ext/couchbase/core/transactions/exceptions.hxx +20 -0
  79. data/ext/couchbase/core/transactions/exceptions_fmt.hxx +3 -0
  80. data/ext/couchbase/core/transactions/forward_compat.cxx +71 -6
  81. data/ext/couchbase/core/transactions/forward_compat.hxx +45 -59
  82. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +616 -0
  83. data/ext/couchbase/core/transactions/get_multi_orchestrator.hxx +61 -0
  84. data/ext/couchbase/core/transactions/internal/doc_record.cxx +8 -0
  85. data/ext/couchbase/core/transactions/internal/doc_record.hxx +16 -5
  86. data/ext/couchbase/core/transactions/internal/exceptions_internal.hxx +12 -0
  87. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +13 -0
  88. data/ext/couchbase/core/transactions/internal/transaction_fields.hxx +1 -0
  89. data/ext/couchbase/core/transactions/staged_mutation.cxx +277 -96
  90. data/ext/couchbase/core/transactions/staged_mutation.hxx +28 -76
  91. data/ext/couchbase/core/transactions/transaction_context.cxx +33 -0
  92. data/ext/couchbase/core/transactions/transaction_get_multi_mode.hxx +28 -0
  93. data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +27 -0
  94. data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +71 -0
  95. data/ext/couchbase/core/transactions/transaction_get_multi_result.hxx +66 -0
  96. data/ext/couchbase/core/transactions/transaction_links.hxx +10 -0
  97. data/ext/couchbase/core/transactions/transactions.cxx +8 -3
  98. data/ext/couchbase/core/utils/connection_string.cxx +4 -0
  99. data/ext/couchbase/core/utils/url_codec.cxx +26 -0
  100. data/ext/couchbase/core/utils/url_codec.hxx +11 -0
  101. data/ext/couchbase/core/websocket_codec.cxx +647 -0
  102. data/ext/couchbase/core/websocket_codec.hxx +77 -0
  103. data/ext/couchbase/couchbase/analytics_options.hxx +70 -6
  104. data/ext/couchbase/couchbase/application_telemetry_options.hxx +124 -0
  105. data/ext/couchbase/couchbase/cluster_options.hxx +17 -0
  106. data/ext/couchbase/couchbase/error_codes.hxx +1 -0
  107. data/ext/couchbase/couchbase/logger.hxx +16 -0
  108. data/ext/couchbase/couchbase/management/bucket_settings.hxx +1 -0
  109. data/ext/couchbase/couchbase/query_options.hxx +70 -6
  110. data/ext/couchbase/couchbase/transactions/async_attempt_context.hxx +29 -5
  111. data/ext/couchbase/couchbase/transactions/attempt_context.hxx +24 -7
  112. data/ext/couchbase/couchbase/transactions/transaction_get_multi_mode.hxx +47 -0
  113. data/ext/couchbase/couchbase/transactions/transaction_get_multi_options.hxx +44 -0
  114. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +46 -0
  115. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_options.hxx +48 -0
  116. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +109 -0
  117. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_spec.hxx +47 -0
  118. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +102 -0
  119. data/ext/couchbase/couchbase/transactions/transaction_get_multi_spec.hxx +45 -0
  120. data/ext/rcb_buckets.cxx +26 -0
  121. data/lib/active_support/cache/couchbase_store.rb +1 -1
  122. data/lib/couchbase/cluster.rb +1 -1
  123. data/lib/couchbase/collection.rb +1 -1
  124. data/lib/couchbase/collection_options.rb +2 -2
  125. data/lib/couchbase/management/analytics_index_manager.rb +4 -4
  126. data/lib/couchbase/management/bucket_manager.rb +8 -2
  127. data/lib/couchbase/protostellar/cluster.rb +2 -2
  128. data/lib/couchbase/protostellar/collection.rb +1 -1
  129. data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +1 -1
  130. data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +4 -4
  131. data/lib/couchbase/protostellar/request_generator/admin/collection.rb +6 -6
  132. data/lib/couchbase/protostellar/request_generator/admin/query.rb +13 -13
  133. data/lib/couchbase/protostellar/request_generator/kv.rb +25 -25
  134. data/lib/couchbase/protostellar/request_generator/query.rb +4 -4
  135. data/lib/couchbase/protostellar/request_generator/search.rb +25 -25
  136. data/lib/couchbase/protostellar/response_converter/search.rb +1 -1
  137. data/lib/couchbase/protostellar/retry/reason.rb +1 -1
  138. data/lib/couchbase/protostellar/timeouts.rb +1 -1
  139. data/lib/couchbase/scope.rb +1 -1
  140. data/lib/couchbase/transcoder_flags.rb +1 -1
  141. data/lib/couchbase/utils/stdlib_logger_adapter.rb +1 -1
  142. data/lib/couchbase/version.rb +1 -1
  143. metadata +47 -19
  144. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/COPYING +0 -0
  145. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/SnappyConfig.cmake.in +0 -0
  146. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/config.h.in +0 -0
  147. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.cc +0 -0
  148. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.h +0 -0
  149. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-internal.h +0 -0
  150. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.cc +0 -0
  151. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.h +0 -0
  152. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.cc +0 -0
  153. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.h +0 -0
  154. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-public.h.in +0 -0
  155. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy.h +0 -0
@@ -19,6 +19,8 @@
19
19
  #include "attempt_context_impl.hxx"
20
20
  #include "attempt_context_testing_hooks.hxx"
21
21
  #include "core/cluster.hxx"
22
+ #include "core/impl/subdoc/opcode.hxx"
23
+ #include "core/impl/subdoc/path_flags.hxx"
22
24
  #include "core/logger/logger.hxx"
23
25
  #include "core/operations.hxx"
24
26
  #include "core/transactions/internal/logging.hxx"
@@ -33,6 +35,100 @@
33
35
 
34
36
  namespace couchbase::core::transactions
35
37
  {
38
+ staged_mutation::staged_mutation(staged_mutation_type type,
39
+ document_id doc_id,
40
+ couchbase::cas cas,
41
+ std::optional<codec::binary> staged_content,
42
+ std::uint32_t staged_flags,
43
+ std::uint32_t current_user_flags,
44
+ std::optional<document_metadata> doc_metadata,
45
+ std::string operation_id)
46
+ : type_{ type }
47
+ , doc_id_{ std::move(doc_id) }
48
+ , cas_{ cas }
49
+ , staged_content_{ std::move(staged_content) }
50
+ , staged_flags_{ staged_flags }
51
+ , current_user_flags_{ current_user_flags }
52
+ , doc_metadata_{ std::move(doc_metadata) }
53
+ , operation_id_{ std::move(operation_id) }
54
+ {
55
+ }
56
+
57
+ auto
58
+ staged_mutation::id() const -> const document_id&
59
+ {
60
+ return doc_id_;
61
+ }
62
+
63
+ auto
64
+ staged_mutation::cas() const -> const couchbase::cas&
65
+ {
66
+ return cas_;
67
+ }
68
+
69
+ auto
70
+ staged_mutation::type() const -> const staged_mutation_type&
71
+ {
72
+ return type_;
73
+ }
74
+
75
+ auto
76
+ staged_mutation::is_staged_binary() const -> bool
77
+ {
78
+ return codec::codec_flags::extract_common_flags(staged_flags_) ==
79
+ codec::codec_flags::common_flags::binary;
80
+ }
81
+
82
+ auto
83
+ staged_mutation::staged_content() const -> const std::optional<codec::binary>&
84
+ {
85
+ return staged_content_;
86
+ }
87
+
88
+ auto
89
+ staged_mutation::staged_flags() const -> std::uint32_t
90
+ {
91
+ return staged_flags_;
92
+ }
93
+
94
+ auto
95
+ staged_mutation::current_user_flags() const -> std::uint32_t
96
+ {
97
+ return current_user_flags_;
98
+ }
99
+
100
+ auto
101
+ staged_mutation::doc_metadata() const -> const std::optional<document_metadata>&
102
+ {
103
+ return doc_metadata_;
104
+ }
105
+
106
+ auto
107
+ staged_mutation::operation_id() const -> const std::string&
108
+ {
109
+ return operation_id_;
110
+ }
111
+
112
+ auto
113
+ staged_mutation::type_as_string() const -> std::string
114
+ {
115
+ switch (type_) {
116
+ case staged_mutation_type::INSERT:
117
+ return "INSERT";
118
+ case staged_mutation_type::REMOVE:
119
+ return "REMOVE";
120
+ case staged_mutation_type::REPLACE:
121
+ return "REPLACE";
122
+ }
123
+ throw std::runtime_error("unknown type of staged mutation");
124
+ }
125
+
126
+ void
127
+ staged_mutation::cas(couchbase::cas cas)
128
+ {
129
+ cas_ = cas;
130
+ }
131
+
36
132
  auto
37
133
  unstaging_state::wait_until_unstage_possible() -> bool
38
134
  {
@@ -98,11 +194,10 @@ staged_mutation_queue::extract_to(const std::string& prefix,
98
194
  tao::json::value removes = tao::json::empty_array;
99
195
 
100
196
  for (const auto& mutation : queue_) {
101
- const tao::json::value doc{ { ATR_FIELD_PER_DOC_ID, mutation.doc().id().key() },
102
- { ATR_FIELD_PER_DOC_BUCKET, mutation.doc().id().bucket() },
103
- { ATR_FIELD_PER_DOC_SCOPE, mutation.doc().id().scope() },
104
- { ATR_FIELD_PER_DOC_COLLECTION,
105
- mutation.doc().id().collection() } };
197
+ const tao::json::value doc{ { ATR_FIELD_PER_DOC_ID, mutation.id().key() },
198
+ { ATR_FIELD_PER_DOC_BUCKET, mutation.id().bucket() },
199
+ { ATR_FIELD_PER_DOC_SCOPE, mutation.id().scope() },
200
+ { ATR_FIELD_PER_DOC_COLLECTION, mutation.id().collection() } };
106
201
  switch (mutation.type()) {
107
202
  case staged_mutation_type::INSERT:
108
203
  inserts.push_back(doc);
@@ -149,7 +244,7 @@ staged_mutation_queue::find_any(const core::document_id& id) -> staged_mutation*
149
244
  {
150
245
  const std::lock_guard<std::mutex> lock(mutex_);
151
246
  for (auto& item : queue_) {
152
- if (document_ids_equal(item.doc().id(), id)) {
247
+ if (document_ids_equal(item.id(), id)) {
153
248
  return &item;
154
249
  }
155
250
  }
@@ -161,7 +256,7 @@ staged_mutation_queue::find_replace(const core::document_id& id) -> staged_mutat
161
256
  {
162
257
  const std::lock_guard<std::mutex> lock(mutex_);
163
258
  for (auto& item : queue_) {
164
- if (item.type() == staged_mutation_type::REPLACE && document_ids_equal(item.doc().id(), id)) {
259
+ if (item.type() == staged_mutation_type::REPLACE && document_ids_equal(item.id(), id)) {
165
260
  return &item;
166
261
  }
167
262
  }
@@ -173,7 +268,7 @@ staged_mutation_queue::find_insert(const core::document_id& id) -> staged_mutati
173
268
  {
174
269
  const std::lock_guard<std::mutex> lock(mutex_);
175
270
  for (auto& item : queue_) {
176
- if (item.type() == staged_mutation_type::INSERT && document_ids_equal(item.doc().id(), id)) {
271
+ if (item.type() == staged_mutation_type::INSERT && document_ids_equal(item.id(), id)) {
177
272
  return &item;
178
273
  }
179
274
  }
@@ -185,7 +280,7 @@ staged_mutation_queue::find_remove(const core::document_id& id) -> staged_mutati
185
280
  {
186
281
  const std::lock_guard<std::mutex> lock(mutex_);
187
282
  for (auto& item : queue_) {
188
- if (item.type() == staged_mutation_type::REMOVE && document_ids_equal(item.doc().id(), id)) {
283
+ if (item.type() == staged_mutation_type::REMOVE && document_ids_equal(item.id(), id)) {
189
284
  return &item;
190
285
  }
191
286
  }
@@ -257,7 +352,7 @@ staged_mutation_queue::commit(const std::shared_ptr<attempt_context_impl>& ctx)
257
352
  "caught exception while trying to initiate commit for {}. Aborting "
258
353
  "rest of commit and waiting for "
259
354
  "in-flight rollback operations to finish",
260
- item.doc().id());
355
+ item.id());
261
356
  aborted = true;
262
357
  break;
263
358
  }
@@ -347,7 +442,7 @@ staged_mutation_queue::rollback(const std::shared_ptr<attempt_context_impl>& ctx
347
442
  "caught exception while trying to initiate rollback for {}. "
348
443
  "Aborting rollback and waiting for "
349
444
  "in-flight rollback operations to finish",
350
- item.doc().id());
445
+ item.id());
351
446
  aborted = true;
352
447
  break;
353
448
  }
@@ -383,10 +478,8 @@ staged_mutation_queue::rollback_insert(const std::shared_ptr<attempt_context_imp
383
478
  async_exp_delay& delay,
384
479
  utils::movable_function<void(std::exception_ptr)> callback)
385
480
  {
386
- CB_ATTEMPT_CTX_LOG_TRACE(ctx,
387
- "rolling back staged insert for {} with cas {}",
388
- item.doc().id(),
389
- item.doc().cas().value());
481
+ CB_ATTEMPT_CTX_LOG_TRACE(
482
+ ctx, "rolling back staged insert for {} with cas {}", item.id(), item.cas().value());
390
483
 
391
484
  asio::post(asio::bind_executor(
392
485
  ctx->cluster_ref().io_context(),
@@ -399,34 +492,33 @@ staged_mutation_queue::rollback_insert(const std::shared_ptr<attempt_context_imp
399
492
  return callback({});
400
493
  };
401
494
 
402
- auto ec =
403
- ctx->error_if_expired_and_not_in_overtime(STAGE_DELETE_INSERTED, item.doc().id().key());
495
+ auto ec = ctx->error_if_expired_and_not_in_overtime(STAGE_DELETE_INSERTED, item.id().key());
404
496
  if (ec) {
405
497
  return handler(client_error(*ec, "expired in rollback and not in overtime mode"));
406
498
  }
407
499
 
408
500
  return ctx->hooks_.before_rollback_delete_inserted(
409
501
  ctx,
410
- item.doc().id().key(),
502
+ item.id().key(),
411
503
  [handler = std::move(handler), ctx, &item, delay](std::optional<error_class> ec) mutable {
412
504
  if (ec) {
413
505
  return handler(client_error(*ec, "before_rollback_delete_insert hook threw error"));
414
506
  }
415
- core::operations::mutate_in_request req{ item.doc().id() };
507
+ core::operations::mutate_in_request req{ item.id() };
416
508
  req.specs =
417
509
  couchbase::mutate_in_specs{
418
510
  couchbase::mutate_in_specs::remove(TRANSACTION_INTERFACE_PREFIX_ONLY).xattr(),
419
511
  }
420
512
  .specs();
421
513
  req.access_deleted = true;
422
- req.cas = item.doc().cas();
514
+ req.cas = item.cas();
423
515
  wrap_durable_request(req, ctx->overall()->config());
424
516
  return ctx->cluster_ref().execute(
425
517
  req,
426
518
  [handler = std::move(handler), ctx, &item, delay](
427
519
  const core::operations::mutate_in_response& resp) mutable {
428
520
  CB_ATTEMPT_CTX_LOG_TRACE(
429
- ctx, "mutate_in for {} with cas {}", item.doc().id(), item.doc().cas().value());
521
+ ctx, "mutate_in for {} with cas {}", item.id(), item.cas().value());
430
522
 
431
523
  auto res = result::create_from_subdoc_response(resp);
432
524
  return validate_rollback_insert_result(ctx, res, item, std::move(handler));
@@ -442,10 +534,8 @@ staged_mutation_queue::rollback_remove_or_replace(
442
534
  async_exp_delay& delay,
443
535
  utils::movable_function<void(std::exception_ptr)> callback)
444
536
  {
445
- CB_ATTEMPT_CTX_LOG_TRACE(ctx,
446
- "rolling back staged remove/replace for {} with cas {}",
447
- item.doc().id(),
448
- item.doc().cas().value());
537
+ CB_ATTEMPT_CTX_LOG_TRACE(
538
+ ctx, "rolling back staged remove/replace for {} with cas {}", item.id(), item.cas().value());
449
539
 
450
540
  asio::post(asio::bind_executor(
451
541
  ctx->cluster_ref().io_context(),
@@ -458,27 +548,26 @@ staged_mutation_queue::rollback_remove_or_replace(
458
548
  }
459
549
  return callback({});
460
550
  };
461
- auto ec =
462
- ctx->error_if_expired_and_not_in_overtime(STAGE_ROLLBACK_DOC, item.doc().id().key());
551
+ auto ec = ctx->error_if_expired_and_not_in_overtime(STAGE_ROLLBACK_DOC, item.id().key());
463
552
  if (ec) {
464
553
  return handler(
465
554
  client_error(*ec, "expired in rollback_remove_or_replace and not in expiry overtime"));
466
555
  }
467
556
  ctx->hooks_.before_doc_rolled_back(
468
557
  ctx,
469
- item.doc().id().key(),
558
+ item.id().key(),
470
559
  [handler = std::move(handler), ctx, &item, delay](std::optional<error_class> ec) mutable {
471
560
  if (ec) {
472
561
  return handler(client_error(*ec, "before_doc_rolled_back hook threw error"));
473
562
  }
474
- core::operations::mutate_in_request req{ item.doc().id() };
563
+ core::operations::mutate_in_request req{ item.id() };
475
564
  req.specs =
476
565
  couchbase::mutate_in_specs{
477
566
  couchbase::mutate_in_specs::remove(TRANSACTION_INTERFACE_PREFIX_ONLY).xattr(),
478
567
  }
479
568
  .specs();
480
- req.cas = item.doc().cas();
481
- req.flags = item.doc().content().flags;
569
+ req.cas = item.cas();
570
+ req.flags = item.current_user_flags();
482
571
  wrap_durable_request(req, ctx->overall()->config());
483
572
  return ctx->cluster_ref().execute(
484
573
  req,
@@ -501,7 +590,7 @@ staged_mutation_queue::commit_doc(const std::shared_ptr<attempt_context_impl>& c
501
590
  {
502
591
  CB_ATTEMPT_CTX_LOG_TRACE(ctx,
503
592
  "commit doc {}, cas_zero_mode {}, ambiguity_resolution_mode {}",
504
- item.doc().id(),
593
+ item.id(),
505
594
  cas_zero_mode,
506
595
  ambiguity_resolution_mode);
507
596
 
@@ -515,7 +604,7 @@ staged_mutation_queue::commit_doc(const std::shared_ptr<attempt_context_impl>& c
515
604
  cas_zero_mode,
516
605
  ambiguity_resolution_mode]() mutable {
517
606
  ctx->check_expiry_during_commit_or_rollback(
518
- STAGE_COMMIT_DOC, std::optional<const std::string>(item.doc().id().key()));
607
+ STAGE_COMMIT_DOC, std::optional<const std::string>(item.id().key()));
519
608
 
520
609
  auto handler = [this, callback = std::move(callback), ctx, &item, delay](
521
610
  const std::optional<client_error>& e,
@@ -535,25 +624,72 @@ staged_mutation_queue::commit_doc(const std::shared_ptr<attempt_context_impl>& c
535
624
 
536
625
  ctx->hooks_.before_doc_committed(
537
626
  ctx,
538
- item.doc().id().key(),
627
+ item.id().key(),
539
628
  [handler = std::move(handler), ctx, &item, delay, ambiguity_resolution_mode, cas_zero_mode](
540
- std::optional<error_class> ec) mutable {
629
+ const std::optional<error_class> ec) mutable {
541
630
  if (ec) {
542
631
  return handler(client_error(*ec, "before_doc_committed hook threw error"),
543
632
  ambiguity_resolution_mode,
544
633
  cas_zero_mode);
545
634
  }
546
635
  // move staged content into doc
547
- CB_ATTEMPT_CTX_LOG_TRACE(ctx,
548
- "commit doc id {}, content {}, cas {}",
549
- item.doc().id(),
550
- to_string(item.content().data),
551
- item.doc().cas().value());
636
+ CB_ATTEMPT_CTX_LOG_TRACE(ctx, "commit doc id {}, cas {}", item.id(), item.cas().value());
552
637
 
553
638
  if (item.type() == staged_mutation_type::INSERT && !cas_zero_mode) {
554
- core::operations::insert_request req{ item.doc().id(), item.content().data };
555
- req.flags = item.content().flags;
639
+ if (item.staged_content().has_value()) {
640
+ // We have stored the content for the staged mutation. This means that the cluster
641
+ // does not support replace_body_with_xattr. Perform a regular KV insert.
642
+ operations::insert_request req{ item.id(), item.staged_content().value() };
643
+ req.flags = item.staged_flags();
644
+ wrap_durable_request(req, ctx->overall()->config());
645
+ return ctx->cluster_ref().execute(
646
+ req,
647
+ [handler = std::move(handler),
648
+ ctx,
649
+ &item,
650
+ delay,
651
+ ambiguity_resolution_mode,
652
+ cas_zero_mode](const core::operations::insert_response& resp) mutable {
653
+ auto res = result::create_from_mutation_response(resp);
654
+ return validate_commit_doc_result(
655
+ ctx,
656
+ res,
657
+ item,
658
+ [ambiguity_resolution_mode, cas_zero_mode, handler = std::move(handler)](
659
+ const auto& e) mutable {
660
+ if (e) {
661
+ return handler(e, ambiguity_resolution_mode, cas_zero_mode);
662
+ }
663
+ // Commit successful
664
+ return handler({}, {}, {});
665
+ });
666
+ });
667
+ }
668
+
669
+ // We have not stored the content for the staged mutation. This means that the cluster
670
+ // supports replace_body_with_xattr.
671
+ operations::mutate_in_request req{ item.id() };
672
+ req.specs = {
673
+ impl::subdoc::command{
674
+ impl::subdoc::opcode::replace_body_with_xattr,
675
+ !item.is_staged_binary() ? STAGED_DATA : STAGED_BINARY_DATA,
676
+ {},
677
+ impl::subdoc::build_mutate_in_path_flags(
678
+ true, false, false, item.is_staged_binary()),
679
+ },
680
+ impl::subdoc::command{
681
+ impl::subdoc::opcode::remove,
682
+ TRANSACTION_INTERFACE_PREFIX_ONLY,
683
+ {},
684
+ impl::subdoc::build_mutate_in_path_flags(true, false, false, false),
685
+ },
686
+ };
687
+ req.cas = couchbase::cas{ item.cas() };
688
+ req.access_deleted = true;
689
+ req.revive_document = true;
690
+ req.flags = item.staged_flags();
556
691
  wrap_durable_request(req, ctx->overall()->config());
692
+
557
693
  return ctx->cluster_ref().execute(
558
694
  req,
559
695
  [handler = std::move(handler),
@@ -561,7 +697,7 @@ staged_mutation_queue::commit_doc(const std::shared_ptr<attempt_context_impl>& c
561
697
  &item,
562
698
  delay,
563
699
  ambiguity_resolution_mode,
564
- cas_zero_mode](const core::operations::insert_response& resp) mutable {
700
+ cas_zero_mode](const operations::mutate_in_response& resp) mutable {
565
701
  auto res = result::create_from_mutation_response(resp);
566
702
  return validate_commit_doc_result(
567
703
  ctx,
@@ -577,25 +713,76 @@ staged_mutation_queue::commit_doc(const std::shared_ptr<attempt_context_impl>& c
577
713
  });
578
714
  });
579
715
  }
580
- core::operations::mutate_in_request req{ item.doc().id() };
581
- req.specs =
582
- couchbase::mutate_in_specs{
583
- // TODO(SA): upsert null to "txn" to match Java implementation
584
- //
585
- // from CoreTransactionAttemptContext.java:
586
- // > Upsert this field to better handle illegal doc mutation.
587
- // > E.g. run shadowDocSameTxnKVInsert without this, fails
588
- // > at this point as path has been removed. Could also handle
589
- // > with a spec change to handle that.
590
- couchbase::mutate_in_specs::remove(TRANSACTION_INTERFACE_PREFIX_ONLY).xattr(),
591
- // subdoc::opcode::set_doc used in replace w/ empty path
592
- couchbase::mutate_in_specs::replace_raw("", item.content().data),
593
- }
594
- .specs();
595
- req.store_semantics = couchbase::store_semantics::replace;
596
- req.cas = couchbase::cas(cas_zero_mode ? 0 : item.doc().cas().value());
597
- req.flags = item.content().flags;
716
+
717
+ if (item.staged_content().has_value()) {
718
+ // We have stored the content for the staged mutation. This means that the cluster does
719
+ // not support replace_body_with_xattr.
720
+ operations::mutate_in_request req{ item.id() };
721
+ req.specs =
722
+ couchbase::mutate_in_specs{
723
+ // TODO(SA): upsert null to "txn" to match Java implementation
724
+ //
725
+ // from CoreTransactionAttemptContext.java:
726
+ // > Upsert this field to better handle illegal doc mutation.
727
+ // > E.g. run shadowDocSameTxnKVInsert without this, fails
728
+ // > at this point as path has been removed. Could also handle
729
+ // > with a spec change to handle that.
730
+ couchbase::mutate_in_specs::remove(TRANSACTION_INTERFACE_PREFIX_ONLY).xattr(),
731
+ // subdoc::opcode::set_doc used in replace w/ empty path
732
+ couchbase::mutate_in_specs::replace_raw("", item.staged_content().value()),
733
+ }
734
+ .specs();
735
+ req.store_semantics = couchbase::store_semantics::replace;
736
+ req.cas = couchbase::cas(cas_zero_mode ? 0 : item.cas().value());
737
+ req.flags = item.staged_flags();
738
+ wrap_durable_request(req, ctx->overall()->config());
739
+ return ctx->cluster_ref().execute(
740
+ req,
741
+ [handler = std::move(handler),
742
+ ctx,
743
+ &item,
744
+ delay,
745
+ ambiguity_resolution_mode,
746
+ cas_zero_mode](const core::operations::mutate_in_response& resp) mutable {
747
+ auto res = result::create_from_subdoc_response(resp);
748
+ return validate_commit_doc_result(
749
+ ctx,
750
+ res,
751
+ item,
752
+ [ambiguity_resolution_mode, cas_zero_mode, handler = std::move(handler)](
753
+ const auto& e) mutable {
754
+ if (e) {
755
+ return handler(e, ambiguity_resolution_mode, cas_zero_mode);
756
+ }
757
+ // Commit successful
758
+ return handler({}, {}, {});
759
+ });
760
+ });
761
+ }
762
+
763
+ // We have not stored the content for the staged mutation. This means that the cluster
764
+ // supports replace_body_with_xattr.
765
+ operations::mutate_in_request req{ item.id() };
766
+ req.specs = {
767
+ impl::subdoc::command{
768
+ impl::subdoc::opcode::replace_body_with_xattr,
769
+ !item.is_staged_binary() ? STAGED_DATA : STAGED_BINARY_DATA,
770
+ {},
771
+ impl::subdoc::build_mutate_in_path_flags(true, false, false, item.is_staged_binary()),
772
+ },
773
+ impl::subdoc::command{
774
+ impl::subdoc::opcode::remove,
775
+ TRANSACTION_INTERFACE_PREFIX_ONLY,
776
+ {},
777
+ impl::subdoc::build_mutate_in_path_flags(true, false, false, false),
778
+ },
779
+ };
780
+ if (!cas_zero_mode) {
781
+ req.cas = item.cas();
782
+ }
783
+ req.flags = item.staged_flags();
598
784
  wrap_durable_request(req, ctx->overall()->config());
785
+
599
786
  return ctx->cluster_ref().execute(
600
787
  req,
601
788
  [handler = std::move(handler),
@@ -603,8 +790,8 @@ staged_mutation_queue::commit_doc(const std::shared_ptr<attempt_context_impl>& c
603
790
  &item,
604
791
  delay,
605
792
  ambiguity_resolution_mode,
606
- cas_zero_mode](const core::operations::mutate_in_response& resp) mutable {
607
- auto res = result::create_from_subdoc_response(resp);
793
+ cas_zero_mode](const operations::mutate_in_response& resp) mutable {
794
+ auto res = result::create_from_mutation_response(resp);
608
795
  return validate_commit_doc_result(
609
796
  ctx,
610
797
  res,
@@ -628,7 +815,7 @@ staged_mutation_queue::remove_doc(const std::shared_ptr<attempt_context_impl>& c
628
815
  async_constant_delay& delay,
629
816
  utils::movable_function<void(std::exception_ptr)> callback)
630
817
  {
631
- CB_ATTEMPT_CTX_LOG_TRACE(ctx, "remove doc {}", item.doc().id());
818
+ CB_ATTEMPT_CTX_LOG_TRACE(ctx, "remove doc {}", item.id());
632
819
 
633
820
  asio::post(asio::bind_executor(
634
821
  ctx->cluster_ref().io_context(),
@@ -642,15 +829,13 @@ staged_mutation_queue::remove_doc(const std::shared_ptr<attempt_context_impl>& c
642
829
  };
643
830
 
644
831
  ctx->check_expiry_during_commit_or_rollback(
645
- STAGE_REMOVE_DOC, std::optional<const std::string>(item.doc().id().key()));
832
+ STAGE_REMOVE_DOC, std::optional<const std::string>(item.id().key()));
646
833
  return ctx->hooks_.before_doc_removed(
647
- ctx,
648
- item.doc().id().key(),
649
- [ctx, &item, delay, handler = std::move(handler)](auto ec) mutable {
834
+ ctx, item.id().key(), [ctx, &item, delay, handler = std::move(handler)](auto ec) mutable {
650
835
  if (ec) {
651
836
  return handler(client_error(*ec, "before_doc_removed hook threw error"));
652
837
  }
653
- core::operations::remove_request req{ item.doc().id() };
838
+ core::operations::remove_request req{ item.id() };
654
839
  wrap_durable_request(req, ctx->overall()->config());
655
840
  return ctx->cluster_ref().execute(
656
841
  req,
@@ -676,17 +861,15 @@ staged_mutation_queue::validate_commit_doc_result(const std::shared_ptr<attempt_
676
861
  }
677
862
  CB_ATTEMPT_CTX_LOG_TRACE(ctx, "commit doc result {}", res);
678
863
  // TODO(SA): mutation tokens
679
- const auto key = item.doc().id().key();
864
+ const auto key = item.id().key();
680
865
  ctx->hooks_.after_doc_committed_before_saving_cas(
681
- ctx,
682
- key,
683
- [ctx, res, key, item = std::move(item), handler = std::move(handler)](auto ec) mutable {
866
+ ctx, key, [ctx, res, key, &item, handler = std::move(handler)](auto ec) mutable {
684
867
  if (ec) {
685
868
  return handler(client_error(*ec, "after_doc_committed_before_saving_cas threw error"));
686
869
  }
687
- item.doc().cas(res.cas);
870
+ item.cas(couchbase::cas{ res.cas });
688
871
  return ctx->hooks_.after_doc_committed(
689
- ctx, key, [res, item = std::move(item), handler = std::move(handler)](auto ec) mutable {
872
+ ctx, key, [handler = std::move(handler)](auto ec) mutable {
690
873
  if (ec) {
691
874
  return handler(client_error(*ec, "after_doc_committed threw error"));
692
875
  }
@@ -708,7 +891,7 @@ staged_mutation_queue::validate_remove_doc_result(const std::shared_ptr<attempt_
708
891
  }
709
892
  CB_ATTEMPT_CTX_LOG_TRACE(ctx, "remove doc result {}", res);
710
893
  return ctx->hooks_.after_doc_removed_pre_retry(
711
- ctx, item.doc().id().key(), [handler = std::move(handler)](auto ec) {
894
+ ctx, item.id().key(), [handler = std::move(handler)](auto ec) {
712
895
  if (ec) {
713
896
  return handler(client_error(*ec, "after_doc_removed_pre_retry threw error"));
714
897
  }
@@ -730,7 +913,7 @@ staged_mutation_queue::validate_rollback_insert_result(
730
913
  }
731
914
  CB_ATTEMPT_CTX_LOG_TRACE(ctx, "rollback insert result {}", res);
732
915
  return ctx->hooks_.after_rollback_delete_inserted(
733
- ctx, item.doc().id().key(), [handler = std::move(handler)](auto ec) {
916
+ ctx, item.id().key(), [handler = std::move(handler)](auto ec) {
734
917
  if (ec) {
735
918
  return handler(client_error(*ec, "after_rollback_delete_insert hook threw error"));
736
919
  }
@@ -752,7 +935,7 @@ staged_mutation_queue::validate_rollback_remove_or_replace_result(
752
935
  }
753
936
  CB_ATTEMPT_CTX_LOG_TRACE(ctx, "rollback remove or replace result {}", res);
754
937
  return ctx->hooks_.after_rollback_replace_or_remove(
755
- ctx, item.doc().id().key(), [handler = std::move(handler)](auto ec) {
938
+ ctx, item.id().key(), [handler = std::move(handler)](auto ec) {
756
939
  if (ec) {
757
940
  return handler(client_error(*ec, "after_rollback_replace_or_remove hook threw error"));
758
941
  }
@@ -774,17 +957,22 @@ staged_mutation_queue::handle_commit_doc_error(
774
957
  try {
775
958
  if (ctx->expiry_overtime_mode_.load()) {
776
959
  CB_ATTEMPT_CTX_LOG_TRACE(
777
- ctx, "commit_doc for {} error while in overtime mode {}", item.doc().id(), e.what());
960
+ ctx, "commit_doc for {} error while in overtime mode {}", item.id(), e.what());
778
961
  throw transaction_operation_failed(FAIL_EXPIRY, "expired during commit")
779
962
  .no_rollback()
780
963
  .failed_post_commit();
781
964
  }
782
- CB_ATTEMPT_CTX_LOG_TRACE(ctx, "commit_doc for {} error {}", item.doc().id(), e.what());
965
+ CB_ATTEMPT_CTX_LOG_TRACE(ctx, "commit_doc for {} error {}", item.id(), e.what());
783
966
  switch (ec) {
784
967
  case FAIL_AMBIGUOUS:
785
968
  ambiguity_resolution_mode = true;
786
969
  throw retry_operation("FAIL_AMBIGUOUS in commit_doc");
787
970
  case FAIL_CAS_MISMATCH:
971
+ if (ambiguity_resolution_mode) {
972
+ throw transaction_operation_failed(ec, e.what()).no_rollback().failed_post_commit();
973
+ }
974
+ cas_zero_mode = true;
975
+ throw retry_operation("FAIL_CAS_MISMATCH in commit_doc");
788
976
  case FAIL_DOC_ALREADY_EXISTS:
789
977
  if (ambiguity_resolution_mode) {
790
978
  throw transaction_operation_failed(ec, e.what()).no_rollback().failed_post_commit();
@@ -824,13 +1012,13 @@ staged_mutation_queue::handle_remove_doc_error(
824
1012
  utils::movable_function<void(std::exception_ptr)> callback)
825
1013
  {
826
1014
  try {
827
- auto ec = e.ec();
1015
+ const auto ec = e.ec();
828
1016
  if (ctx->expiry_overtime_mode_.load()) {
829
1017
  CB_ATTEMPT_CTX_LOG_TRACE(
830
- ctx, "remove_doc for {} error while in overtime mode {}", item.doc().id(), e.what());
1018
+ ctx, "remove_doc for {} error while in overtime mode {}", item.id(), e.what());
831
1019
  throw transaction_operation_failed(ec, e.what()).no_rollback().failed_post_commit();
832
1020
  }
833
- CB_ATTEMPT_CTX_LOG_TRACE(ctx, "remove_doc for {} error {}", item.doc().id(), e.what());
1021
+ CB_ATTEMPT_CTX_LOG_TRACE(ctx, "remove_doc for {} error {}", item.id(), e.what());
834
1022
  switch (ec) {
835
1023
  case FAIL_AMBIGUOUS:
836
1024
  throw retry_operation("remove_doc got FAIL_AMBIGUOUS");
@@ -863,14 +1051,14 @@ staged_mutation_queue::handle_rollback_insert_error(
863
1051
  try {
864
1052
  if (ctx->expiry_overtime_mode_.load()) {
865
1053
  CB_ATTEMPT_CTX_LOG_TRACE(
866
- ctx, "rollback_insert for {} error while in overtime mode {}", item.doc().id(), e.what());
1054
+ ctx, "rollback_insert for {} error while in overtime mode {}", item.id(), e.what());
867
1055
  throw transaction_operation_failed(
868
1056
  FAIL_EXPIRY, std::string("expired while rolling back insert with {} ") + e.what())
869
1057
  .no_rollback()
870
1058
  .expired();
871
1059
  }
872
- CB_ATTEMPT_CTX_LOG_TRACE(ctx, "rollback_insert for {} error {}", item.doc().id(), e.what());
873
- switch (auto ec = e.ec(); ec) {
1060
+ CB_ATTEMPT_CTX_LOG_TRACE(ctx, "rollback_insert for {} error {}", item.id(), e.what());
1061
+ switch (const auto ec = e.ec(); ec) {
874
1062
  case FAIL_HARD:
875
1063
  case FAIL_CAS_MISMATCH:
876
1064
  throw transaction_operation_failed(ec, e.what()).no_rollback();
@@ -914,15 +1102,15 @@ staged_mutation_queue::handle_rollback_remove_or_replace_error(
914
1102
  CB_ATTEMPT_CTX_LOG_TRACE(
915
1103
  ctx,
916
1104
  "rollback_remove_or_replace_error for {} error while in overtime mode {}",
917
- item.doc().id(),
1105
+ item.id(),
918
1106
  e.what());
919
1107
  throw transaction_operation_failed(FAIL_EXPIRY,
920
1108
  std::string("expired while handling ") + e.what())
921
1109
  .no_rollback();
922
1110
  }
923
1111
  CB_ATTEMPT_CTX_LOG_TRACE(
924
- ctx, "rollback_remove_or_replace_error for {} error {}", item.doc().id(), e.what());
925
- switch (auto ec = e.ec(); ec) {
1112
+ ctx, "rollback_remove_or_replace_error for {} error {}", item.id(), e.what());
1113
+ switch (const auto ec = e.ec(); ec) {
926
1114
  case FAIL_HARD:
927
1115
  case FAIL_DOC_NOT_FOUND:
928
1116
  case FAIL_CAS_MISMATCH:
@@ -952,11 +1140,4 @@ staged_mutation_queue::handle_rollback_remove_or_replace_error(
952
1140
  callback(std::current_exception());
953
1141
  }
954
1142
  }
955
-
956
- auto
957
- staged_mutation::is_staged_binary() const -> bool
958
- {
959
- return codec::codec_flags::has_common_flags(content_.flags,
960
- codec::codec_flags::binary_common_flags);
961
- }
962
1143
  } // namespace couchbase::core::transactions