couchbase 3.0.0.alpha.5-universal-darwin-19 → 3.0.0.beta.1-universal-darwin-19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +12 -3
  3. data/README.md +4 -2
  4. data/Rakefile +1 -1
  5. data/couchbase.gemspec +17 -12
  6. data/ext/.idea/misc.xml +12 -0
  7. data/ext/CMakeLists.txt +10 -1
  8. data/ext/build_config.hxx.in +20 -0
  9. data/ext/build_version.hxx.in +1 -1
  10. data/ext/couchbase/bucket.hxx +90 -24
  11. data/ext/couchbase/cluster.hxx +125 -84
  12. data/ext/couchbase/cluster_options.hxx +53 -0
  13. data/ext/couchbase/configuration.hxx +220 -2
  14. data/ext/couchbase/couchbase.cxx +134 -127
  15. data/ext/couchbase/io/dns_client.hxx +3 -1
  16. data/ext/couchbase/io/http_command.hxx +91 -0
  17. data/ext/couchbase/io/http_session.hxx +58 -19
  18. data/ext/couchbase/io/http_session_manager.hxx +26 -31
  19. data/ext/couchbase/io/mcbp_command.hxx +180 -0
  20. data/ext/couchbase/io/mcbp_message.hxx +5 -0
  21. data/ext/couchbase/io/mcbp_session.hxx +213 -98
  22. data/ext/couchbase/io/streams.hxx +165 -0
  23. data/ext/couchbase/operations.hxx +1 -1
  24. data/ext/couchbase/operations/analytics_dataset_create.hxx +1 -1
  25. data/ext/couchbase/operations/bucket_create.hxx +4 -2
  26. data/ext/couchbase/operations/bucket_drop.hxx +4 -2
  27. data/ext/couchbase/operations/bucket_flush.hxx +4 -2
  28. data/ext/couchbase/operations/bucket_get.hxx +4 -2
  29. data/ext/couchbase/operations/bucket_get_all.hxx +4 -2
  30. data/ext/couchbase/operations/bucket_update.hxx +4 -2
  31. data/ext/couchbase/operations/cluster_developer_preview_enable.hxx +4 -2
  32. data/ext/couchbase/operations/collection_create.hxx +4 -2
  33. data/ext/couchbase/operations/collection_drop.hxx +4 -2
  34. data/ext/couchbase/operations/document_analytics.hxx +0 -4
  35. data/ext/couchbase/operations/document_decrement.hxx +6 -3
  36. data/ext/couchbase/operations/document_get.hxx +3 -0
  37. data/ext/couchbase/operations/document_get_and_lock.hxx +3 -0
  38. data/ext/couchbase/operations/document_get_and_touch.hxx +5 -2
  39. data/ext/couchbase/operations/document_get_projected.hxx +12 -9
  40. data/ext/couchbase/operations/document_increment.hxx +6 -3
  41. data/ext/couchbase/operations/document_insert.hxx +5 -2
  42. data/ext/couchbase/operations/document_lookup_in.hxx +3 -0
  43. data/ext/couchbase/operations/document_mutate_in.hxx +6 -3
  44. data/ext/couchbase/operations/document_remove.hxx +3 -0
  45. data/ext/couchbase/operations/document_replace.hxx +5 -2
  46. data/ext/couchbase/operations/document_search.hxx +6 -7
  47. data/ext/couchbase/operations/document_touch.hxx +5 -2
  48. data/ext/couchbase/operations/document_unlock.hxx +3 -0
  49. data/ext/couchbase/operations/document_upsert.hxx +5 -2
  50. data/ext/couchbase/operations/query_index_build_deferred.hxx +3 -3
  51. data/ext/couchbase/operations/query_index_create.hxx +3 -3
  52. data/ext/couchbase/operations/query_index_drop.hxx +3 -3
  53. data/ext/couchbase/operations/query_index_get_all.hxx +3 -3
  54. data/ext/couchbase/operations/scope_create.hxx +4 -2
  55. data/ext/couchbase/operations/scope_drop.hxx +4 -2
  56. data/ext/couchbase/operations/scope_get_all.hxx +4 -2
  57. data/ext/couchbase/operations/search_index_analyze_document.hxx +2 -2
  58. data/ext/couchbase/operations/search_index_control_ingest.hxx +2 -2
  59. data/ext/couchbase/operations/search_index_control_plan_freeze.hxx +2 -2
  60. data/ext/couchbase/operations/search_index_control_query.hxx +2 -2
  61. data/ext/couchbase/operations/search_index_drop.hxx +2 -2
  62. data/ext/couchbase/operations/search_index_get.hxx +2 -2
  63. data/ext/couchbase/operations/search_index_get_all.hxx +2 -2
  64. data/ext/couchbase/operations/search_index_get_documents_count.hxx +2 -2
  65. data/ext/couchbase/operations/search_index_upsert.hxx +2 -2
  66. data/ext/couchbase/origin.hxx +148 -0
  67. data/ext/couchbase/protocol/cmd_cluster_map_change_notification.hxx +1 -6
  68. data/ext/couchbase/protocol/cmd_decrement.hxx +5 -5
  69. data/ext/couchbase/protocol/cmd_get_and_touch.hxx +5 -5
  70. data/ext/couchbase/protocol/cmd_get_cluster_config.hxx +1 -6
  71. data/ext/couchbase/protocol/cmd_increment.hxx +5 -5
  72. data/ext/couchbase/protocol/cmd_info.hxx +0 -11
  73. data/ext/couchbase/protocol/cmd_insert.hxx +5 -5
  74. data/ext/couchbase/protocol/cmd_mutate_in.hxx +6 -6
  75. data/ext/couchbase/protocol/cmd_replace.hxx +5 -5
  76. data/ext/couchbase/protocol/cmd_touch.hxx +1 -1
  77. data/ext/couchbase/protocol/cmd_upsert.hxx +5 -5
  78. data/ext/couchbase/timeout_defaults.hxx +7 -0
  79. data/ext/couchbase/utils/connection_string.hxx +139 -0
  80. data/ext/extconf.rb +44 -11
  81. data/ext/test/main.cxx +93 -15
  82. data/ext/third_party/http_parser/Makefile +160 -0
  83. data/ext/third_party/json/Makefile +77 -0
  84. data/lib/couchbase/analytics_options.rb +18 -4
  85. data/lib/couchbase/binary_collection.rb +2 -2
  86. data/lib/couchbase/binary_collection_options.rb +2 -2
  87. data/lib/couchbase/bucket.rb +4 -4
  88. data/lib/couchbase/cluster.rb +60 -46
  89. data/lib/couchbase/collection.rb +13 -13
  90. data/lib/couchbase/collection_options.rb +15 -9
  91. data/{bin/console → lib/couchbase/datastructures.rb} +4 -7
  92. data/lib/couchbase/datastructures/couchbase_list.rb +171 -0
  93. data/lib/couchbase/datastructures/couchbase_map.rb +205 -0
  94. data/lib/couchbase/datastructures/couchbase_queue.rb +145 -0
  95. data/lib/couchbase/datastructures/couchbase_set.rb +138 -0
  96. data/lib/couchbase/errors.rb +66 -63
  97. data/lib/couchbase/libcouchbase.bundle +0 -0
  98. data/lib/couchbase/management/user_manager.rb +1 -1
  99. data/lib/couchbase/mutation_state.rb +1 -0
  100. data/lib/couchbase/query_options.rb +25 -2
  101. data/lib/couchbase/scope.rb +0 -7
  102. data/lib/couchbase/search_options.rb +7 -0
  103. data/lib/couchbase/version.rb +1 -1
  104. data/lib/couchbase/view_options.rb +4 -3
  105. metadata +20 -82
  106. data/.github/workflows/tests-6.0.3.yml +0 -52
  107. data/.github/workflows/tests-dev-preview.yml +0 -55
  108. data/.github/workflows/tests.yml +0 -50
  109. data/.gitignore +0 -20
  110. data/.gitmodules +0 -21
  111. data/.idea/.gitignore +0 -5
  112. data/.idea/dictionaries/gem_terms.xml +0 -18
  113. data/.idea/inspectionProfiles/Project_Default.xml +0 -8
  114. data/.idea/vcs.xml +0 -13
  115. data/bin/check-cluster +0 -31
  116. data/bin/fetch-stats +0 -19
  117. data/bin/init-cluster +0 -82
  118. data/bin/jenkins/build-extension +0 -35
  119. data/bin/jenkins/install-dependencies +0 -47
  120. data/bin/jenkins/test-with-cbdyncluster +0 -58
  121. data/bin/setup +0 -24
  122. data/ext/couchbase/configuration_monitor.hxx +0 -93
  123. data/ext/couchbase/operations/command.hxx +0 -163
  124. data/rbi/couchbase.rbi +0 -79
@@ -119,7 +119,9 @@ class dns_client
119
119
  return;
120
120
  }
121
121
  self->udp_.cancel();
122
- self->tcp_.cancel();
122
+ if (self->tcp_.is_open()) {
123
+ self->tcp_.cancel();
124
+ }
123
125
  });
124
126
  }
125
127
 
@@ -0,0 +1,91 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020 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
+ #pragma once
19
+
20
+ #include <io/http_session.hxx>
21
+
22
+ namespace couchbase::operations
23
+ {
24
+
25
+ template<typename Request>
26
+ struct http_command : public std::enable_shared_from_this<http_command<Request>> {
27
+ using encoded_request_type = typename Request::encoded_request_type;
28
+ using encoded_response_type = typename Request::encoded_response_type;
29
+ asio::steady_timer deadline;
30
+ asio::steady_timer retry_backoff;
31
+ Request request;
32
+ encoded_request_type encoded;
33
+
34
+ http_command(asio::io_context& ctx, Request req)
35
+ : deadline(ctx)
36
+ , retry_backoff(ctx)
37
+ , request(req)
38
+ {
39
+ }
40
+
41
+ template<typename Handler>
42
+ void send_to(std::shared_ptr<io::http_session> session, Handler&& handler)
43
+ {
44
+ encoded.type = Request::type;
45
+ request.encode_to(encoded);
46
+ encoded.headers["client-context-id"] = request.client_context_id;
47
+ auto log_prefix = session->log_prefix();
48
+ spdlog::debug("{} HTTP request: {}, method={}, path={}, client_context_id={}, timeout={}ms",
49
+ log_prefix,
50
+ encoded.type,
51
+ encoded.method,
52
+ encoded.path,
53
+ request.client_context_id,
54
+ request.timeout.count());
55
+ SPDLOG_TRACE("{} HTTP request: {}, method={}, path={}, client_context_id={}, timeout={}ms{:a}",
56
+ log_prefix,
57
+ encoded.type,
58
+ encoded.method,
59
+ encoded.path,
60
+ request.client_context_id,
61
+ request.timeout.count(),
62
+ spdlog::to_hex(encoded.body));
63
+ session->write_and_subscribe(encoded,
64
+ [self = this->shared_from_this(), log_prefix, handler = std::forward<Handler>(handler)](
65
+ std::error_code ec, io::http_response&& msg) mutable {
66
+ self->deadline.cancel();
67
+ encoded_response_type resp(msg);
68
+ spdlog::debug("{} HTTP response: {}, client_context_id={}, status={}",
69
+ log_prefix,
70
+ self->request.type,
71
+ self->request.client_context_id,
72
+ resp.status_code);
73
+ SPDLOG_TRACE("{} HTTP response: {}, client_context_id={}, status={}{:a}",
74
+ log_prefix,
75
+ self->request.type,
76
+ self->request.client_context_id,
77
+ resp.status_code,
78
+ spdlog::to_hex(resp.body));
79
+ handler(make_response(ec, self->request, resp));
80
+ });
81
+ deadline.expires_after(request.timeout);
82
+ deadline.async_wait([session](std::error_code ec) {
83
+ if (ec == asio::error::operation_aborted) {
84
+ return;
85
+ }
86
+ session->stop();
87
+ });
88
+ }
89
+ };
90
+
91
+ } // namespace couchbase::operations
@@ -51,8 +51,7 @@ class http_session : public std::enable_shared_from_this<http_session>
51
51
  , id_(uuid::to_string(uuid::random()))
52
52
  , ctx_(ctx)
53
53
  , resolver_(ctx_)
54
- , strand_(asio::make_strand(ctx_))
55
- , socket_(strand_)
54
+ , stream_(std::make_unique<plain_stream_impl>(ctx_))
56
55
  , deadline_timer_(ctx_)
57
56
  , username_(username)
58
57
  , password_(password)
@@ -66,10 +65,37 @@ class http_session : public std::enable_shared_from_this<http_session>
66
65
  client_id_,
67
66
  id_,
68
67
  BACKEND_SYSTEM))
68
+ , log_prefix_(fmt::format("[{}/{}]", client_id_, id_))
69
+ {
70
+ }
71
+
72
+ http_session(const std::string& client_id,
73
+ asio::io_context& ctx,
74
+ asio::ssl::context& tls,
75
+ const std::string& username,
76
+ const std::string& password,
77
+ const std::string& hostname,
78
+ const std::string& service)
79
+ : client_id_(client_id)
80
+ , id_(uuid::to_string(uuid::random()))
81
+ , ctx_(ctx)
82
+ , resolver_(ctx_)
83
+ , stream_(std::make_unique<tls_stream_impl>(ctx_, tls))
84
+ , deadline_timer_(ctx_)
85
+ , username_(username)
86
+ , password_(password)
87
+ , hostname_(hostname)
88
+ , service_(service)
89
+ , user_agent_(fmt::format("ruby/{}.{}.{}/{}; client/{}; session/{}; {}",
90
+ BACKEND_VERSION_MAJOR,
91
+ BACKEND_VERSION_MINOR,
92
+ BACKEND_VERSION_PATCH,
93
+ BACKEND_GIT_REVISION,
94
+ client_id_,
95
+ id_,
96
+ BACKEND_SYSTEM))
97
+ , log_prefix_(fmt::format("[{}/{}]", client_id_, id_))
69
98
  {
70
- log_prefix_ = fmt::format("[{}/{}]", client_id_, id_);
71
- resolver_.async_resolve(
72
- hostname, service, std::bind(&http_session::on_resolve, this, std::placeholders::_1, std::placeholders::_2));
73
99
  }
74
100
 
75
101
  ~http_session()
@@ -77,6 +103,12 @@ class http_session : public std::enable_shared_from_this<http_session>
77
103
  stop();
78
104
  }
79
105
 
106
+ void start()
107
+ {
108
+ resolver_.async_resolve(
109
+ hostname_, service_, std::bind(&http_session::on_resolve, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
110
+ }
111
+
80
112
  [[nodiscard]] const std::string& log_prefix() const
81
113
  {
82
114
  return log_prefix_;
@@ -100,8 +132,8 @@ class http_session : public std::enable_shared_from_this<http_session>
100
132
  void stop()
101
133
  {
102
134
  stopped_ = true;
103
- if (socket_.is_open()) {
104
- socket_.close();
135
+ if (stream_->is_open()) {
136
+ stream_->close();
105
137
  }
106
138
  deadline_timer_.cancel();
107
139
 
@@ -116,6 +148,11 @@ class http_session : public std::enable_shared_from_this<http_session>
116
148
  }
117
149
  }
118
150
 
151
+ bool keep_alive()
152
+ {
153
+ return keep_alive_;
154
+ }
155
+
119
156
  bool is_stopped()
120
157
  {
121
158
  return stopped_;
@@ -177,7 +214,7 @@ class http_session : public std::enable_shared_from_this<http_session>
177
214
  }
178
215
  endpoints_ = endpoints;
179
216
  do_connect(endpoints_.begin());
180
- deadline_timer_.async_wait(std::bind(&http_session::check_deadline, this, std::placeholders::_1));
217
+ deadline_timer_.async_wait(std::bind(&http_session::check_deadline, shared_from_this(), std::placeholders::_1));
181
218
  }
182
219
 
183
220
  void do_connect(asio::ip::tcp::resolver::results_type::iterator it)
@@ -185,7 +222,7 @@ class http_session : public std::enable_shared_from_this<http_session>
185
222
  if (it != endpoints_.end()) {
186
223
  spdlog::debug("{} connecting to {}:{}", log_prefix_, it->endpoint().address().to_string(), it->endpoint().port());
187
224
  deadline_timer_.expires_after(timeout_defaults::connect_timeout);
188
- socket_.async_connect(it->endpoint(), std::bind(&http_session::on_connect, this, std::placeholders::_1, it));
225
+ stream_->async_connect(it->endpoint(), std::bind(&http_session::on_connect, shared_from_this(), std::placeholders::_1, it));
189
226
  } else {
190
227
  spdlog::error("{} no more endpoints left to connect", log_prefix_);
191
228
  stop();
@@ -197,7 +234,9 @@ class http_session : public std::enable_shared_from_this<http_session>
197
234
  if (stopped_) {
198
235
  return;
199
236
  }
200
- if (!socket_.is_open() || ec) {
237
+ if (!stream_->is_open() || ec) {
238
+ spdlog::warn(
239
+ "{} unable to connect to {}:{}: {}", log_prefix_, it->endpoint().address().to_string(), it->endpoint().port(), ec.message());
201
240
  do_connect(++it);
202
241
  } else {
203
242
  connected_ = true;
@@ -219,10 +258,10 @@ class http_session : public std::enable_shared_from_this<http_session>
219
258
  return;
220
259
  }
221
260
  if (deadline_timer_.expiry() <= asio::steady_timer::clock_type::now()) {
222
- socket_.close();
261
+ stream_->close();
223
262
  deadline_timer_.expires_at(asio::steady_timer::time_point::max());
224
263
  }
225
- deadline_timer_.async_wait(std::bind(&http_session::check_deadline, this, std::placeholders::_1));
264
+ deadline_timer_.async_wait(std::bind(&http_session::check_deadline, shared_from_this(), std::placeholders::_1));
226
265
  }
227
266
 
228
267
  void do_read()
@@ -230,12 +269,12 @@ class http_session : public std::enable_shared_from_this<http_session>
230
269
  if (stopped_) {
231
270
  return;
232
271
  }
233
- socket_.async_read_some(
272
+ stream_->async_read_some(
234
273
  asio::buffer(input_buffer_), [self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
235
- if (self->stopped_) {
274
+ if (ec == asio::error::operation_aborted || self->stopped_) {
236
275
  return;
237
276
  }
238
- if (ec && ec != asio::error::operation_aborted) {
277
+ if (ec) {
239
278
  spdlog::error("{} IO error while reading from the socket: {}", self->log_prefix_, ec.message());
240
279
  return self->stop();
241
280
  }
@@ -273,8 +312,8 @@ class http_session : public std::enable_shared_from_this<http_session>
273
312
  for (auto& buf : writing_buffer_) {
274
313
  buffers.emplace_back(asio::buffer(buf));
275
314
  }
276
- asio::async_write(socket_, buffers, [self = shared_from_this()](std::error_code ec, std::size_t /* bytes_transferred */) {
277
- if (self->stopped_) {
315
+ stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t /* bytes_transferred */) {
316
+ if (ec == asio::error::operation_aborted || self->stopped_) {
278
317
  return;
279
318
  }
280
319
  if (ec) {
@@ -293,8 +332,7 @@ class http_session : public std::enable_shared_from_this<http_session>
293
332
  std::string id_;
294
333
  asio::io_context& ctx_;
295
334
  asio::ip::tcp::resolver resolver_;
296
- asio::strand<asio::io_context::executor_type> strand_;
297
- asio::ip::tcp::socket socket_;
335
+ std::unique_ptr<stream_impl> stream_;
298
336
  asio::steady_timer deadline_timer_;
299
337
 
300
338
  std::string username_;
@@ -305,6 +343,7 @@ class http_session : public std::enable_shared_from_this<http_session>
305
343
 
306
344
  bool stopped_{ false };
307
345
  bool connected_{ false };
346
+ bool keep_alive_{ false };
308
347
 
309
348
  std::function<void()> on_stop_handler_{ nullptr };
310
349
 
@@ -28,14 +28,16 @@ namespace couchbase::io
28
28
  class http_session_manager : public std::enable_shared_from_this<http_session_manager>
29
29
  {
30
30
  public:
31
- http_session_manager(const std::string& client_id, asio::io_context& ctx)
31
+ http_session_manager(const std::string& client_id, asio::io_context& ctx, asio::ssl::context& tls)
32
32
  : client_id_(client_id)
33
33
  , ctx_(ctx)
34
+ , tls_(tls)
34
35
  {
35
36
  }
36
37
 
37
- void set_configuration(const configuration& config)
38
+ void set_configuration(const configuration& config, const cluster_options& options)
38
39
  {
40
+ options_ = options;
39
41
  config_ = config;
40
42
  next_index_ = 0;
41
43
  if (config_.nodes.size() > 1) {
@@ -48,6 +50,7 @@ class http_session_manager : public std::enable_shared_from_this<http_session_ma
48
50
 
49
51
  std::shared_ptr<http_session> check_out(service_type type, const std::string& username, const std::string& password)
50
52
  {
53
+ std::scoped_lock lock(sessions_mutex_);
51
54
  if (idle_sessions_[type].empty()) {
52
55
  std::string hostname;
53
56
  std::uint16_t port = 0;
@@ -56,8 +59,15 @@ class http_session_manager : public std::enable_shared_from_this<http_session_ma
56
59
  return nullptr;
57
60
  }
58
61
  config_.nodes.size();
59
- auto session = std::make_shared<http_session>(client_id_, ctx_, username, password, hostname, std::to_string(port));
62
+ std::shared_ptr<http_session> session;
63
+ if (options_.enable_tls) {
64
+ session = std::make_shared<http_session>(client_id_, ctx_, tls_, username, password, hostname, std::to_string(port));
65
+ } else {
66
+ session = std::make_shared<http_session>(client_id_, ctx_, username, password, hostname, std::to_string(port));
67
+ }
68
+ session->start();
60
69
  session->on_stop([type, id = session->id(), self = this->shared_from_this()]() {
70
+ std::scoped_lock inner_lock(self->sessions_mutex_);
61
71
  self->busy_sessions_[type].remove_if([id](const auto& s) -> bool { return s->id() == id; });
62
72
  self->idle_sessions_[type].remove_if([id](const auto& s) -> bool { return s->id() == id; });
63
73
  });
@@ -72,7 +82,14 @@ class http_session_manager : public std::enable_shared_from_this<http_session_ma
72
82
 
73
83
  void check_in(service_type type, std::shared_ptr<http_session> session)
74
84
  {
75
- idle_sessions_[type].push_back(session);
85
+ if (!session->keep_alive()) {
86
+ return session->stop();
87
+ }
88
+ if (!session->is_stopped()) {
89
+ std::scoped_lock lock(sessions_mutex_);
90
+ spdlog::debug("{} put HTTP session back to idle connections", session->log_prefix());
91
+ idle_sessions_[type].push_back(session);
92
+ }
76
93
  }
77
94
 
78
95
  private:
@@ -83,34 +100,9 @@ class http_session_manager : public std::enable_shared_from_this<http_session_ma
83
100
  --candidates;
84
101
  auto& node = config_.nodes[next_index_];
85
102
  next_index_ = (next_index_ + 1) % config_.nodes.size();
86
- std::uint16_t port = 0;
87
- switch (type) {
88
- case service_type::query:
89
- port = node.services_plain.query.value_or(0);
90
- break;
91
-
92
- case service_type::analytics:
93
- port = node.services_plain.analytics.value_or(0);
94
- break;
95
-
96
- case service_type::search:
97
- port = node.services_plain.search.value_or(0);
98
- break;
99
-
100
- case service_type::views:
101
- port = node.services_plain.views.value_or(0);
102
- break;
103
-
104
- case service_type::management:
105
- port = node.services_plain.management.value_or(0);
106
- break;
107
-
108
- case service_type::kv:
109
- port = node.services_plain.key_value.value_or(0);
110
- break;
111
- }
103
+ std::uint16_t port = node.port_or(options_.network, type, options_.enable_tls, 0);
112
104
  if (port != 0) {
113
- return { node.hostname, port };
105
+ return { node.hostname_for(options_.network), port };
114
106
  }
115
107
  }
116
108
  return { "", 0 };
@@ -118,10 +110,13 @@ class http_session_manager : public std::enable_shared_from_this<http_session_ma
118
110
 
119
111
  std::string client_id_;
120
112
  asio::io_context& ctx_;
113
+ asio::ssl::context& tls_;
114
+ cluster_options options_;
121
115
 
122
116
  configuration config_{};
123
117
  std::map<service_type, std::list<std::shared_ptr<http_session>>> busy_sessions_{};
124
118
  std::map<service_type, std::list<std::shared_ptr<http_session>>> idle_sessions_{};
125
119
  std::size_t next_index_{ 0 };
120
+ std::mutex sessions_mutex_{};
126
121
  };
127
122
  } // namespace couchbase::io
@@ -0,0 +1,180 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020 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
+ #pragma once
19
+
20
+ #include <io/mcbp_session.hxx>
21
+ #include <protocol/cmd_get_collection_id.hxx>
22
+ #include <functional>
23
+ #include <utility>
24
+
25
+ namespace couchbase::operations
26
+ {
27
+
28
+ using mcbp_command_handler = std::function<void(std::error_code, std::optional<io::mcbp_message>)>;
29
+
30
+ template<typename Request>
31
+ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Request>> {
32
+ using encoded_request_type = typename Request::encoded_request_type;
33
+ using encoded_response_type = typename Request::encoded_response_type;
34
+ asio::steady_timer deadline;
35
+ asio::steady_timer retry_backoff;
36
+ Request request;
37
+ encoded_request_type encoded;
38
+ std::optional<std::uint32_t> opaque_{};
39
+ std::shared_ptr<io::mcbp_session> session_{};
40
+ mcbp_command_handler handler_{};
41
+
42
+ mcbp_command(asio::io_context& ctx, Request req)
43
+ : deadline(ctx)
44
+ , retry_backoff(ctx)
45
+ , request(req)
46
+ {
47
+ }
48
+
49
+ void start(mcbp_command_handler&& handler)
50
+ {
51
+ handler_ = handler;
52
+ deadline.expires_after(request.timeout);
53
+ deadline.async_wait([self = this->shared_from_this()](std::error_code ec) {
54
+ if (ec == asio::error::operation_aborted) {
55
+ return;
56
+ }
57
+ self->cancel();
58
+ });
59
+ }
60
+
61
+ void cancel()
62
+ {
63
+ if (opaque_ && session_) {
64
+ session_->cancel(opaque_.value(), asio::error::operation_aborted);
65
+ }
66
+ handler_ = nullptr;
67
+ }
68
+
69
+ void invoke_handler(std::error_code ec, std::optional<io::mcbp_message> msg = {})
70
+ {
71
+ if (handler_) {
72
+ handler_(ec, std::move(msg));
73
+ }
74
+ handler_ = nullptr;
75
+ }
76
+
77
+ void request_collection_id()
78
+ {
79
+ protocol::client_request<protocol::get_collection_id_request_body> req;
80
+ req.opaque(session_->next_opaque());
81
+ req.body().collection_path(request.id.collection);
82
+ session_->write_and_subscribe(req.opaque(),
83
+ req.data(session_->supports_feature(protocol::hello_feature::snappy)),
84
+ [self = this->shared_from_this()](std::error_code ec, io::mcbp_message&& msg) mutable {
85
+ if (ec == asio::error::operation_aborted) {
86
+ return self->invoke_handler(std::make_error_code(error::common_errc::ambiguous_timeout));
87
+ }
88
+ if (ec == std::make_error_code(error::common_errc::collection_not_found)) {
89
+ if (self->request.id.collection_uid) {
90
+ return self->handle_unknown_collection();
91
+ }
92
+ return self->invoke_handler(ec);
93
+ }
94
+ if (ec) {
95
+ return self->invoke_handler(ec);
96
+ }
97
+ protocol::client_response<protocol::get_collection_id_response_body> resp(msg);
98
+ self->session_->update_collection_uid(self->request.id.collection, resp.body().collection_uid());
99
+ self->request.id.collection_uid = resp.body().collection_uid();
100
+ return self->send();
101
+ });
102
+ }
103
+
104
+ void handle_unknown_collection()
105
+ {
106
+ auto backoff = std::chrono::milliseconds(500);
107
+ auto time_left = deadline.expiry() - std::chrono::steady_clock::now();
108
+ spdlog::debug("{} unknown collection response for \"{}/{}/{}\", time_left={}ms",
109
+ session_->log_prefix(),
110
+ request.id.bucket,
111
+ request.id.collection,
112
+ request.id.key,
113
+ std::chrono::duration_cast<std::chrono::milliseconds>(time_left).count());
114
+ if (time_left < backoff) {
115
+ return invoke_handler(std::make_error_code(error::common_errc::ambiguous_timeout));
116
+ }
117
+ retry_backoff.expires_after(backoff);
118
+ retry_backoff.async_wait([self = this->shared_from_this()](std::error_code ec) mutable {
119
+ if (ec == asio::error::operation_aborted) {
120
+ return;
121
+ }
122
+ self->request_collection_id();
123
+ });
124
+ }
125
+
126
+ void send()
127
+ {
128
+ opaque_ = session_->next_opaque();
129
+ request.opaque = *opaque_;
130
+ if (!request.id.collection_uid) {
131
+ if (session_->supports_feature(protocol::hello_feature::collections)) {
132
+ auto collection_id = session_->get_collection_uid(request.id.collection);
133
+ if (collection_id) {
134
+ request.id.collection_uid = *collection_id;
135
+ } else {
136
+ spdlog::debug("{} no cache entry for collection, resolve collection id for \"{}/{}/{}\", timeout={}ms",
137
+ session_->log_prefix(),
138
+ request.id.bucket,
139
+ request.id.collection,
140
+ request.id.key,
141
+ request.timeout.count());
142
+ return request_collection_id();
143
+ }
144
+ } else {
145
+ if (!request.id.collection.empty() && request.id.collection != "_default._default") {
146
+ return invoke_handler(std::make_error_code(error::common_errc::unsupported_operation));
147
+ }
148
+ }
149
+ }
150
+ request.encode_to(encoded);
151
+
152
+ session_->write_and_subscribe(request.opaque,
153
+ encoded.data(session_->supports_feature(protocol::hello_feature::snappy)),
154
+ [self = this->shared_from_this()](std::error_code ec, io::mcbp_message&& msg) mutable {
155
+ self->retry_backoff.cancel();
156
+ if (ec == asio::error::operation_aborted) {
157
+ return self->invoke_handler(std::make_error_code(error::common_errc::ambiguous_timeout));
158
+ }
159
+ if (ec == std::make_error_code(error::common_errc::request_canceled)) {
160
+ return self->invoke_handler(ec);
161
+ }
162
+ if (msg.header.status() == static_cast<std::uint16_t>(protocol::status::unknown_collection)) {
163
+ return self->handle_unknown_collection();
164
+ }
165
+ self->deadline.cancel();
166
+ self->invoke_handler(ec, msg);
167
+ });
168
+ }
169
+
170
+ void send_to(std::shared_ptr<io::mcbp_session> session)
171
+ {
172
+ if (!handler_) {
173
+ return;
174
+ }
175
+ session_ = std::move(session);
176
+ send();
177
+ }
178
+ };
179
+
180
+ } // namespace couchbase::operations