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
@@ -0,0 +1,616 @@
1
+ /*
2
+ * Copyright 2021-Present Couchbase, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #include "get_multi_orchestrator.hxx"
18
+
19
+ #include "active_transaction_record.hxx"
20
+ #include "attempt_context_impl.hxx"
21
+ #include "core/document_id.hxx"
22
+ #include "core/transactions/error_class.hxx"
23
+ #include "core/transactions/exceptions.hxx"
24
+ #include "forward_compat.hxx"
25
+
26
+ #include <algorithm>
27
+ #include <chrono>
28
+ #include <exception>
29
+ #include <queue>
30
+ #include <vector>
31
+
32
+ namespace couchbase::core::transactions
33
+ {
34
+ enum class get_multi_mode : std::uint8_t {
35
+ prioritise_latency,
36
+ disable_read_skew_detection,
37
+ prioritise_read_skew_detection,
38
+ };
39
+
40
+ namespace
41
+ {
42
+ auto
43
+ convert_mode(transaction_get_multi_mode mode) -> get_multi_mode
44
+ {
45
+ switch (mode) {
46
+ case transaction_get_multi_mode::prioritise_latency:
47
+ return get_multi_mode::prioritise_latency;
48
+ case transaction_get_multi_mode::disable_read_skew_detection:
49
+ return get_multi_mode::disable_read_skew_detection;
50
+ case transaction_get_multi_mode::prioritise_read_skew_detection:
51
+ return get_multi_mode::prioritise_read_skew_detection;
52
+ }
53
+ return get_multi_mode::prioritise_latency;
54
+ }
55
+
56
+ auto
57
+ convert_mode(transaction_get_multi_replicas_from_preferred_server_group_mode mode) -> get_multi_mode
58
+ {
59
+ switch (mode) {
60
+ case transaction_get_multi_replicas_from_preferred_server_group_mode::prioritise_latency:
61
+ return get_multi_mode::prioritise_latency;
62
+ case transaction_get_multi_replicas_from_preferred_server_group_mode::
63
+ disable_read_skew_detection:
64
+ return get_multi_mode::disable_read_skew_detection;
65
+ case transaction_get_multi_replicas_from_preferred_server_group_mode::
66
+ prioritise_read_skew_detection:
67
+ return get_multi_mode::prioritise_read_skew_detection;
68
+ }
69
+ return get_multi_mode::prioritise_latency;
70
+ }
71
+ } // namespace
72
+
73
+ enum class get_multi_phase : std::uint8_t {
74
+ first_doc_fetch,
75
+ subsequent_to_first_doc_fetch,
76
+ discovered_docs_in_t1,
77
+ resolving_t1_atr_entry_missing,
78
+ };
79
+
80
+ struct get_multi_spec {
81
+ std::size_t index{};
82
+ core::document_id id{};
83
+ };
84
+
85
+ // TODO(SA): I believe it should be accessible as an object directly
86
+ struct transaction_id {
87
+ std::string transaction;
88
+ std::string attempt;
89
+ std::string operation;
90
+
91
+ auto operator==(const transaction_id& other) const -> bool
92
+ {
93
+ return transaction == other.transaction && attempt == other.attempt &&
94
+ operation == other.operation;
95
+ }
96
+ auto operator<(const transaction_id& other) const -> bool
97
+ {
98
+ if (transaction != other.transaction) {
99
+ return transaction < other.transaction;
100
+ }
101
+
102
+ if (attempt != other.attempt) {
103
+ return attempt < other.attempt;
104
+ }
105
+
106
+ return operation < other.operation;
107
+ }
108
+ };
109
+
110
+ struct get_multi_result {
111
+ get_multi_spec spec;
112
+ std::optional<transaction_get_result> get_result{};
113
+ std::exception_ptr error{ nullptr };
114
+
115
+ [[nodiscard]] auto doc_exists() const -> bool
116
+ {
117
+ return get_result.has_value();
118
+ }
119
+
120
+ [[nodiscard]] auto has_transactional_metadata() const -> bool
121
+ {
122
+ if (const auto& result = get_result; result) {
123
+ return result->links().atr_id().has_value();
124
+ }
125
+ return false;
126
+ }
127
+
128
+ [[nodiscard]] auto extract_transaction_id() const -> std::optional<transaction_id>
129
+ {
130
+ if (const auto& result = get_result; result) {
131
+ auto txn = result->links().staged_transaction_id();
132
+ auto atmpt = result->links().staged_attempt_id();
133
+ auto op = result->links().staged_operation_id();
134
+
135
+ if (txn && atmpt && op) {
136
+ return transaction_id{
137
+ txn.value(),
138
+ atmpt.value(),
139
+ op.value(),
140
+ };
141
+ }
142
+ }
143
+ return {};
144
+ }
145
+
146
+ void copy_content_from_staged_operation_into_result()
147
+ {
148
+ if (auto& result = get_result; result) {
149
+ result->content(result->links().staged_content_json_or_binary());
150
+ }
151
+ }
152
+
153
+ [[nodiscard]] auto extract_atr_document_id() const -> std::optional<core::document_id>
154
+ {
155
+ if (const auto& result = get_result; result) {
156
+ auto id = result->links().atr_id();
157
+ auto bucket = result->links().atr_bucket_name();
158
+ auto scope = result->links().atr_scope_name();
159
+ auto collection = result->links().atr_collection_name();
160
+
161
+ if (id && bucket && scope && collection) {
162
+ return core::document_id{
163
+ bucket.value(),
164
+ scope.value(),
165
+ collection.value(),
166
+ id.value(),
167
+ };
168
+ }
169
+ }
170
+ return {};
171
+ }
172
+ };
173
+
174
+ struct classified_error {
175
+ core::transactions::error_class error_class;
176
+ core::transactions::external_exception cause;
177
+ };
178
+
179
+ auto
180
+ classify_error(const std::exception_ptr& err) -> classified_error
181
+ {
182
+ try {
183
+ std::rethrow_exception(err);
184
+ } catch (const transaction_operation_failed& e) {
185
+ return classified_error{ e.ec(), e.cause() };
186
+ } catch (...) {
187
+ return classified_error{ error_class::FAIL_OTHER, external_exception::UNKNOWN };
188
+ }
189
+ return classified_error{ error_class::FAIL_OTHER, external_exception::UNKNOWN };
190
+ }
191
+
192
+ auto
193
+ contains_mutation(const std::optional<std::vector<doc_record>>& mutated_ids,
194
+ const core::document_id& id) -> bool
195
+ {
196
+ if (!mutated_ids) {
197
+ return false;
198
+ }
199
+ return std::any_of(mutated_ids->begin(), mutated_ids->end(), [&id](const auto& mutation) {
200
+ return mutation == id;
201
+ });
202
+ }
203
+
204
+ class get_multi_operation : public std::enable_shared_from_this<get_multi_operation>
205
+ {
206
+ public:
207
+ static constexpr std::size_t default_number_of_concurrent_requests{ 100 };
208
+
209
+ ~get_multi_operation() = default;
210
+ get_multi_operation(const get_multi_operation&) = delete;
211
+ get_multi_operation(get_multi_operation&&) = delete;
212
+ auto operator=(const get_multi_operation&) -> get_multi_operation& = delete;
213
+ auto operator=(get_multi_operation&&) -> get_multi_operation& = delete;
214
+
215
+ get_multi_operation(
216
+ std::shared_ptr<attempt_context_impl> attempt,
217
+ const std::vector<core::document_id>& ids,
218
+ get_multi_mode mode,
219
+ std::size_t number_of_concurrent_requests,
220
+ bool use_replicas,
221
+ utils::movable_function<void(std::exception_ptr, std::vector<get_multi_result>)> callback)
222
+ : attempt_{ std::move(attempt) }
223
+ , responses_left_{ ids.size() }
224
+ , mode_{ mode }
225
+ , number_of_concurrent_requests_{ number_of_concurrent_requests }
226
+ , use_replicas_{ use_replicas } // TODO(SA): weird warning
227
+ // NOLINTNEXTLINE(bugprone-throw-keyword-missing)
228
+ , callback_{ std::move(callback) }
229
+ {
230
+ results_.resize(ids.size());
231
+ for (std::size_t index = 0; index < ids.size(); ++index) {
232
+ specs_.emplace_back(get_multi_spec{ index, ids[index] });
233
+ to_fetch_.emplace(get_multi_spec{ index, ids[index] });
234
+ }
235
+ }
236
+
237
+ void handle_individual_document_error(get_multi_spec spec, const std::exception_ptr& err)
238
+ {
239
+ auto index = spec.index;
240
+
241
+ if (err == nullptr) {
242
+ results_[index] = { std::move(spec), {}, {} };
243
+ } else {
244
+ auto [ec, cause] = classify_error(err);
245
+
246
+ if (cause == DOCUMENT_UNRETRIEVABLE_EXCEPTION || cause == DOCUMENT_NOT_FOUND_EXCEPTION) {
247
+ results_[index] = { std::move(spec), {}, {} };
248
+ } else {
249
+ results_[index] = { std::move(spec), {}, err };
250
+ }
251
+ }
252
+
253
+ --responses_left_;
254
+ }
255
+
256
+ void handle_individual_document_success(get_multi_spec spec,
257
+ std::optional<transaction_get_result> res)
258
+ {
259
+ auto index = spec.index;
260
+ results_[index] = { std::move(spec), std::move(res), {} };
261
+ --responses_left_;
262
+ }
263
+
264
+ auto pop_next_spec() -> std::optional<get_multi_spec>
265
+ {
266
+ if (to_fetch_.empty()) {
267
+ return {};
268
+ }
269
+ auto spec = to_fetch_.front();
270
+ to_fetch_.pop();
271
+ return spec;
272
+ }
273
+
274
+ void invoke_callback(std::exception_ptr error = nullptr)
275
+ {
276
+ if (auto callback = std::move(callback_); callback) {
277
+ callback(std::move(error), std::move(results_));
278
+ }
279
+ }
280
+
281
+ void retry(std::queue<get_multi_spec> specs)
282
+ {
283
+ to_fetch_ = std::move(specs);
284
+ responses_left_ = to_fetch_.size();
285
+ fetch_multiple_documents();
286
+ }
287
+
288
+ void reset_and_retry()
289
+ {
290
+ std::queue<get_multi_spec> to_fetch;
291
+ for (const auto& spec : specs_) {
292
+ to_fetch.emplace(spec);
293
+ }
294
+ results_.clear();
295
+ if (phase_ != get_multi_phase::first_doc_fetch) {
296
+ phase_ = get_multi_phase::subsequent_to_first_doc_fetch;
297
+ }
298
+ retry(std::move(to_fetch));
299
+ }
300
+
301
+ void completed()
302
+ {
303
+ invoke_callback();
304
+ }
305
+
306
+ void resolve_read_skew()
307
+ {
308
+ std::string other_attempt_id;
309
+ document_id atr_document_id{};
310
+
311
+ for (const auto& result : results_) {
312
+ if (!result.doc_exists()) {
313
+ continue;
314
+ }
315
+
316
+ auto txn_id = result.extract_transaction_id();
317
+ if (!txn_id) {
318
+ continue;
319
+ }
320
+ if (other_attempt_id.empty()) {
321
+ auto atr_id = result.extract_atr_document_id();
322
+ if (!atr_id) {
323
+ continue;
324
+ }
325
+
326
+ other_attempt_id = txn_id->attempt;
327
+ atr_document_id = atr_id.value();
328
+
329
+ } else if (other_attempt_id != txn_id->attempt) {
330
+ return reset_and_retry();
331
+ }
332
+ }
333
+
334
+ if (other_attempt_id.empty()) {
335
+ return reset_and_retry();
336
+ }
337
+
338
+ active_transaction_record::get_atr(
339
+ attempt_->cluster_ref(),
340
+ atr_document_id,
341
+ [other_attempt_id, self = shared_from_this()](std::error_code ec,
342
+ std::optional<active_transaction_record> atr) {
343
+ if (ec) {
344
+ return self->reset_and_retry();
345
+ }
346
+
347
+ std::vector<get_multi_result*> fetched_in_t1;
348
+ for (auto& result : self->results_) {
349
+ if (result.doc_exists()) {
350
+ auto txn_id = result.extract_transaction_id();
351
+ if (txn_id && txn_id->attempt == other_attempt_id) {
352
+ fetched_in_t1.push_back(&result);
353
+ }
354
+ }
355
+ }
356
+
357
+ if (atr) {
358
+ std::optional<atr_entry> attempt{};
359
+ for (const auto& entry : atr->entries()) {
360
+ if (entry.attempt_id() == other_attempt_id) {
361
+ attempt = entry;
362
+ }
363
+ }
364
+ if (attempt) {
365
+ auto state = attempt->state();
366
+
367
+ if (state == attempt_state::PENDING || state == attempt_state::ABORTED) {
368
+ return self->completed();
369
+ }
370
+
371
+ if (state == attempt_state::COMMITTED) {
372
+ if (self->phase_ == get_multi_phase::subsequent_to_first_doc_fetch) {
373
+ std::vector<get_multi_result> were_in_t1;
374
+ for (auto& result : self->results_) {
375
+ if (result.has_transactional_metadata() &&
376
+ (contains_mutation(attempt->inserted_ids(), result.spec.id) ||
377
+ contains_mutation(attempt->replaced_ids(), result.spec.id) ||
378
+ contains_mutation(attempt->removed_ids(), result.spec.id))) {
379
+ were_in_t1.emplace_back(result);
380
+ }
381
+ }
382
+
383
+ if (were_in_t1.empty()) {
384
+ for (auto& result : fetched_in_t1) {
385
+ result->copy_content_from_staged_operation_into_result();
386
+ }
387
+ return self->completed();
388
+ }
389
+
390
+ self->phase_ = get_multi_phase::discovered_docs_in_t1;
391
+
392
+ std::queue<get_multi_spec> to_fetch;
393
+ for (const auto& result : were_in_t1) {
394
+ to_fetch.emplace(result.spec);
395
+ }
396
+ return self->retry(std::move(to_fetch));
397
+ }
398
+ if (self->phase_ == get_multi_phase::discovered_docs_in_t1) {
399
+ for (auto& result : fetched_in_t1) {
400
+ result->copy_content_from_staged_operation_into_result();
401
+ }
402
+ return self->completed();
403
+ }
404
+ }
405
+
406
+ return self->reset_and_retry();
407
+ }
408
+
409
+ // T1's ATR entry is missing
410
+ if (self->phase_ == get_multi_phase::resolving_t1_atr_entry_missing) {
411
+ if (fetched_in_t1.empty()) {
412
+ return self->reset_and_retry();
413
+ }
414
+ return self->completed();
415
+ }
416
+ self->phase_ = get_multi_phase::resolving_t1_atr_entry_missing;
417
+ std::queue<get_multi_spec> to_fetch;
418
+ for (const auto& result : fetched_in_t1) {
419
+ to_fetch.emplace(result->spec);
420
+ }
421
+ return self->retry(std::move(to_fetch));
422
+ }
423
+ });
424
+ }
425
+
426
+ void disambiguate_results()
427
+ {
428
+ if (std::chrono::steady_clock::now() >= deadline_) {
429
+ return invoke_callback(std::make_exception_ptr(
430
+ transaction_operation_failed(error_class::FAIL_EXPIRY,
431
+ "timeout while fetching multiple documents")
432
+ .expired()));
433
+ }
434
+
435
+ std::set<transaction_id> transaction_ids{};
436
+
437
+ for (const auto& result : results_) {
438
+ if (const auto& id = result.extract_transaction_id(); id) {
439
+ transaction_ids.insert(id.value());
440
+ }
441
+ }
442
+
443
+ switch (transaction_ids.size()) {
444
+ case 0:
445
+ // no read skew
446
+ return completed();
447
+
448
+ case 1:
449
+ // one other transaction is involved, maybe we can resolve
450
+ return resolve_read_skew();
451
+
452
+ default:
453
+ // several transactions, too complex to resolve
454
+ return reset_and_retry();
455
+ }
456
+ }
457
+
458
+ void fetch_individual_document(const get_multi_spec& spec)
459
+ {
460
+ auto handler = [spec, self = shared_from_this()](const std::exception_ptr& error,
461
+ std::optional<transaction_get_result> res) {
462
+ if (res) {
463
+ auto forward_compat_err =
464
+ check_forward_compat(forward_compat_stage::GET_MULTI_GET, res->links().forward_compat());
465
+ if (forward_compat_err) {
466
+ self->invoke_callback(std::make_exception_ptr(forward_compat_err.value()));
467
+ return;
468
+ }
469
+ self->handle_individual_document_success(spec, std::move(res));
470
+ } else {
471
+ self->handle_individual_document_error(spec, error);
472
+ }
473
+ if (auto next_spec = self->pop_next_spec(); next_spec) {
474
+ return self->fetch_individual_document(next_spec.value());
475
+ }
476
+ if (self->responses_left_ == 0) {
477
+ if (self->phase_ == get_multi_phase::first_doc_fetch) {
478
+ self->phase_ = get_multi_phase::subsequent_to_first_doc_fetch;
479
+ }
480
+
481
+ switch (self->mode_) {
482
+ case get_multi_mode::disable_read_skew_detection:
483
+ return self->invoke_callback();
484
+ case get_multi_mode::prioritise_latency:
485
+ self->deadline_ = std::chrono::steady_clock::now() + std::chrono::milliseconds{ 100 };
486
+ break;
487
+ case get_multi_mode::prioritise_read_skew_detection:
488
+ self->deadline_ = self->attempt_->expiry_time();
489
+ break;
490
+ }
491
+
492
+ self->disambiguate_results();
493
+ }
494
+ };
495
+ if (use_replicas_) {
496
+ attempt_->get_replica_from_preferred_server_group(spec.id, std::move(handler));
497
+ } else {
498
+ attempt_->get_optional(spec.id, std::move(handler));
499
+ }
500
+ }
501
+
502
+ void fetch_multiple_documents()
503
+ {
504
+ std::size_t requests_scheduled{ 0 };
505
+ while (requests_scheduled < number_of_concurrent_requests_) {
506
+ if (auto next_spec = pop_next_spec(); next_spec) {
507
+ fetch_individual_document(next_spec.value());
508
+ ++requests_scheduled;
509
+ } else {
510
+ break;
511
+ }
512
+ }
513
+ }
514
+
515
+ private:
516
+ std::shared_ptr<attempt_context_impl> attempt_;
517
+ std::size_t responses_left_;
518
+ get_multi_mode mode_;
519
+ std::size_t number_of_concurrent_requests_;
520
+ bool use_replicas_;
521
+ utils::movable_function<void(std::exception_ptr, std::vector<get_multi_result>)> callback_;
522
+
523
+ std::vector<get_multi_spec> specs_{};
524
+ std::queue<get_multi_spec> to_fetch_{};
525
+ std::vector<get_multi_result> results_{};
526
+
527
+ std::chrono::steady_clock::time_point deadline_{};
528
+ get_multi_phase phase_{ get_multi_phase::first_doc_fetch };
529
+ };
530
+
531
+ get_multi_orchestrator::get_multi_orchestrator(std::shared_ptr<attempt_context_impl> attempt,
532
+ std::vector<core::document_id> ids)
533
+ : attempt_{ std::move(attempt) }
534
+ , ids_{ std::move(ids) }
535
+ {
536
+ }
537
+
538
+ void
539
+ get_multi_orchestrator::get_multi(
540
+ transaction_get_multi_mode mode,
541
+ utils::movable_function<void(std::exception_ptr, std::optional<transaction_get_multi_result>)>&&
542
+ cb)
543
+ {
544
+ auto operation = std::make_shared<get_multi_operation>(
545
+ attempt_,
546
+ ids_,
547
+ convert_mode(mode),
548
+ get_multi_operation::default_number_of_concurrent_requests,
549
+ false,
550
+ [cb = std::move(cb), self = shared_from_this()](std::exception_ptr error,
551
+ std::vector<get_multi_result> results) {
552
+ if (error) {
553
+ return cb(std::move(error), {});
554
+ }
555
+
556
+ std::vector<std::optional<codec::encoded_value>> content{};
557
+ content.resize(results.size());
558
+ std::exception_ptr first_error{ nullptr };
559
+ for (auto& result : results) {
560
+ if (auto& get = result.get_result; get) {
561
+ content[result.spec.index] = get->content();
562
+ }
563
+ if (result.error && !first_error) {
564
+ first_error = std::move(result.error);
565
+ }
566
+ }
567
+ if (first_error) {
568
+ return cb(first_error, transaction_get_multi_result{ content });
569
+ }
570
+ return cb(nullptr, transaction_get_multi_result{ content });
571
+ });
572
+ operation->fetch_multiple_documents();
573
+ }
574
+
575
+ void
576
+ get_multi_orchestrator::get_multi_replicas_from_preferred_server_group(
577
+ transaction_get_multi_replicas_from_preferred_server_group_mode mode,
578
+ utils::movable_function<
579
+ void(std::exception_ptr,
580
+ std::optional<transaction_get_multi_replicas_from_preferred_server_group_result>)>&& cb)
581
+ {
582
+ auto operation = std::make_shared<get_multi_operation>(
583
+ attempt_,
584
+ ids_,
585
+ convert_mode(mode),
586
+ get_multi_operation::default_number_of_concurrent_requests,
587
+ true,
588
+ [cb = std::move(cb), self = shared_from_this()](std::exception_ptr error,
589
+ std::vector<get_multi_result> results) {
590
+ if (error) {
591
+ return cb(std::move(error), {});
592
+ }
593
+
594
+ std::vector<std::optional<codec::encoded_value>> content{};
595
+ content.resize(results.size());
596
+ std::exception_ptr first_error{ nullptr };
597
+ for (auto& result : results) {
598
+ if (auto& get = result.get_result; get) {
599
+ content[result.spec.index] = get->content();
600
+ }
601
+ if (result.error && !first_error) {
602
+ first_error = std::move(result.error);
603
+ }
604
+ }
605
+
606
+ if (first_error) {
607
+ return cb(first_error,
608
+ transaction_get_multi_replicas_from_preferred_server_group_result{ content });
609
+ }
610
+
611
+ return cb(nullptr,
612
+ transaction_get_multi_replicas_from_preferred_server_group_result{ content });
613
+ });
614
+ operation->fetch_multiple_documents();
615
+ }
616
+ } // namespace couchbase::core::transactions
@@ -0,0 +1,61 @@
1
+ /*
2
+ * Copyright 2021-Present Couchbase, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ #pragma once
17
+
18
+ #include "core/document_id.hxx"
19
+ #include "core/utils/movable_function.hxx"
20
+ #include "transaction_get_multi_mode.hxx"
21
+ #include "transaction_get_multi_replicas_from_preferred_server_group_mode.hxx"
22
+ #include "transaction_get_multi_replicas_from_preferred_server_group_result.hxx"
23
+ #include "transaction_get_multi_result.hxx"
24
+
25
+ #include <memory>
26
+ #include <vector>
27
+
28
+ namespace couchbase::core::transactions
29
+ {
30
+ class attempt_context_impl;
31
+ class get_multi_operation;
32
+
33
+ class get_multi_orchestrator : public std::enable_shared_from_this<get_multi_orchestrator>
34
+ {
35
+ public:
36
+ get_multi_orchestrator(std::shared_ptr<attempt_context_impl> attempt,
37
+ std::vector<core::document_id> ids);
38
+ get_multi_orchestrator(const get_multi_orchestrator&) = default;
39
+ get_multi_orchestrator(get_multi_orchestrator&&) = default;
40
+ ~get_multi_orchestrator() = default;
41
+ auto operator=(const get_multi_orchestrator&) -> get_multi_orchestrator& = default;
42
+ auto operator=(get_multi_orchestrator&&) -> get_multi_orchestrator& = default;
43
+
44
+ void get_multi(transaction_get_multi_mode mode,
45
+ utils::movable_function<void(std::exception_ptr,
46
+ std::optional<transaction_get_multi_result>)>&& cb);
47
+
48
+ void get_multi_replicas_from_preferred_server_group(
49
+ transaction_get_multi_replicas_from_preferred_server_group_mode mode,
50
+ utils::movable_function<
51
+ void(std::exception_ptr,
52
+ std::optional<transaction_get_multi_replicas_from_preferred_server_group_result>)>&& cb);
53
+
54
+ private:
55
+ std::shared_ptr<attempt_context_impl> attempt_;
56
+ std::vector<core::document_id> ids_;
57
+
58
+ std::shared_ptr<get_multi_operation> operation_{ nullptr };
59
+ };
60
+
61
+ } // namespace couchbase::core::transactions
@@ -29,4 +29,12 @@ doc_record::create_from(const tao::json::value& obj) -> doc_record
29
29
  const std::string id = obj.at(ATR_FIELD_PER_DOC_ID).get_string();
30
30
  return { bucket_name, scope_name, collection_name, id };
31
31
  }
32
+
33
+ auto
34
+ operator==(const doc_record& doc, const core::document_id& id) -> bool
35
+ {
36
+ return doc.id_.bucket() == id.bucket() && doc.id_.scope() == id.scope() &&
37
+ doc.id_.collection() == id.collection() && doc.id_.key() == id.key();
38
+ }
39
+
32
40
  } // namespace couchbase::core::transactions