couchbase 3.4.1 → 3.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/ext/couchbase/CMakeLists.txt +2 -0
- data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +4 -0
- data/ext/couchbase/core/cluster_options.hxx +0 -1
- data/ext/couchbase/core/config_profile.cxx +23 -1
- data/ext/couchbase/core/config_profile.hxx +2 -12
- data/ext/couchbase/core/impl/analytics.cxx +236 -0
- data/ext/couchbase/core/impl/cluster.cxx +0 -1
- data/ext/couchbase/core/impl/dns_srv_tracker.cxx +5 -3
- data/ext/couchbase/core/impl/query.cxx +5 -5
- data/ext/couchbase/core/io/dns_client.cxx +225 -0
- data/ext/couchbase/core/io/dns_client.hxx +19 -188
- data/ext/couchbase/core/transactions/active_transaction_record.hxx +2 -2
- data/ext/couchbase/core/transactions/attempt_context_impl.cxx +3 -0
- data/ext/couchbase/core/transactions/attempt_context_impl.hxx +1 -1
- data/ext/couchbase/core/transactions/internal/transaction_context.hxx +12 -12
- data/ext/couchbase/core/transactions/internal/transactions_cleanup.hxx +7 -1
- data/ext/couchbase/core/transactions/transaction_context.cxx +1 -0
- data/ext/couchbase/core/transactions/transactions_cleanup.cxx +144 -155
- data/ext/couchbase/core/utils/connection_string.cxx +10 -3
- data/ext/couchbase/core/utils/connection_string.hxx +3 -3
- data/ext/couchbase/couchbase/analytics_error_context.hxx +143 -0
- data/ext/couchbase/couchbase/analytics_meta_data.hxx +155 -0
- data/ext/couchbase/couchbase/analytics_metrics.hxx +163 -0
- data/ext/couchbase/couchbase/analytics_options.hxx +359 -0
- data/ext/couchbase/couchbase/analytics_result.hxx +102 -0
- data/ext/couchbase/couchbase/analytics_scan_consistency.hxx +46 -0
- data/ext/couchbase/couchbase/analytics_status.hxx +41 -0
- data/ext/couchbase/couchbase/analytics_warning.hxx +85 -0
- data/ext/couchbase/couchbase/cluster.hxx +33 -0
- data/ext/couchbase/couchbase/fmt/analytics_status.hxx +76 -0
- data/ext/couchbase/couchbase/query_options.hxx +0 -1
- data/ext/couchbase/couchbase/scope.hxx +33 -0
- data/ext/couchbase/couchbase/transactions/attempt_context.hxx +1 -1
- data/ext/couchbase/test/CMakeLists.txt +1 -2
- data/ext/couchbase/test/test_helper.hxx +1 -1
- data/ext/couchbase/test/test_integration_analytics.cxx +289 -13
- data/ext/couchbase/test/test_integration_crud.cxx +8 -1
- data/ext/couchbase/test/test_integration_examples.cxx +41 -0
- data/ext/couchbase/test/test_integration_management.cxx +15 -3
- data/ext/couchbase/test/test_integration_search.cxx +601 -0
- data/ext/couchbase/test/test_transaction_transaction_simple.cxx +73 -0
- data/ext/couchbase/test/test_unit_config_profiles.cxx +12 -12
- data/ext/couchbase/test/test_unit_connection_string.cxx +35 -0
- data/ext/couchbase/third_party/snappy/CMakeLists.txt +150 -27
- data/ext/couchbase/third_party/snappy/cmake/config.h.in +28 -24
- data/ext/couchbase/third_party/snappy/snappy-internal.h +189 -25
- data/ext/couchbase/third_party/snappy/snappy-sinksource.cc +26 -9
- data/ext/couchbase/third_party/snappy/snappy-sinksource.h +11 -11
- data/ext/couchbase/third_party/snappy/snappy-stubs-internal.cc +1 -1
- data/ext/couchbase/third_party/snappy/snappy-stubs-internal.h +227 -308
- data/ext/couchbase/third_party/snappy/snappy-stubs-public.h.in +0 -11
- data/ext/couchbase/third_party/snappy/snappy.cc +1176 -410
- data/ext/couchbase/third_party/snappy/snappy.h +19 -4
- data/ext/couchbase.cxx +27 -6
- data/ext/revisions.rb +3 -3
- data/lib/couchbase/cluster.rb +13 -9
- data/lib/couchbase/cluster_registry.rb +7 -2
- data/lib/couchbase/configuration.rb +3 -4
- data/lib/couchbase/options.rb +85 -2
- data/lib/couchbase/search_options.rb +158 -240
- data/lib/couchbase/version.rb +1 -1
- metadata +17 -6
- data/ext/couchbase/core/CMakeLists.txt +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78899f9fd7614787a46a54ca185e869cc93a7714ff0a1ef4c38feef98d7d54be
|
4
|
+
data.tar.gz: f42e90dc0e70b2f690f9c4f7dffe3cd9675db7d8f9025bb5db315b6e59407199
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 804b0a4c05754a5349b19f4b06ee1017f62790e2f5f50696639d9cfc1b785d4f43d688cb05c325f5e10cb1e36fca1e4775c93f91cbf10ad600a5f4755a091a28
|
7
|
+
data.tar.gz: cbb64fe3e1c2f5925584032cd69bb626e87c9ff61c7735a570a7e291287874076e366e2282e15d432c18832b9853c749d806cab28aa65716dc91ab835910bd6b
|
data/README.md
CHANGED
@@ -23,7 +23,7 @@ The library has been tested with MRI 3.0, 3.1 and 3.2. Supported platforms are L
|
|
23
23
|
Add this line to your application's Gemfile:
|
24
24
|
|
25
25
|
```ruby
|
26
|
-
gem "couchbase", "3.4.
|
26
|
+
gem "couchbase", "3.4.2"
|
27
27
|
```
|
28
28
|
|
29
29
|
And then execute:
|
@@ -139,7 +139,7 @@ Now the API reference is accessible using a web browser (where `VERSION` is the
|
|
139
139
|
|
140
140
|
The gem is available as open source under the terms of the [Apache2 License](https://opensource.org/licenses/Apache-2.0).
|
141
141
|
|
142
|
-
Copyright 2011-
|
142
|
+
Copyright 2011-Present Couchbase, Inc.
|
143
143
|
|
144
144
|
Licensed under the Apache License, Version 2.0 (the "License");
|
145
145
|
you may not use this file except in compliance with the License.
|
@@ -255,6 +255,7 @@ set(couchbase_cxx_client_FILES
|
|
255
255
|
core/utils/mutation_token.cxx
|
256
256
|
core/utils/split_string.cxx
|
257
257
|
core/utils/url_codec.cxx
|
258
|
+
core/impl/analytics.cxx
|
258
259
|
core/impl/analytics_error_category.cxx
|
259
260
|
core/impl/append.cxx
|
260
261
|
core/impl/best_effort_retry_strategy.cxx
|
@@ -321,6 +322,7 @@ set(couchbase_cxx_client_FILES
|
|
321
322
|
core/impl/upsert.cxx
|
322
323
|
core/impl/view_error_category.cxx
|
323
324
|
core/impl/watch_query_indexes.cxx
|
325
|
+
core/io/dns_client.cxx
|
324
326
|
core/io/dns_config.cxx
|
325
327
|
core/io/http_parser.cxx
|
326
328
|
core/io/mcbp_message.cxx
|
@@ -18,6 +18,10 @@ set(PEGTL_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
|
18
18
|
set(PEGTL_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
19
19
|
add_subdirectory(${PROJECT_SOURCE_DIR}/third_party/snappy)
|
20
20
|
set_target_properties(snappy PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
21
|
+
if(NOT MSVC)
|
22
|
+
# https://github.com/google/snappy/pull/156
|
23
|
+
target_compile_options(snappy PRIVATE -Wno-sign-compare)
|
24
|
+
endif()
|
21
25
|
|
22
26
|
set(HDR_LOG_REQUIRED OFF CACHE BOOL "" FORCE)
|
23
27
|
set(HDR_HISTOGRAM_BUILD_SHARED OFF CACHE BOOL "" FORCE)
|
@@ -53,7 +53,6 @@ struct cluster_options {
|
|
53
53
|
std::chrono::milliseconds analytics_timeout = timeout_defaults::analytics_timeout;
|
54
54
|
std::chrono::milliseconds search_timeout = timeout_defaults::search_timeout;
|
55
55
|
std::chrono::milliseconds management_timeout = timeout_defaults::management_timeout;
|
56
|
-
std::chrono::milliseconds dns_srv_timeout = timeout_defaults::dns_srv_timeout;
|
57
56
|
|
58
57
|
bool enable_tls{ false };
|
59
58
|
std::string trust_certificate{};
|
@@ -22,4 +22,26 @@ couchbase::core::known_profiles()
|
|
22
22
|
{
|
23
23
|
static couchbase::core::config_profiles profiles{};
|
24
24
|
return profiles;
|
25
|
-
}
|
25
|
+
}
|
26
|
+
|
27
|
+
void
|
28
|
+
couchbase::core::development_profile::apply(couchbase::core::cluster_options& opts)
|
29
|
+
{
|
30
|
+
opts.key_value_timeout = std::chrono::seconds(20);
|
31
|
+
opts.key_value_durable_timeout = std::chrono::seconds(20);
|
32
|
+
opts.connect_timeout = std::chrono::seconds(20);
|
33
|
+
opts.view_timeout = std::chrono::minutes(2);
|
34
|
+
opts.query_timeout = std::chrono::minutes(2);
|
35
|
+
opts.analytics_timeout = std::chrono::minutes(2);
|
36
|
+
opts.search_timeout = std::chrono::minutes(2);
|
37
|
+
opts.management_timeout = std::chrono::minutes(2);
|
38
|
+
|
39
|
+
// C++SDK specific
|
40
|
+
opts.dns_config = couchbase::core::io::dns::dns_config{
|
41
|
+
opts.dns_config.nameserver(),
|
42
|
+
opts.dns_config.port(),
|
43
|
+
std::chrono::seconds(20), // timeout to make DNS-SRV query
|
44
|
+
};
|
45
|
+
opts.resolve_timeout = std::chrono::seconds(20); // timeout to resolve hostnames
|
46
|
+
opts.bootstrap_timeout = std::chrono::minutes(2); // overall timeout to bootstrap
|
47
|
+
}
|
@@ -36,17 +36,7 @@ class config_profile
|
|
36
36
|
class development_profile : public config_profile
|
37
37
|
{
|
38
38
|
public:
|
39
|
-
void apply(couchbase::core::cluster_options& opts) override
|
40
|
-
{
|
41
|
-
opts.key_value_timeout = std::chrono::milliseconds(20000); // 20 sec kv timeout.
|
42
|
-
opts.key_value_durable_timeout = std::chrono::milliseconds(20000); // 20 sec durable kv timeout.
|
43
|
-
opts.connect_timeout = std::chrono::milliseconds(20000); // 20 sec connect timeout.
|
44
|
-
opts.view_timeout = std::chrono::milliseconds(120000); // 2 minute view timeout
|
45
|
-
opts.query_timeout = std::chrono::milliseconds(120000); // 2 minute query timeout
|
46
|
-
opts.analytics_timeout = std::chrono::milliseconds(120000); // 2 minute analytics timeout
|
47
|
-
opts.search_timeout = std::chrono::milliseconds(120000); // 2 minute search timeout
|
48
|
-
opts.management_timeout = std::chrono::milliseconds(120000); // 2 minute management timeout
|
49
|
-
}
|
39
|
+
void apply(couchbase::core::cluster_options& opts) override;
|
50
40
|
};
|
51
41
|
|
52
42
|
// this class just registers the known profiles defined above, and allows access to them.
|
@@ -89,4 +79,4 @@ class config_profiles
|
|
89
79
|
config_profiles&
|
90
80
|
known_profiles();
|
91
81
|
|
92
|
-
} // namespace couchbase::core
|
82
|
+
} // namespace couchbase::core
|
@@ -0,0 +1,236 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020-Present Couchbase, Inc.
|
4
|
+
*
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
*
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
*
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#include <couchbase/cluster.hxx>
|
19
|
+
#include <couchbase/error_codes.hxx>
|
20
|
+
|
21
|
+
#include "core/cluster.hxx"
|
22
|
+
#include "core/operations/document_analytics.hxx"
|
23
|
+
|
24
|
+
namespace couchbase
|
25
|
+
{
|
26
|
+
static analytics_error_context
|
27
|
+
build_context(core::operations::analytics_response& resp)
|
28
|
+
{
|
29
|
+
return {
|
30
|
+
resp.ctx.ec,
|
31
|
+
resp.ctx.last_dispatched_to,
|
32
|
+
resp.ctx.last_dispatched_from,
|
33
|
+
resp.ctx.retry_attempts,
|
34
|
+
std::move(resp.ctx.retry_reasons),
|
35
|
+
resp.ctx.first_error_code,
|
36
|
+
std::move(resp.ctx.first_error_message),
|
37
|
+
std::move(resp.ctx.client_context_id),
|
38
|
+
std::move(resp.ctx.statement),
|
39
|
+
std::move(resp.ctx.parameters),
|
40
|
+
std::move(resp.ctx.method),
|
41
|
+
std::move(resp.ctx.path),
|
42
|
+
resp.ctx.http_status,
|
43
|
+
std::move(resp.ctx.http_body),
|
44
|
+
std::move(resp.ctx.hostname),
|
45
|
+
resp.ctx.port,
|
46
|
+
};
|
47
|
+
}
|
48
|
+
|
49
|
+
static analytics_status
|
50
|
+
map_status(core::operations::analytics_response::analytics_status status)
|
51
|
+
{
|
52
|
+
switch (status) {
|
53
|
+
case core::operations::analytics_response::running:
|
54
|
+
return analytics_status::running;
|
55
|
+
case core::operations::analytics_response::success:
|
56
|
+
return analytics_status::success;
|
57
|
+
case core::operations::analytics_response::errors:
|
58
|
+
return analytics_status::errors;
|
59
|
+
case core::operations::analytics_response::completed:
|
60
|
+
return analytics_status::completed;
|
61
|
+
case core::operations::analytics_response::stopped:
|
62
|
+
return analytics_status::stopped;
|
63
|
+
case core::operations::analytics_response::timedout:
|
64
|
+
return analytics_status::timeout;
|
65
|
+
case core::operations::analytics_response::closed:
|
66
|
+
return analytics_status::closed;
|
67
|
+
case core::operations::analytics_response::fatal:
|
68
|
+
return analytics_status::fatal;
|
69
|
+
case core::operations::analytics_response::aborted:
|
70
|
+
return analytics_status::aborted;
|
71
|
+
case core::operations::analytics_response::unknown:
|
72
|
+
return analytics_status::unknown;
|
73
|
+
}
|
74
|
+
return analytics_status::unknown;
|
75
|
+
}
|
76
|
+
|
77
|
+
static std::optional<couchbase::core::analytics_scan_consistency>
|
78
|
+
map_scan_consistency(std::optional<couchbase::analytics_scan_consistency> consistency)
|
79
|
+
{
|
80
|
+
if (consistency.has_value()) {
|
81
|
+
switch (consistency.value()) {
|
82
|
+
case analytics_scan_consistency::not_bounded:
|
83
|
+
return couchbase::core::analytics_scan_consistency::not_bounded;
|
84
|
+
case analytics_scan_consistency::request_plus:
|
85
|
+
return couchbase::core::analytics_scan_consistency::request_plus;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
return {};
|
89
|
+
}
|
90
|
+
|
91
|
+
static std::vector<codec::binary>
|
92
|
+
map_rows(const core::operations::analytics_response& resp)
|
93
|
+
{
|
94
|
+
std::vector<codec::binary> rows;
|
95
|
+
rows.reserve(resp.rows.size());
|
96
|
+
for (const auto& row : resp.rows) {
|
97
|
+
rows.emplace_back(core::utils::to_binary(row));
|
98
|
+
}
|
99
|
+
return rows;
|
100
|
+
}
|
101
|
+
|
102
|
+
static std::vector<analytics_warning>
|
103
|
+
map_warnings(core::operations::analytics_response& resp)
|
104
|
+
{
|
105
|
+
if (resp.meta.warnings.empty()) {
|
106
|
+
return {};
|
107
|
+
}
|
108
|
+
std::vector<analytics_warning> warnings;
|
109
|
+
warnings.reserve(resp.meta.warnings.size());
|
110
|
+
for (auto& warning : resp.meta.warnings) {
|
111
|
+
warnings.emplace_back(warning.code, std::move(warning.message));
|
112
|
+
}
|
113
|
+
return warnings;
|
114
|
+
}
|
115
|
+
|
116
|
+
static analytics_metrics
|
117
|
+
map_metrics(const core::operations::analytics_response& resp)
|
118
|
+
{
|
119
|
+
return analytics_metrics{
|
120
|
+
resp.meta.metrics.elapsed_time, resp.meta.metrics.execution_time, resp.meta.metrics.result_count,
|
121
|
+
resp.meta.metrics.result_size, resp.meta.metrics.processed_objects, resp.meta.metrics.error_count,
|
122
|
+
resp.meta.metrics.warning_count,
|
123
|
+
};
|
124
|
+
}
|
125
|
+
|
126
|
+
static std::optional<std::vector<std::byte>>
|
127
|
+
map_signature(core::operations::analytics_response& resp)
|
128
|
+
{
|
129
|
+
if (!resp.meta.signature) {
|
130
|
+
return {};
|
131
|
+
}
|
132
|
+
return core::utils::to_binary(resp.meta.signature.value());
|
133
|
+
}
|
134
|
+
|
135
|
+
static analytics_result
|
136
|
+
build_result(core::operations::analytics_response& resp)
|
137
|
+
{
|
138
|
+
return {
|
139
|
+
analytics_meta_data{
|
140
|
+
std::move(resp.meta.request_id),
|
141
|
+
std::move(resp.meta.client_context_id),
|
142
|
+
map_status(resp.meta.status),
|
143
|
+
map_warnings(resp),
|
144
|
+
map_metrics(resp),
|
145
|
+
map_signature(resp),
|
146
|
+
},
|
147
|
+
map_rows(resp),
|
148
|
+
};
|
149
|
+
}
|
150
|
+
|
151
|
+
static core::operations::analytics_request
|
152
|
+
build_analytics_request(std::string statement,
|
153
|
+
analytics_options::built options,
|
154
|
+
std::optional<std::string> bucket_name,
|
155
|
+
std::optional<std::string> scope_name)
|
156
|
+
{
|
157
|
+
core::operations::analytics_request request{
|
158
|
+
std::move(statement),
|
159
|
+
options.readonly,
|
160
|
+
options.priority,
|
161
|
+
std::move(bucket_name),
|
162
|
+
std::move(scope_name),
|
163
|
+
{},
|
164
|
+
map_scan_consistency(options.scan_consistency),
|
165
|
+
{},
|
166
|
+
{},
|
167
|
+
{},
|
168
|
+
{},
|
169
|
+
std::move(options.client_context_id),
|
170
|
+
options.timeout,
|
171
|
+
};
|
172
|
+
if (!options.raw.empty()) {
|
173
|
+
for (auto& [name, value] : options.raw) {
|
174
|
+
request.raw[name] = std::move(value);
|
175
|
+
}
|
176
|
+
}
|
177
|
+
if (!options.positional_parameters.empty()) {
|
178
|
+
for (auto& value : options.positional_parameters) {
|
179
|
+
request.positional_parameters.emplace_back(std::move(value));
|
180
|
+
}
|
181
|
+
}
|
182
|
+
if (!options.named_parameters.empty()) {
|
183
|
+
for (auto& [name, value] : options.named_parameters) {
|
184
|
+
request.named_parameters[name] = std::move(value);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
return request;
|
188
|
+
}
|
189
|
+
|
190
|
+
void
|
191
|
+
cluster::analytics_query(std::string statement, const analytics_options& options, analytics_handler&& handler) const
|
192
|
+
{
|
193
|
+
auto request = build_analytics_request(std::move(statement), options.build(), {}, {});
|
194
|
+
|
195
|
+
core_->execute(std::move(request), [handler = std::move(handler)](core::operations::analytics_response resp) mutable {
|
196
|
+
auto r = std::move(resp);
|
197
|
+
return handler(build_context(r), build_result(r));
|
198
|
+
});
|
199
|
+
}
|
200
|
+
|
201
|
+
auto
|
202
|
+
cluster::analytics_query(std::string statement, const analytics_options& options) const
|
203
|
+
-> std::future<std::pair<analytics_error_context, analytics_result>>
|
204
|
+
{
|
205
|
+
auto barrier = std::make_shared<std::promise<std::pair<analytics_error_context, analytics_result>>>();
|
206
|
+
auto future = barrier->get_future();
|
207
|
+
analytics_query(std::move(statement), options, [barrier](auto ctx, auto result) {
|
208
|
+
barrier->set_value({ std::move(ctx), std::move(result) });
|
209
|
+
});
|
210
|
+
return future;
|
211
|
+
}
|
212
|
+
|
213
|
+
void
|
214
|
+
scope::analytics_query(std::string statement, const analytics_options& options, analytics_handler&& handler) const
|
215
|
+
{
|
216
|
+
auto request = build_analytics_request(std::move(statement), options.build(), bucket_name_, name_);
|
217
|
+
|
218
|
+
core_->execute(std::move(request), [handler = std::move(handler)](core::operations::analytics_response resp) mutable {
|
219
|
+
auto r = std::move(resp);
|
220
|
+
return handler(build_context(r), build_result(r));
|
221
|
+
});
|
222
|
+
}
|
223
|
+
|
224
|
+
auto
|
225
|
+
scope::analytics_query(std::string statement, const analytics_options& options) const
|
226
|
+
-> std::future<std::pair<analytics_error_context, analytics_result>>
|
227
|
+
{
|
228
|
+
auto barrier = std::make_shared<std::promise<std::pair<analytics_error_context, analytics_result>>>();
|
229
|
+
auto future = barrier->get_future();
|
230
|
+
analytics_query(std::move(statement), options, [barrier](auto ctx, auto result) {
|
231
|
+
barrier->set_value({ std::move(ctx), std::move(result) });
|
232
|
+
});
|
233
|
+
return future;
|
234
|
+
}
|
235
|
+
|
236
|
+
} // namespace couchbase
|
@@ -95,7 +95,6 @@ options_to_origin(const std::string& connection_string, const couchbase::cluster
|
|
95
95
|
if (opts.dns.nameserver) {
|
96
96
|
user_options.dns_config =
|
97
97
|
io::dns::dns_config(opts.dns.nameserver.value(), opts.dns.port.value_or(io::dns::dns_config::default_port), opts.dns.timeout);
|
98
|
-
user_options.dns_srv_timeout = opts.dns.timeout;
|
99
98
|
}
|
100
99
|
user_options.enable_clustermap_notification = opts.behavior.enable_clustermap_notification;
|
101
100
|
user_options.show_queries = opts.behavior.show_queries;
|
@@ -16,11 +16,13 @@
|
|
16
16
|
*/
|
17
17
|
|
18
18
|
#include "dns_srv_tracker.hxx"
|
19
|
-
|
20
|
-
#include "
|
19
|
+
|
20
|
+
#include "core/logger/logger.hxx"
|
21
|
+
#include "core/utils/join_strings.hxx"
|
21
22
|
|
22
23
|
#include <asio/bind_executor.hpp>
|
23
24
|
#include <asio/io_context.hpp>
|
25
|
+
#include <asio/post.hpp>
|
24
26
|
|
25
27
|
#include <memory>
|
26
28
|
|
@@ -44,7 +46,7 @@ dns_srv_tracker::get_srv_nodes(utils::movable_function<void(origin::node_list, s
|
|
44
46
|
address_,
|
45
47
|
service_,
|
46
48
|
config_,
|
47
|
-
[self = shared_from_this(), callback = std::move(callback)](couchbase::core::io::dns::
|
49
|
+
[self = shared_from_this(), callback = std::move(callback)](couchbase::core::io::dns::dns_srv_response&& resp) mutable {
|
48
50
|
origin::node_list nodes;
|
49
51
|
if (resp.ec) {
|
50
52
|
CB_LOG_WARNING("failed to fetch DNS SRV records for \"{}\" ({}), assuming that cluster is listening this address",
|
@@ -152,10 +152,10 @@ build_result(operations::query_response& resp)
|
|
152
152
|
}
|
153
153
|
|
154
154
|
static core::operations::query_request
|
155
|
-
build_query_request(query_options::built options)
|
155
|
+
build_query_request(std::string statement, query_options::built options)
|
156
156
|
{
|
157
157
|
operations::query_request request{
|
158
|
-
|
158
|
+
std::move(statement),
|
159
159
|
options.adhoc,
|
160
160
|
options.metrics,
|
161
161
|
options.readonly,
|
@@ -222,8 +222,9 @@ build_transaction_query_result(operations::query_response resp, std::error_code
|
|
222
222
|
core::operations::query_request
|
223
223
|
build_transaction_query_request(query_options::built opts)
|
224
224
|
{
|
225
|
-
return build_query_request(opts);
|
225
|
+
return build_query_request("", opts);
|
226
226
|
}
|
227
|
+
|
227
228
|
void
|
228
229
|
initiate_query_operation(std::shared_ptr<couchbase::core::cluster> core,
|
229
230
|
std::string statement,
|
@@ -231,8 +232,7 @@ initiate_query_operation(std::shared_ptr<couchbase::core::cluster> core,
|
|
231
232
|
query_options::built options,
|
232
233
|
query_handler&& handler)
|
233
234
|
{
|
234
|
-
auto request = build_query_request(options);
|
235
|
-
request.statement = std::move(statement);
|
235
|
+
auto request = build_query_request(std::move(statement), options);
|
236
236
|
if (query_context) {
|
237
237
|
request.query_context = std::move(query_context);
|
238
238
|
}
|
@@ -0,0 +1,225 @@
|
|
1
|
+
/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2
|
+
/*
|
3
|
+
* Copyright 2020-Present Couchbase, Inc.
|
4
|
+
*
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
* you may not use this file except in compliance with the License.
|
7
|
+
* You may obtain a copy of the License at
|
8
|
+
*
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
*
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
* See the License for the specific language governing permissions and
|
15
|
+
* limitations under the License.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#include "dns_client.hxx"
|
19
|
+
|
20
|
+
#include "core/logger/logger.hxx"
|
21
|
+
#include "core/utils/join_strings.hxx"
|
22
|
+
#include "dns_codec.hxx"
|
23
|
+
#include "dns_config.hxx"
|
24
|
+
|
25
|
+
#include <couchbase/error_codes.hxx>
|
26
|
+
|
27
|
+
#include <asio/ip/tcp.hpp>
|
28
|
+
#include <asio/read.hpp>
|
29
|
+
#include <asio/write.hpp>
|
30
|
+
|
31
|
+
#include <memory>
|
32
|
+
#include <sstream>
|
33
|
+
|
34
|
+
namespace couchbase::core::io::dns
|
35
|
+
{
|
36
|
+
class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
|
37
|
+
{
|
38
|
+
public:
|
39
|
+
dns_srv_command(asio::io_context& ctx,
|
40
|
+
const std::string& name,
|
41
|
+
const std::string& service,
|
42
|
+
const asio::ip::address& address,
|
43
|
+
std::uint16_t port,
|
44
|
+
utils::movable_function<void(couchbase::core::io::dns::dns_srv_response&& resp)> handler)
|
45
|
+
: deadline_(ctx)
|
46
|
+
, udp_deadline_(ctx)
|
47
|
+
, udp_(ctx)
|
48
|
+
, tcp_(ctx)
|
49
|
+
, address_(address)
|
50
|
+
, port_(port)
|
51
|
+
, handler_(std::move(handler))
|
52
|
+
{
|
53
|
+
static std::string protocol{ "_tcp" };
|
54
|
+
dns_message request{};
|
55
|
+
question_record qr;
|
56
|
+
qr.klass = resource_class::in;
|
57
|
+
qr.type = resource_type::srv;
|
58
|
+
qr.name.labels.push_back(service);
|
59
|
+
qr.name.labels.push_back(protocol);
|
60
|
+
std::string label;
|
61
|
+
std::istringstream name_stream(name);
|
62
|
+
while (std::getline(name_stream, label, '.')) {
|
63
|
+
qr.name.labels.push_back(label);
|
64
|
+
}
|
65
|
+
request.questions.emplace_back(qr);
|
66
|
+
send_buf_ = dns_codec::encode(request);
|
67
|
+
}
|
68
|
+
|
69
|
+
void execute(std::chrono::milliseconds total_timeout, std::chrono::milliseconds udp_timeout)
|
70
|
+
{
|
71
|
+
asio::ip::udp::endpoint endpoint(address_, port_);
|
72
|
+
udp_.open(endpoint.protocol());
|
73
|
+
udp_.async_send_to(
|
74
|
+
asio::buffer(send_buf_), endpoint, [self = shared_from_this()](std::error_code ec1, std::size_t /* bytes_transferred */) mutable {
|
75
|
+
if (ec1) {
|
76
|
+
self->udp_deadline_.cancel();
|
77
|
+
CB_LOG_DEBUG("DNS UDP write operation has got error {}, retrying with TCP", ec1.message());
|
78
|
+
return self->retry_with_tcp();
|
79
|
+
}
|
80
|
+
|
81
|
+
self->recv_buf_.resize(512);
|
82
|
+
self->udp_.async_receive_from(
|
83
|
+
asio::buffer(self->recv_buf_), self->udp_sender_, [self](std::error_code ec2, std::size_t bytes_transferred) mutable {
|
84
|
+
self->udp_deadline_.cancel();
|
85
|
+
if (ec2) {
|
86
|
+
CB_LOG_DEBUG("DNS UDP read operation has got error {}, retrying with TCP", ec2.message());
|
87
|
+
return self->retry_with_tcp();
|
88
|
+
}
|
89
|
+
self->recv_buf_.resize(bytes_transferred);
|
90
|
+
const dns_message message = dns_codec::decode(self->recv_buf_);
|
91
|
+
if (message.header.flags.tc == truncation::yes) {
|
92
|
+
self->udp_.close();
|
93
|
+
CB_LOG_DEBUG("DNS UDP read operation returned truncated response, retrying with TCP");
|
94
|
+
return self->retry_with_tcp();
|
95
|
+
}
|
96
|
+
self->deadline_.cancel();
|
97
|
+
dns_srv_response resp{ ec2 };
|
98
|
+
resp.targets.reserve(message.answers.size());
|
99
|
+
for (const auto& answer : message.answers) {
|
100
|
+
resp.targets.emplace_back(dns_srv_response::address{ utils::join_strings(answer.target.labels, "."), answer.port });
|
101
|
+
}
|
102
|
+
CB_LOG_DEBUG("DNS UDP returned {} records", resp.targets.size());
|
103
|
+
return self->handler_(std::move(resp));
|
104
|
+
});
|
105
|
+
});
|
106
|
+
udp_deadline_.expires_after(udp_timeout);
|
107
|
+
deadline_.async_wait([self = shared_from_this()](std::error_code ec) {
|
108
|
+
if (ec == asio::error::operation_aborted) {
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
CB_LOG_DEBUG("DNS UDP deadline has been reached, cancelling UDP operation and fall back to TCP");
|
112
|
+
self->udp_.cancel();
|
113
|
+
return self->retry_with_tcp();
|
114
|
+
});
|
115
|
+
|
116
|
+
deadline_.expires_after(total_timeout);
|
117
|
+
deadline_.async_wait([self = shared_from_this()](std::error_code ec) {
|
118
|
+
if (ec == asio::error::operation_aborted) {
|
119
|
+
return;
|
120
|
+
}
|
121
|
+
CB_LOG_DEBUG("DNS deadline has been reached, cancelling in-flight operations (tcp.is_open={})", self->tcp_.is_open());
|
122
|
+
self->udp_.cancel();
|
123
|
+
if (self->tcp_.is_open()) {
|
124
|
+
self->tcp_.cancel();
|
125
|
+
}
|
126
|
+
});
|
127
|
+
}
|
128
|
+
|
129
|
+
private:
|
130
|
+
void retry_with_tcp()
|
131
|
+
{
|
132
|
+
if (bool expected_state{ false }; !retrying_with_tcp_.compare_exchange_strong(expected_state, true)) {
|
133
|
+
return;
|
134
|
+
}
|
135
|
+
|
136
|
+
const asio::ip::tcp::no_delay no_delay(true);
|
137
|
+
std::error_code ignore_ec;
|
138
|
+
tcp_.set_option(no_delay, ignore_ec);
|
139
|
+
const asio::ip::tcp::endpoint endpoint(address_, port_);
|
140
|
+
tcp_.async_connect(endpoint, [self = shared_from_this()](std::error_code ec1) mutable {
|
141
|
+
if (ec1) {
|
142
|
+
self->deadline_.cancel();
|
143
|
+
CB_LOG_DEBUG("DNS TCP connection has been aborted, {}", ec1.message());
|
144
|
+
return self->handler_({ ec1 });
|
145
|
+
}
|
146
|
+
auto send_size = static_cast<std::uint16_t>(self->send_buf_.size());
|
147
|
+
self->send_buf_.insert(self->send_buf_.begin(), static_cast<std::uint8_t>(send_size & 0xffU));
|
148
|
+
self->send_buf_.insert(self->send_buf_.begin(), static_cast<std::uint8_t>(send_size >> 8U));
|
149
|
+
asio::async_write(
|
150
|
+
self->tcp_, asio::buffer(self->send_buf_), [self](std::error_code ec2, std::size_t /* bytes_transferred */) mutable {
|
151
|
+
if (ec2) {
|
152
|
+
CB_LOG_DEBUG("DNS TCP write operation has been aborted, {}", ec2.message());
|
153
|
+
self->deadline_.cancel();
|
154
|
+
if (ec2 == asio::error::operation_aborted) {
|
155
|
+
ec2 = errc::common::unambiguous_timeout;
|
156
|
+
}
|
157
|
+
return self->handler_({ ec2 });
|
158
|
+
}
|
159
|
+
asio::async_read(self->tcp_,
|
160
|
+
asio::buffer(&self->recv_buf_size_, sizeof(std::uint16_t)),
|
161
|
+
[self](std::error_code ec3, std::size_t /* bytes_transferred */) mutable {
|
162
|
+
if (ec3) {
|
163
|
+
CB_LOG_DEBUG("DNS TCP buf size read operation has been aborted, {}", ec3.message());
|
164
|
+
self->deadline_.cancel();
|
165
|
+
return self->handler_({ ec3 });
|
166
|
+
}
|
167
|
+
self->recv_buf_size_ = utils::byte_swap(self->recv_buf_size_);
|
168
|
+
self->recv_buf_.resize(self->recv_buf_size_);
|
169
|
+
CB_LOG_DEBUG("DNS TCP schedule read of {} bytes", self->recv_buf_size_);
|
170
|
+
asio::async_read(self->tcp_,
|
171
|
+
asio::buffer(self->recv_buf_),
|
172
|
+
[self](std::error_code ec4, std::size_t bytes_transferred) mutable {
|
173
|
+
self->deadline_.cancel();
|
174
|
+
if (ec4) {
|
175
|
+
CB_LOG_DEBUG("DNS TCP read operation has been aborted, {}", ec4.message());
|
176
|
+
return self->handler_({ ec4 });
|
177
|
+
}
|
178
|
+
self->recv_buf_.resize(bytes_transferred);
|
179
|
+
const dns_message message = dns_codec::decode(self->recv_buf_);
|
180
|
+
dns_srv_response resp{ ec4 };
|
181
|
+
resp.targets.reserve(message.answers.size());
|
182
|
+
for (const auto& answer : message.answers) {
|
183
|
+
resp.targets.emplace_back(dns_srv_response::address{
|
184
|
+
utils::join_strings(answer.target.labels, "."), answer.port });
|
185
|
+
}
|
186
|
+
CB_LOG_DEBUG("DNS TCP returned {} records", resp.targets.size());
|
187
|
+
return self->handler_(std::move(resp));
|
188
|
+
});
|
189
|
+
});
|
190
|
+
});
|
191
|
+
});
|
192
|
+
}
|
193
|
+
|
194
|
+
asio::steady_timer deadline_;
|
195
|
+
asio::steady_timer udp_deadline_;
|
196
|
+
asio::ip::udp::socket udp_;
|
197
|
+
asio::ip::udp::endpoint udp_sender_{};
|
198
|
+
asio::ip::tcp::socket tcp_;
|
199
|
+
|
200
|
+
asio::ip::address address_;
|
201
|
+
std::uint16_t port_;
|
202
|
+
utils::movable_function<void(couchbase::core::io::dns::dns_srv_response&& resp)> handler_;
|
203
|
+
|
204
|
+
std::vector<std::uint8_t> send_buf_{};
|
205
|
+
std::uint16_t recv_buf_size_{ 0 };
|
206
|
+
std::vector<std::uint8_t> recv_buf_{};
|
207
|
+
|
208
|
+
std::atomic_bool retrying_with_tcp_{ false };
|
209
|
+
};
|
210
|
+
|
211
|
+
void
|
212
|
+
dns_client::query_srv(const std::string& name,
|
213
|
+
const std::string& service,
|
214
|
+
const dns_config& config,
|
215
|
+
utils::movable_function<void(dns_srv_response&&)>&& handler)
|
216
|
+
{
|
217
|
+
std::error_code ec;
|
218
|
+
auto address = asio::ip::address::from_string(config.nameserver(), ec);
|
219
|
+
if (ec) {
|
220
|
+
return handler({ ec });
|
221
|
+
}
|
222
|
+
auto cmd = std::make_shared<dns_srv_command>(ctx_, name, service, address, config.port(), std::move(handler));
|
223
|
+
return cmd->execute(config.timeout(), config.timeout() / 2);
|
224
|
+
}
|
225
|
+
} // namespace couchbase::core::io::dns
|