couchbase 3.0.0.alpha.2-universal-darwin-19 → 3.0.0.alpha.3-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 (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<http_session>
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<http_session>
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<http_session>
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<http_session>
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<http_session>
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<http_session>
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<mcbp_session>
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<mcbp_session>
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<mcbp_session>
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<mcbp_session>
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<mcbp_session>
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<mcbp_session>
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<mcbp_session>
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<command<Request>> {
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<command<Request>> {
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