couchbase 3.0.0.alpha.2 → 3.0.0.alpha.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/tests-dev-preview.yml +52 -0
  3. data/.gitmodules +3 -0
  4. data/.idea/vcs.xml +1 -0
  5. data/.yardopts +1 -0
  6. data/README.md +1 -1
  7. data/Rakefile +5 -1
  8. data/bin/init-cluster +13 -5
  9. data/couchbase.gemspec +2 -1
  10. data/examples/managing_query_indexes.rb +1 -1
  11. data/examples/managing_search_indexes.rb +62 -0
  12. data/examples/search.rb +187 -0
  13. data/ext/.clang-tidy +1 -0
  14. data/ext/build_version.hxx.in +1 -1
  15. data/ext/couchbase/bucket.hxx +0 -40
  16. data/ext/couchbase/couchbase.cxx +2578 -1368
  17. data/ext/couchbase/io/http_session.hxx +27 -7
  18. data/ext/couchbase/io/mcbp_parser.hxx +2 -0
  19. data/ext/couchbase/io/mcbp_session.hxx +53 -24
  20. data/ext/couchbase/io/session_manager.hxx +6 -1
  21. data/ext/couchbase/operations.hxx +13 -0
  22. data/ext/couchbase/operations/bucket_create.hxx +1 -0
  23. data/ext/couchbase/operations/bucket_drop.hxx +1 -0
  24. data/ext/couchbase/operations/bucket_flush.hxx +1 -0
  25. data/ext/couchbase/operations/bucket_get.hxx +1 -0
  26. data/ext/couchbase/operations/bucket_get_all.hxx +1 -0
  27. data/ext/couchbase/operations/bucket_update.hxx +1 -0
  28. data/ext/couchbase/operations/cluster_developer_preview_enable.hxx +1 -0
  29. data/ext/couchbase/operations/collection_create.hxx +6 -1
  30. data/ext/couchbase/operations/collection_drop.hxx +1 -0
  31. data/ext/couchbase/operations/command.hxx +86 -11
  32. data/ext/couchbase/operations/document_decrement.hxx +1 -0
  33. data/ext/couchbase/operations/document_exists.hxx +1 -0
  34. data/ext/couchbase/operations/document_get.hxx +1 -0
  35. data/ext/couchbase/operations/document_get_and_lock.hxx +1 -0
  36. data/ext/couchbase/operations/document_get_and_touch.hxx +1 -0
  37. data/ext/couchbase/operations/document_get_projected.hxx +243 -0
  38. data/ext/couchbase/operations/document_increment.hxx +4 -1
  39. data/ext/couchbase/operations/document_insert.hxx +1 -0
  40. data/ext/couchbase/operations/document_lookup_in.hxx +1 -0
  41. data/ext/couchbase/operations/document_mutate_in.hxx +1 -0
  42. data/ext/couchbase/operations/document_query.hxx +13 -2
  43. data/ext/couchbase/operations/document_remove.hxx +1 -0
  44. data/ext/couchbase/operations/document_replace.hxx +1 -0
  45. data/ext/couchbase/operations/document_search.hxx +337 -0
  46. data/ext/couchbase/operations/document_touch.hxx +1 -0
  47. data/ext/couchbase/operations/document_unlock.hxx +1 -0
  48. data/ext/couchbase/operations/document_upsert.hxx +1 -0
  49. data/ext/couchbase/operations/query_index_build_deferred.hxx +1 -0
  50. data/ext/couchbase/operations/query_index_create.hxx +1 -0
  51. data/ext/couchbase/operations/query_index_drop.hxx +1 -0
  52. data/ext/couchbase/operations/query_index_get_all.hxx +1 -0
  53. data/ext/couchbase/operations/scope_create.hxx +1 -0
  54. data/ext/couchbase/operations/scope_drop.hxx +1 -0
  55. data/ext/couchbase/operations/scope_get_all.hxx +2 -0
  56. data/ext/couchbase/operations/search_index.hxx +62 -0
  57. data/ext/couchbase/operations/search_index_analyze_document.hxx +92 -0
  58. data/ext/couchbase/operations/search_index_control_ingest.hxx +78 -0
  59. data/ext/couchbase/operations/search_index_control_plan_freeze.hxx +80 -0
  60. data/ext/couchbase/operations/search_index_control_query.hxx +80 -0
  61. data/ext/couchbase/operations/search_index_drop.hxx +77 -0
  62. data/ext/couchbase/operations/search_index_get.hxx +80 -0
  63. data/ext/couchbase/operations/search_index_get_all.hxx +82 -0
  64. data/ext/couchbase/operations/search_index_get_documents_count.hxx +81 -0
  65. data/ext/couchbase/operations/search_index_upsert.hxx +106 -0
  66. data/ext/couchbase/protocol/client_opcode.hxx +10 -0
  67. data/ext/couchbase/protocol/cmd_get_collection_id.hxx +117 -0
  68. data/ext/couchbase/timeout_defaults.hxx +32 -0
  69. data/ext/couchbase/version.hxx +1 -1
  70. data/ext/test/main.cxx +5 -5
  71. data/lib/couchbase/binary_collection.rb +16 -12
  72. data/lib/couchbase/binary_collection_options.rb +4 -0
  73. data/lib/couchbase/cluster.rb +88 -8
  74. data/lib/couchbase/collection.rb +39 -15
  75. data/lib/couchbase/collection_options.rb +19 -2
  76. data/lib/couchbase/json_transcoder.rb +2 -2
  77. data/lib/couchbase/management/bucket_manager.rb +37 -23
  78. data/lib/couchbase/management/collection_manager.rb +15 -6
  79. data/lib/couchbase/management/query_index_manager.rb +16 -6
  80. data/lib/couchbase/management/search_index_manager.rb +61 -14
  81. data/lib/couchbase/search_options.rb +1492 -0
  82. data/lib/couchbase/version.rb +1 -1
  83. metadata +22 -2
@@ -33,6 +33,7 @@
33
33
  #include <io/http_parser.hxx>
34
34
  #include <io/http_message.hxx>
35
35
  #include <platform/base64.h>
36
+ #include <timeout_defaults.hxx>
36
37
 
37
38
  namespace couchbase::io
38
39
  {
@@ -73,9 +74,14 @@ class http_session : public std::enable_shared_from_this
73
74
  stop();
74
75
  }
75
76
 
76
- [[nodiscard]] std::string id()
77
+ [[nodiscard]] uuid::uuid_t id()
77
78
  {
78
- return uuid::to_string(id_);
79
+ return id_;
80
+ }
81
+
82
+ void on_stop(std::function<void()> handler)
83
+ {
84
+ on_stop_handler_ = std::move(handler);
79
85
  }
80
86
 
81
87
  void stop()
@@ -85,6 +91,16 @@ class http_session : public std::enable_shared_from_this
85
91
  socket_.close();
86
92
  }
87
93
  deadline_timer_.cancel();
94
+
95
+ for (auto& handler : command_handlers_) {
96
+ handler(std::make_error_code(error::common_errc::ambiguous_timeout), {});
97
+ }
98
+ command_handlers_.clear();
99
+
100
+ if (on_stop_handler_) {
101
+ on_stop_handler_();
102
+ on_stop_handler_ = nullptr;
103
+ }
88
104
  }
89
105
 
90
106
  bool is_stopped()
@@ -105,7 +121,7 @@ class http_session : public std::enable_shared_from_this
105
121
  if (stopped_) {
106
122
  return;
107
123
  }
108
- output_buffer_.emplace_back(buf.begin(), buf.end());
124
+ output_buffer_.emplace_back(std::vector<uint8_t>{ buf.begin(), buf.end() });
109
125
  }
110
126
 
111
127
  void flush()
@@ -155,7 +171,7 @@ class http_session : public std::enable_shared_from_this
155
171
  {
156
172
  if (it != endpoints_.end()) {
157
173
  spdlog::trace("connecting to {}:{}", it->endpoint().address().to_string(), it->endpoint().port());
158
- deadline_timer_.expires_after(std::chrono::seconds(10));
174
+ deadline_timer_.expires_after(timeout_defaults::connect_timeout);
159
175
  socket_.async_connect(it->endpoint(), std::bind(&http_session::on_connect, this, std::placeholders::_1, it));
160
176
  } else {
161
177
  spdlog::error("no more endpoints left to connect");
@@ -213,9 +229,11 @@ class http_session : public std::enable_shared_from_this
213
229
  switch (self->parser_.feed(reinterpret_cast<const char*>(self->input_buffer_.data()), bytes_transferred)) {
214
230
  case http_parser::status::ok:
215
231
  if (self->parser_.complete) {
216
- auto handler = self->command_handlers_.front();
217
- self->command_handlers_.pop_front();
218
- handler({}, std::move(self->parser_.response));
232
+ if (!self->command_handlers_.empty()) {
233
+ auto handler = self->command_handlers_.front();
234
+ self->command_handlers_.pop_front();
235
+ handler({}, std::move(self->parser_.response));
236
+ }
219
237
  self->parser_.reset();
220
238
  return;
221
239
  }
@@ -273,6 +291,8 @@ class http_session : public std::enable_shared_from_this
273
291
  bool stopped_{ false };
274
292
  bool connected_{ false };
275
293
 
294
+ std::function<void()> on_stop_handler_{ nullptr };
295
+
276
296
  std::list<std::function<void(std::error_code, io::http_response&&)>> command_handlers_{};
277
297
  http_parser parser_{};
278
298
  std::array<std::uint8_t, 16384> input_buffer_{};
@@ -73,6 +73,8 @@ struct mcbp_parser {
73
73
  if (success) {
74
74
  std::copy(uncompressed.begin(), uncompressed.end(), std::back_inserter(msg.body));
75
75
  use_raw_value = false;
76
+ // patch header with new body size
77
+ msg.header.bodylen = htonl(static_cast<std::uint32_t>(prefix_size + uncompressed.size()));
76
78
  }
77
79
  }
78
80
  if (use_raw_value) {
@@ -28,6 +28,8 @@
28
28
  #include <io/mcbp_message.hxx>
29
29
  #include <io/mcbp_parser.hxx>
30
30
 
31
+ #include <timeout_defaults.hxx>
32
+
31
33
  #include <protocol/hello_feature.hxx>
32
34
  #include <protocol/client_request.hxx>
33
35
  #include <protocol/client_response.hxx>
@@ -39,7 +41,6 @@
39
41
  #include <protocol/cmd_select_bucket.hxx>
40
42
  #include <protocol/cmd_get_cluster_config.hxx>
41
43
  #include <protocol/cmd_get_error_map.hxx>
42
- #include <protocol/cmd_get_collections_manifest.hxx>
43
44
  #include <protocol/cmd_get.hxx>
44
45
  #include <protocol/cmd_cluster_map_change_notification.hxx>
45
46
 
@@ -55,6 +56,35 @@ namespace couchbase::io
55
56
 
56
57
  class mcbp_session : public std::enable_shared_from_this<mcbp_session>
57
58
  {
59
+ class collection_cache
60
+ {
61
+ private:
62
+ std::map<std::string, std::uint32_t> cid_map_{ { "_default._default", 0 } };
63
+
64
+ public:
65
+ [[nodiscard]] std::optional<std::uint32_t> get(const std::string& path)
66
+ {
67
+ Expects(!path.empty());
68
+ auto ptr = cid_map_.find(path);
69
+ if (ptr != cid_map_.end()) {
70
+ return ptr->second;
71
+ }
72
+ return {};
73
+ }
74
+
75
+ void update(const std::string& path, std::uint32_t id)
76
+ {
77
+ Expects(!path.empty());
78
+ cid_map_[path] = id;
79
+ }
80
+
81
+ void reset()
82
+ {
83
+ cid_map_.clear();
84
+ cid_map_["_default._default"] = 0;
85
+ }
86
+ };
87
+
58
88
  class message_handler
59
89
  {
60
90
  public:
@@ -138,10 +168,6 @@ class mcbp_session : public std::enable_shared_from_this
138
168
  sb_req.opaque(session_->next_opaque());
139
169
  sb_req.body().bucket_name(session_->bucket_name_.value());
140
170
  session_->write(sb_req.data());
141
-
142
- protocol::client_request<protocol::get_collections_manifest_request_body> gcm_req;
143
- gcm_req.opaque(session_->next_opaque());
144
- session_->write(gcm_req.data());
145
171
  }
146
172
  protocol::client_request<protocol::get_cluster_config_request_body> cfg_req;
147
173
  cfg_req.opaque(session_->next_opaque());
@@ -216,21 +242,6 @@ class mcbp_session : public std::enable_shared_from_this
216
242
  return complete(std::make_error_code(error::network_errc::protocol_error));
217
243
  }
218
244
  } break;
219
- case protocol::client_opcode::get_collections_manifest: {
220
- protocol::client_response<protocol::get_collections_manifest_response_body> resp(msg);
221
- if (resp.status() == protocol::status::success) {
222
- session_->manifest_.emplace(resp.body().manifest());
223
- spdlog::trace(
224
- "collections manifest for bucket \"{}\": {}", session_->bucket_name_.value_or(""), *session_->manifest_);
225
- } else if (resp.status() == protocol::status::no_collections_manifest) {
226
- spdlog::trace("collection manifest is not available for bucket \"{}\": {}",
227
- session_->bucket_name_.value_or(""),
228
- resp.error_message());
229
- } else {
230
- spdlog::warn("unexpected message status during bootstrap: {} (opcode={})", resp.error_message(), opcode);
231
- return complete(std::make_error_code(error::network_errc::protocol_error));
232
- }
233
- } break;
234
245
  case protocol::client_opcode::select_bucket: {
235
246
  protocol::client_response<protocol::select_bucket_response_body> resp(msg);
236
247
  if (resp.status() == protocol::status::success) {
@@ -320,6 +331,7 @@ class mcbp_session : public std::enable_shared_from_this
320
331
  spdlog::warn("unexpected message status: {}", resp.error_message());
321
332
  }
322
333
  } break;
334
+ case protocol::client_opcode::get_collection_id:
323
335
  case protocol::client_opcode::get:
324
336
  case protocol::client_opcode::get_and_lock:
325
337
  case protocol::client_opcode::get_and_touch:
@@ -481,9 +493,16 @@ class mcbp_session : public std::enable_shared_from_this
481
493
  }
482
494
  }
483
495
 
484
- [[nodiscard]] std::optional<collections_manifest> manifest()
496
+ void cancel(uint32_t opaque, std::error_code ec)
485
497
  {
486
- return manifest_;
498
+ if (stopped_) {
499
+ return;
500
+ }
501
+ auto handler = command_handlers_.find(opaque);
502
+ if (handler != command_handlers_.end()) {
503
+ handler->second(ec, {});
504
+ command_handlers_.erase(handler);
505
+ }
487
506
  }
488
507
 
489
508
  bool supports_feature(protocol::hello_feature feature)
@@ -663,6 +682,16 @@ class mcbp_session : public std::enable_shared_from_this
663
682
  }
664
683
  }
665
684
 
685
+ std::optional<std::uint32_t> get_collection_uid(const std::string& collection_path)
686
+ {
687
+ return collection_cache_.get(collection_path);
688
+ }
689
+
690
+ void update_collection_uid(const std::string& path, std::uint32_t uid)
691
+ {
692
+ collection_cache_.update(path, uid);
693
+ }
694
+
666
695
  private:
667
696
  void invoke_bootstrap_handler(std::error_code ec)
668
697
  {
@@ -704,7 +733,7 @@ class mcbp_session : public std::enable_shared_from_this
704
733
  }
705
734
  if (it != endpoints_.end()) {
706
735
  spdlog::trace("connecting to {}:{}", it->endpoint().address().to_string(), it->endpoint().port());
707
- deadline_timer_.expires_after(std::chrono::seconds(10));
736
+ deadline_timer_.expires_after(timeout_defaults::connect_timeout);
708
737
  socket_.async_connect(it->endpoint(), std::bind(&mcbp_session::on_connect, this, std::placeholders::_1, it));
709
738
  } else {
710
739
  spdlog::error("no more endpoints left to connect");
@@ -852,7 +881,7 @@ class mcbp_session : public std::enable_shared_from_this
852
881
  std::vector<protocol::hello_feature> supported_features_;
853
882
  std::optional<configuration> config_;
854
883
  std::optional<error_map> errmap_;
855
- std::optional<collections_manifest> manifest_;
884
+ collection_cache collection_cache_;
856
885
 
857
886
  std::atomic_bool reading_{ false };
858
887
  };
@@ -25,7 +25,7 @@
25
25
  namespace couchbase::io
26
26
  {
27
27
 
28
- class session_manager
28
+ class session_manager : public std::enable_shared_from_this<session_manager>
29
29
  {
30
30
  public:
31
31
  session_manager(uuid::uuid_t client_id, asio::io_context& ctx)
@@ -57,11 +57,16 @@ class session_manager
57
57
  }
58
58
  config_.nodes.size();
59
59
  auto session = std::make_shared<http_session>(client_id_, ctx_, username, password, hostname, std::to_string(port));
60
+ session->on_stop([type, id = session->id(), self = this->shared_from_this()]() {
61
+ self->busy_sessions_[type].remove_if([id](const auto& s) -> bool { return s->id() == id; });
62
+ self->idle_sessions_[type].remove_if([id](const auto& s) -> bool { return s->id() == id; });
63
+ });
60
64
  busy_sessions_[type].push_back(session);
61
65
  return session;
62
66
  }
63
67
  auto session = idle_sessions_[type].front();
64
68
  idle_sessions_[type].pop_front();
69
+ busy_sessions_[type].push_back(session);
65
70
  return session;
66
71
  }
67
72
 
@@ -18,6 +18,7 @@
18
18
  #pragma once
19
19
 
20
20
  #include <document_id.hxx>
21
+ #include <timeout_defaults.hxx>
21
22
 
22
23
  #include <operations/document_get.hxx>
23
24
  #include <operations/document_get_and_lock.hxx>
@@ -33,8 +34,10 @@
33
34
  #include <operations/document_unlock.hxx>
34
35
  #include <operations/document_increment.hxx>
35
36
  #include <operations/document_decrement.hxx>
37
+ #include <operations/document_get_projected.hxx>
36
38
 
37
39
  #include <operations/document_query.hxx>
40
+ #include <operations/document_search.hxx>
38
41
 
39
42
  #include <operations/bucket_get_all.hxx>
40
43
  #include <operations/bucket_get.hxx>
@@ -56,4 +59,14 @@
56
59
  #include <operations/query_index_create.hxx>
57
60
  #include <operations/query_index_build_deferred.hxx>
58
61
 
62
+ #include <operations/search_index_get_all.hxx>
63
+ #include <operations/search_index_get.hxx>
64
+ #include <operations/search_index_get_documents_count.hxx>
65
+ #include <operations/search_index_upsert.hxx>
66
+ #include <operations/search_index_drop.hxx>
67
+ #include <operations/search_index_control_ingest.hxx>
68
+ #include <operations/search_index_control_query.hxx>
69
+ #include <operations/search_index_control_plan_freeze.hxx>
70
+ #include <operations/search_index_analyze_document.hxx>
71
+
59
72
  #include <operations/command.hxx>
@@ -39,6 +39,7 @@ struct bucket_create_request {
39
39
  static const inline service_type type = service_type::management;
40
40
 
41
41
  bucket_settings bucket{};
42
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
42
43
 
43
44
  void encode_to(encoded_request_type& encoded)
44
45
  {
@@ -34,6 +34,7 @@ struct bucket_drop_request {
34
34
  static const inline service_type type = service_type::management;
35
35
 
36
36
  std::string name;
37
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
37
38
 
38
39
  void encode_to(encoded_request_type& encoded)
39
40
  {
@@ -34,6 +34,7 @@ struct bucket_flush_request {
34
34
  static const inline service_type type = service_type::management;
35
35
 
36
36
  std::string name;
37
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
37
38
 
38
39
  void encode_to(encoded_request_type& encoded)
39
40
  {
@@ -38,6 +38,7 @@ struct bucket_get_request {
38
38
  static const inline service_type type = service_type::management;
39
39
 
40
40
  std::string name;
41
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
41
42
 
42
43
  void encode_to(encoded_request_type& encoded)
43
44
  {
@@ -36,6 +36,7 @@ struct bucket_get_all_request {
36
36
  using encoded_response_type = io::http_response;
37
37
 
38
38
  static const inline service_type type = service_type::management;
39
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
39
40
 
40
41
  void encode_to(encoded_request_type& encoded)
41
42
  {
@@ -38,6 +38,7 @@ struct bucket_update_request {
38
38
  using encoded_response_type = io::http_response;
39
39
 
40
40
  static const inline service_type type = service_type::management;
41
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
41
42
 
42
43
  bucket_settings bucket{};
43
44
 
@@ -35,6 +35,7 @@ struct cluster_developer_preview_enable_request {
35
35
  using encoded_response_type = io::http_response;
36
36
 
37
37
  static const inline service_type type = service_type::management;
38
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
38
39
 
39
40
  void encode_to(encoded_request_type& encoded)
40
41
  {
@@ -42,6 +42,7 @@ struct collection_create_request {
42
42
  std::string scope_name;
43
43
  std::string collection_name;
44
44
  std::uint32_t max_expiry{ 0 };
45
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
45
46
 
46
47
  void encode_to(encoded_request_type& encoded)
47
48
  {
@@ -62,7 +63,11 @@ make_response(std::error_code ec, collection_create_request&, collection_create_
62
63
  if (!ec) {
63
64
  switch (encoded.status_code) {
64
65
  case 400:
65
- response.ec = std::make_error_code(error::management_errc::collection_exists);
66
+ if (encoded.body.find("Collection with this name already exists") != std::string::npos) {
67
+ response.ec = std::make_error_code(error::management_errc::collection_exists);
68
+ } else {
69
+ response.ec = std::make_error_code(error::common_errc::invalid_argument);
70
+ }
66
71
  break;
67
72
  case 404:
68
73
  if (encoded.body.find("Scope with this name is not found") != std::string::npos) {
@@ -41,6 +41,7 @@ struct collection_drop_request {
41
41
  std::string bucket_name;
42
42
  std::string scope_name;
43
43
  std::string collection_name;
44
+ std::chrono::milliseconds timeout{ timeout_defaults::management_timeout };
44
45
 
45
46
  void encode_to(encoded_request_type& encoded)
46
47
  {
@@ -19,6 +19,7 @@
19
19
 
20
20
  #include <io/mcbp_session.hxx>
21
21
  #include <io/http_session.hxx>
22
+ #include <protocol/cmd_get_collection_id.hxx>
22
23
 
23
24
  namespace couchbase::operations
24
25
  {
@@ -28,30 +29,103 @@ struct command : public std::enable_shared_from_this> {
28
29
  using encoded_request_type = typename Request::encoded_request_type;
29
30
  using encoded_response_type = typename Request::encoded_response_type;
30
31
  asio::steady_timer deadline;
32
+ asio::steady_timer retry_backoff;
31
33
  Request request;
32
34
  encoded_request_type encoded;
33
35
 
34
36
  command(asio::io_context& ctx, Request&& req)
35
37
  : deadline(ctx)
38
+ , retry_backoff(ctx)
36
39
  , request(req)
37
40
  {
38
41
  }
39
42
 
43
+ template<typename Handler>
44
+ void request_collection_id(std::shared_ptr<io::mcbp_session> session, Handler&& handler)
45
+ {
46
+ protocol::client_request<protocol::get_collection_id_request_body> req;
47
+ req.opaque(session->next_opaque());
48
+ req.body().collection_path(request.id.collection);
49
+ session->write_and_subscribe(req.opaque(),
50
+ req.data(session->supports_feature(protocol::hello_feature::snappy)),
51
+ [self = this->shared_from_this(), session, handler = std::forward<Handler>(handler)](
52
+ std::error_code ec, io::mcbp_message&& msg) mutable {
53
+ if (ec == std::make_error_code(error::common_errc::collection_not_found)) {
54
+ if (self->request.id.collection_uid) {
55
+ return self->handle_unknown_collection(session, std::forward<Handler>(handler));
56
+ }
57
+ return handler(make_response(ec, self->request, {}));
58
+ }
59
+ if (ec) {
60
+ return handler(make_response(ec, self->request, {}));
61
+ }
62
+ protocol::client_response<protocol::get_collection_id_response_body> resp(msg);
63
+ session->update_collection_uid(self->request.id.collection, resp.body().collection_uid());
64
+ self->request.id.collection_uid = resp.body().collection_uid();
65
+ return self->send_to(session, std::forward<Handler>(handler));
66
+ });
67
+ }
68
+
69
+ template<typename Handler>
70
+ void handle_unknown_collection(std::shared_ptr<io::mcbp_session> session, Handler&& handler)
71
+ {
72
+ auto backoff = std::chrono::milliseconds(500);
73
+ if (std::chrono::steady_clock::now() + backoff > deadline.expiry()) {
74
+ return handler(make_response(std::make_error_code(error::common_errc::ambiguous_timeout), request, {}));
75
+ }
76
+ retry_backoff.expires_after(backoff);
77
+ retry_backoff.async_wait(
78
+ [self = this->shared_from_this(), session, handler = std::forward<Handler>(handler)](std::error_code ec) mutable {
79
+ if (ec == asio::error::operation_aborted) {
80
+ return handler(make_response(std::make_error_code(error::common_errc::ambiguous_timeout), self->request, {}));
81
+ }
82
+ self->request_collection_id(session, std::forward<Handler>(handler));
83
+ });
84
+ }
85
+
40
86
  template<typename Handler>
41
87
  void send_to(std::shared_ptr<io::mcbp_session> session, Handler&& handler)
42
88
  {
43
- request.opaque = session->next_opaque();
89
+ auto opaque = session->next_opaque();
90
+ request.opaque = opaque;
91
+ if (!request.id.collection_uid) {
92
+ if (session->supports_feature(protocol::hello_feature::collections)) {
93
+ auto collection_id = session->get_collection_uid(request.id.collection);
94
+ if (collection_id) {
95
+ request.id.collection_uid = *collection_id;
96
+ } else {
97
+ return request_collection_id(session, std::forward<Handler>(handler));
98
+ }
99
+ } else {
100
+ if (!request.id.collection.empty() && request.id.collection != "_default._default") {
101
+ return handler(make_response(std::make_error_code(error::common_errc::unsupported_operation), request, {}));
102
+ }
103
+ }
104
+ }
44
105
  request.encode_to(encoded);
45
106
  session->write_and_subscribe(
46
107
  request.opaque,
47
108
  encoded.data(session->supports_feature(protocol::hello_feature::snappy)),
48
- [self = this->shared_from_this(), handler = std::forward<Handler>(handler)](std::error_code ec, io::mcbp_message&& msg) mutable {
49
- encoded_response_type resp(msg);
109
+ [self = this->shared_from_this(), session, handler = std::forward<Handler>(handler)](std::error_code ec,
110
+ io::mcbp_message&& msg) mutable {
111
+ if (ec == asio::error::operation_aborted) {
112
+ return handler(make_response(std::make_error_code(error::common_errc::ambiguous_timeout), self->request, {}));
113
+ }
50
114
  self->deadline.cancel();
115
+ self->retry_backoff.cancel();
116
+ encoded_response_type resp(msg);
117
+ if (resp.status() == protocol::status::unknown_collection) {
118
+ return self->handle_unknown_collection(session, std::forward<Handler>(handler));
119
+ }
51
120
  handler(make_response(ec, self->request, resp));
52
121
  });
53
- deadline.expires_after(std::chrono::milliseconds(2500));
54
- deadline.async_wait(std::bind(&command<Request>::deadline_handler, this));
122
+ deadline.expires_after(request.timeout);
123
+ deadline.async_wait([session, opaque](std::error_code ec) {
124
+ if (ec == asio::error::operation_aborted) {
125
+ return;
126
+ }
127
+ session->cancel(opaque, asio::error::operation_aborted);
128
+ });
55
129
  }
56
130
 
57
131
  template<typename Handler>
@@ -65,12 +139,13 @@ struct command : public std::enable_shared_from_this> {
65
139
  self->deadline.cancel();
66
140
  handler(make_response(ec, self->request, resp));
67
141
  });
68
- deadline.expires_after(std::chrono::milliseconds(2500));
69
- deadline.async_wait(std::bind(&command<Request>::deadline_handler, this));
70
- }
71
-
72
- void deadline_handler()
73
- {
142
+ deadline.expires_after(request.timeout);
143
+ deadline.async_wait([session](std::error_code ec) {
144
+ if (ec == asio::error::operation_aborted) {
145
+ return;
146
+ }
147
+ session->stop();
148
+ });
74
149
  }
75
150
  };
76
151