couchbase 3.4.1 → 3.4.2
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 +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
|