couchbase 3.0.0.alpha.5 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) 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/management/user_manager.rb +1 -1
  98. data/lib/couchbase/mutation_state.rb +1 -0
  99. data/lib/couchbase/query_options.rb +25 -2
  100. data/lib/couchbase/scope.rb +0 -7
  101. data/lib/couchbase/search_options.rb +7 -0
  102. data/lib/couchbase/version.rb +1 -1
  103. data/lib/couchbase/view_options.rb +4 -3
  104. metadata +20 -82
  105. data/.github/workflows/tests-6.0.3.yml +0 -52
  106. data/.github/workflows/tests-dev-preview.yml +0 -55
  107. data/.github/workflows/tests.yml +0 -50
  108. data/.gitignore +0 -20
  109. data/.gitmodules +0 -21
  110. data/.idea/.gitignore +0 -5
  111. data/.idea/dictionaries/gem_terms.xml +0 -18
  112. data/.idea/inspectionProfiles/Project_Default.xml +0 -8
  113. data/.idea/vcs.xml +0 -13
  114. data/bin/check-cluster +0 -31
  115. data/bin/fetch-stats +0 -19
  116. data/bin/init-cluster +0 -82
  117. data/bin/jenkins/build-extension +0 -35
  118. data/bin/jenkins/install-dependencies +0 -47
  119. data/bin/jenkins/test-with-cbdyncluster +0 -58
  120. data/bin/setup +0 -24
  121. data/ext/couchbase/configuration_monitor.hxx +0 -93
  122. data/ext/couchbase/operations/command.hxx +0 -163
  123. 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