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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/ext/cache/extconf_include.rb +3 -3
- data/ext/cache/mozilla-ca-bundle.crt +3 -165
- data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
- data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/CMakeLists.txt +14 -10
- data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy.cc +7 -4
- data/ext/couchbase/CMakeLists.txt +12 -1
- data/ext/couchbase/cmake/Profiler.cmake +15 -0
- data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +2 -2
- data/ext/couchbase/cmake/couchbase_cxx_client.pc.in +1 -1
- data/ext/couchbase/core/app_telemetry_address.cxx +55 -0
- data/ext/couchbase/core/app_telemetry_address.hxx +39 -0
- data/ext/couchbase/core/app_telemetry_meter.cxx +753 -0
- data/ext/couchbase/core/app_telemetry_meter.hxx +198 -0
- data/ext/couchbase/core/app_telemetry_reporter.cxx +895 -0
- data/ext/couchbase/core/app_telemetry_reporter.hxx +59 -0
- data/ext/couchbase/core/bucket.cxx +77 -35
- data/ext/couchbase/core/bucket.hxx +17 -10
- data/ext/couchbase/core/cluster.cxx +54 -16
- data/ext/couchbase/core/cluster_credentials.cxx +27 -0
- data/ext/couchbase/core/cluster_credentials.hxx +36 -0
- data/ext/couchbase/core/cluster_options.hxx +12 -0
- data/ext/couchbase/core/collections_component.cxx +7 -5
- data/ext/couchbase/core/http_component.cxx +6 -0
- data/ext/couchbase/core/impl/binary_collection.cxx +4 -0
- data/ext/couchbase/core/impl/bucket_manager.cxx +2 -0
- data/ext/couchbase/core/impl/cluster.cxx +9 -0
- data/ext/couchbase/core/impl/collection.cxx +2 -0
- data/ext/couchbase/core/impl/error.cxx +1 -0
- data/ext/couchbase/core/impl/logger.cxx +51 -0
- data/ext/couchbase/core/impl/replica_utils.cxx +1 -1
- data/ext/couchbase/core/impl/transaction_get_multi_replicas_from_preferred_server_group_spec.cxx +32 -0
- data/ext/couchbase/core/impl/transaction_get_multi_spec.cxx +30 -0
- data/ext/couchbase/core/impl/transaction_op_error_category.cxx +2 -0
- data/ext/couchbase/core/io/config_tracker.cxx +6 -6
- data/ext/couchbase/core/io/http_command.hxx +35 -11
- data/ext/couchbase/core/io/http_session.cxx +10 -0
- data/ext/couchbase/core/io/http_session.hxx +4 -0
- data/ext/couchbase/core/io/http_session_manager.hxx +83 -34
- data/ext/couchbase/core/io/mcbp_command.hxx +41 -2
- data/ext/couchbase/core/io/mcbp_session.cxx +52 -19
- data/ext/couchbase/core/io/mcbp_session.hxx +3 -0
- data/ext/couchbase/core/logger/logger.cxx +46 -0
- data/ext/couchbase/core/logger/logger.hxx +41 -1
- data/ext/couchbase/core/management/bucket_settings.hxx +1 -0
- data/ext/couchbase/core/management/bucket_settings_json.hxx +4 -0
- data/ext/couchbase/core/meta/features.hxx +32 -0
- data/ext/couchbase/core/operations/document_analytics.cxx +9 -9
- data/ext/couchbase/core/operations/document_append.cxx +1 -0
- data/ext/couchbase/core/operations/document_append.hxx +1 -0
- data/ext/couchbase/core/operations/document_get_all_replicas.hxx +10 -2
- data/ext/couchbase/core/operations/document_lookup_in.cxx +4 -0
- data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +14 -2
- data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +4 -0
- data/ext/couchbase/core/operations/document_mutate_in.cxx +4 -0
- data/ext/couchbase/core/operations/document_mutate_in.hxx +1 -0
- data/ext/couchbase/core/operations/document_prepend.cxx +1 -0
- data/ext/couchbase/core/operations/document_prepend.hxx +1 -0
- data/ext/couchbase/core/operations/document_query.cxx +12 -10
- data/ext/couchbase/core/operations/http_noop.cxx +1 -0
- data/ext/couchbase/core/operations/management/bucket_create.cxx +3 -0
- data/ext/couchbase/core/operations/management/bucket_update.cxx +3 -0
- data/ext/couchbase/core/origin.cxx +0 -5
- data/ext/couchbase/core/origin.hxx +2 -11
- data/ext/couchbase/core/platform/random.cc +6 -3
- data/ext/couchbase/core/platform/random.h +2 -2
- data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +9 -0
- data/ext/couchbase/core/timeout_defaults.hxx +4 -0
- data/ext/couchbase/core/topology/configuration.cxx +10 -13
- data/ext/couchbase/core/topology/configuration.hxx +14 -15
- data/ext/couchbase/core/topology/configuration_json.hxx +6 -0
- data/ext/couchbase/core/transactions/async_attempt_context.hxx +22 -2
- data/ext/couchbase/core/transactions/attempt_context.hxx +25 -7
- data/ext/couchbase/core/transactions/attempt_context_impl.cxx +688 -238
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +91 -12
- data/ext/couchbase/core/transactions/exceptions.cxx +5 -0
- data/ext/couchbase/core/transactions/exceptions.hxx +20 -0
- data/ext/couchbase/core/transactions/exceptions_fmt.hxx +3 -0
- data/ext/couchbase/core/transactions/forward_compat.cxx +71 -6
- data/ext/couchbase/core/transactions/forward_compat.hxx +45 -59
- data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +616 -0
- data/ext/couchbase/core/transactions/get_multi_orchestrator.hxx +61 -0
- data/ext/couchbase/core/transactions/internal/doc_record.cxx +8 -0
- data/ext/couchbase/core/transactions/internal/doc_record.hxx +16 -5
- data/ext/couchbase/core/transactions/internal/exceptions_internal.hxx +12 -0
- data/ext/couchbase/core/transactions/internal/transaction_context.hxx +13 -0
- data/ext/couchbase/core/transactions/internal/transaction_fields.hxx +1 -0
- data/ext/couchbase/core/transactions/staged_mutation.cxx +277 -96
- data/ext/couchbase/core/transactions/staged_mutation.hxx +28 -76
- data/ext/couchbase/core/transactions/transaction_context.cxx +33 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_mode.hxx +28 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +27 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +71 -0
- data/ext/couchbase/core/transactions/transaction_get_multi_result.hxx +66 -0
- data/ext/couchbase/core/transactions/transaction_links.hxx +10 -0
- data/ext/couchbase/core/transactions/transactions.cxx +8 -3
- data/ext/couchbase/core/utils/connection_string.cxx +4 -0
- data/ext/couchbase/core/utils/url_codec.cxx +26 -0
- data/ext/couchbase/core/utils/url_codec.hxx +11 -0
- data/ext/couchbase/core/websocket_codec.cxx +647 -0
- data/ext/couchbase/core/websocket_codec.hxx +77 -0
- data/ext/couchbase/couchbase/analytics_options.hxx +70 -6
- data/ext/couchbase/couchbase/application_telemetry_options.hxx +124 -0
- data/ext/couchbase/couchbase/cluster_options.hxx +17 -0
- data/ext/couchbase/couchbase/error_codes.hxx +1 -0
- data/ext/couchbase/couchbase/logger.hxx +16 -0
- data/ext/couchbase/couchbase/management/bucket_settings.hxx +1 -0
- data/ext/couchbase/couchbase/query_options.hxx +70 -6
- data/ext/couchbase/couchbase/transactions/async_attempt_context.hxx +29 -5
- data/ext/couchbase/couchbase/transactions/attempt_context.hxx +24 -7
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_mode.hxx +47 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_options.hxx +44 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +46 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_options.hxx +48 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +109 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_spec.hxx +47 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +102 -0
- data/ext/couchbase/couchbase/transactions/transaction_get_multi_spec.hxx +45 -0
- data/ext/rcb_buckets.cxx +26 -0
- data/lib/active_support/cache/couchbase_store.rb +1 -1
- data/lib/couchbase/cluster.rb +1 -1
- data/lib/couchbase/collection.rb +1 -1
- data/lib/couchbase/collection_options.rb +2 -2
- data/lib/couchbase/management/analytics_index_manager.rb +4 -4
- data/lib/couchbase/management/bucket_manager.rb +8 -2
- data/lib/couchbase/protostellar/cluster.rb +2 -2
- data/lib/couchbase/protostellar/collection.rb +1 -1
- data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +1 -1
- data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +4 -4
- data/lib/couchbase/protostellar/request_generator/admin/collection.rb +6 -6
- data/lib/couchbase/protostellar/request_generator/admin/query.rb +13 -13
- data/lib/couchbase/protostellar/request_generator/kv.rb +25 -25
- data/lib/couchbase/protostellar/request_generator/query.rb +4 -4
- data/lib/couchbase/protostellar/request_generator/search.rb +25 -25
- data/lib/couchbase/protostellar/response_converter/search.rb +1 -1
- data/lib/couchbase/protostellar/retry/reason.rb +1 -1
- data/lib/couchbase/protostellar/timeouts.rb +1 -1
- data/lib/couchbase/scope.rb +1 -1
- data/lib/couchbase/transcoder_flags.rb +1 -1
- data/lib/couchbase/utils/stdlib_logger_adapter.rb +1 -1
- data/lib/couchbase/version.rb +1 -1
- metadata +47 -19
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/COPYING +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/SnappyConfig.cmake.in +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/config.h.in +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.cc +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-internal.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.cc +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.cc +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.h +0 -0
- /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-public.h.in +0 -0
- /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
|