couchbase 3.0.1 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +73 -4
  3. data/ext/build_config.hxx.in +2 -0
  4. data/ext/build_version.hxx.in +11 -8
  5. data/ext/cmake/BuildTracing.cmake +1 -1
  6. data/ext/cmake/CompilerWarnings.cmake +5 -0
  7. data/ext/cmake/Testing.cmake +3 -6
  8. data/ext/couchbase/bucket.hxx +9 -1
  9. data/ext/couchbase/cbsasl/client.h +1 -1
  10. data/ext/couchbase/cluster.hxx +89 -6
  11. data/ext/couchbase/configuration.hxx +2 -0
  12. data/ext/couchbase/couchbase.cxx +1647 -507
  13. data/ext/couchbase/diagnostics.hxx +0 -3
  14. data/ext/couchbase/io/dns_client.hxx +2 -2
  15. data/ext/couchbase/io/http_command.hxx +6 -3
  16. data/ext/couchbase/io/http_session.hxx +14 -18
  17. data/ext/couchbase/io/http_session_manager.hxx +83 -2
  18. data/ext/couchbase/io/mcbp_command.hxx +4 -1
  19. data/ext/couchbase/io/mcbp_context.hxx +37 -0
  20. data/ext/couchbase/io/mcbp_session.hxx +91 -30
  21. data/ext/couchbase/operations.hxx +5 -0
  22. data/ext/couchbase/operations/analytics_dataset_create.hxx +3 -2
  23. data/ext/couchbase/operations/analytics_dataset_drop.hxx +3 -2
  24. data/ext/couchbase/operations/analytics_dataset_get_all.hxx +3 -2
  25. data/ext/couchbase/operations/analytics_dataverse_create.hxx +3 -2
  26. data/ext/couchbase/operations/analytics_dataverse_drop.hxx +3 -2
  27. data/ext/couchbase/operations/analytics_get_pending_mutations.hxx +3 -2
  28. data/ext/couchbase/operations/analytics_index_create.hxx +3 -2
  29. data/ext/couchbase/operations/analytics_index_drop.hxx +3 -2
  30. data/ext/couchbase/operations/analytics_index_get_all.hxx +5 -2
  31. data/ext/couchbase/operations/analytics_link_connect.hxx +3 -2
  32. data/ext/couchbase/operations/analytics_link_disconnect.hxx +3 -2
  33. data/ext/couchbase/operations/bucket_create.hxx +3 -2
  34. data/ext/couchbase/operations/bucket_drop.hxx +3 -2
  35. data/ext/couchbase/operations/bucket_flush.hxx +3 -2
  36. data/ext/couchbase/operations/bucket_get.hxx +3 -2
  37. data/ext/couchbase/operations/bucket_get_all.hxx +3 -2
  38. data/ext/couchbase/operations/bucket_update.hxx +3 -2
  39. data/ext/couchbase/operations/cluster_developer_preview_enable.hxx +3 -2
  40. data/ext/couchbase/operations/collection_create.hxx +3 -2
  41. data/ext/couchbase/operations/collection_drop.hxx +3 -2
  42. data/ext/couchbase/operations/collections_manifest_get.hxx +3 -2
  43. data/ext/couchbase/operations/document_analytics.hxx +3 -2
  44. data/ext/couchbase/operations/document_append.hxx +77 -0
  45. data/ext/couchbase/operations/document_decrement.hxx +3 -2
  46. data/ext/couchbase/operations/document_exists.hxx +3 -2
  47. data/ext/couchbase/operations/document_get.hxx +3 -2
  48. data/ext/couchbase/operations/document_get_and_lock.hxx +3 -2
  49. data/ext/couchbase/operations/document_get_and_touch.hxx +3 -2
  50. data/ext/couchbase/operations/document_get_projected.hxx +3 -2
  51. data/ext/couchbase/operations/document_increment.hxx +3 -2
  52. data/ext/couchbase/operations/document_insert.hxx +3 -2
  53. data/ext/couchbase/operations/document_lookup_in.hxx +8 -2
  54. data/ext/couchbase/operations/document_mutate_in.hxx +13 -2
  55. data/ext/couchbase/operations/document_prepend.hxx +77 -0
  56. data/ext/couchbase/operations/document_query.hxx +3 -2
  57. data/ext/couchbase/operations/document_remove.hxx +5 -2
  58. data/ext/couchbase/operations/document_replace.hxx +3 -2
  59. data/ext/couchbase/operations/document_search.hxx +3 -2
  60. data/ext/couchbase/operations/document_touch.hxx +3 -2
  61. data/ext/couchbase/operations/document_unlock.hxx +3 -2
  62. data/ext/couchbase/operations/document_upsert.hxx +3 -2
  63. data/ext/couchbase/operations/document_view.hxx +3 -2
  64. data/ext/couchbase/operations/group_drop.hxx +3 -2
  65. data/ext/couchbase/operations/group_get.hxx +3 -2
  66. data/ext/couchbase/operations/group_get_all.hxx +3 -2
  67. data/ext/couchbase/operations/group_upsert.hxx +3 -2
  68. data/ext/couchbase/operations/http_noop.hxx +78 -0
  69. data/ext/couchbase/operations/mcbp_noop.hxx +61 -0
  70. data/ext/couchbase/operations/query_index_build_deferred.hxx +3 -2
  71. data/ext/couchbase/operations/query_index_create.hxx +3 -2
  72. data/ext/couchbase/operations/query_index_drop.hxx +3 -2
  73. data/ext/couchbase/operations/query_index_get_all.hxx +3 -2
  74. data/ext/couchbase/operations/role_get_all.hxx +3 -2
  75. data/ext/couchbase/operations/scope_create.hxx +3 -2
  76. data/ext/couchbase/operations/scope_drop.hxx +3 -2
  77. data/ext/couchbase/operations/scope_get_all.hxx +3 -2
  78. data/ext/couchbase/operations/search_get_stats.hxx +3 -2
  79. data/ext/couchbase/operations/search_index_analyze_document.hxx +3 -2
  80. data/ext/couchbase/operations/search_index_control_ingest.hxx +3 -2
  81. data/ext/couchbase/operations/search_index_control_plan_freeze.hxx +3 -2
  82. data/ext/couchbase/operations/search_index_control_query.hxx +3 -2
  83. data/ext/couchbase/operations/search_index_drop.hxx +3 -2
  84. data/ext/couchbase/operations/search_index_get.hxx +3 -2
  85. data/ext/couchbase/operations/search_index_get_all.hxx +3 -2
  86. data/ext/couchbase/operations/search_index_get_documents_count.hxx +3 -2
  87. data/ext/couchbase/operations/search_index_get_stats.hxx +3 -2
  88. data/ext/couchbase/operations/search_index_upsert.hxx +3 -2
  89. data/ext/couchbase/operations/user_drop.hxx +3 -2
  90. data/ext/couchbase/operations/user_get.hxx +3 -2
  91. data/ext/couchbase/operations/user_get_all.hxx +3 -2
  92. data/ext/couchbase/operations/user_upsert.hxx +3 -2
  93. data/ext/couchbase/operations/view_index_drop.hxx +3 -2
  94. data/ext/couchbase/operations/view_index_get.hxx +3 -2
  95. data/ext/couchbase/operations/view_index_get_all.hxx +3 -2
  96. data/ext/couchbase/operations/view_index_upsert.hxx +3 -2
  97. data/ext/couchbase/platform/terminate_handler.cc +5 -2
  98. data/ext/couchbase/protocol/client_opcode.hxx +368 -0
  99. data/ext/couchbase/protocol/cmd_append.hxx +145 -0
  100. data/ext/couchbase/protocol/cmd_hello.hxx +1 -0
  101. data/ext/couchbase/protocol/cmd_lookup_in.hxx +11 -3
  102. data/ext/couchbase/protocol/cmd_mutate_in.hxx +46 -4
  103. data/ext/couchbase/protocol/cmd_noop.hxx +82 -0
  104. data/ext/couchbase/protocol/cmd_prepend.hxx +145 -0
  105. data/ext/couchbase/protocol/durability_level.hxx +16 -0
  106. data/ext/couchbase/protocol/hello_feature.hxx +9 -0
  107. data/ext/couchbase/protocol/unsigned_leb128.h +2 -2
  108. data/ext/couchbase/service_type.hxx +1 -1
  109. data/ext/couchbase/version.hxx +18 -4
  110. data/ext/extconf.rb +9 -6
  111. data/ext/test/CMakeLists.txt +5 -0
  112. data/ext/test/test_helper.hxx +3 -3
  113. data/ext/test/test_helper_native.hxx +2 -5
  114. data/ext/test/test_native_binary_operations.cxx +186 -0
  115. data/ext/test/test_native_diagnostics.cxx +54 -3
  116. data/ext/test/test_ruby_trivial_crud.cxx +1 -1
  117. data/lib/couchbase.rb +1 -0
  118. data/lib/couchbase/analytics_options.rb +1 -71
  119. data/lib/couchbase/binary_collection.rb +60 -22
  120. data/lib/couchbase/binary_collection_options.rb +0 -76
  121. data/lib/couchbase/bucket.rb +40 -36
  122. data/lib/couchbase/cluster.rb +89 -156
  123. data/lib/couchbase/collection.rb +290 -72
  124. data/lib/couchbase/collection_options.rb +30 -243
  125. data/lib/couchbase/datastructures/couchbase_list.rb +5 -16
  126. data/lib/couchbase/datastructures/couchbase_map.rb +5 -16
  127. data/lib/couchbase/datastructures/couchbase_queue.rb +5 -16
  128. data/lib/couchbase/datastructures/couchbase_set.rb +5 -16
  129. data/lib/couchbase/diagnostics.rb +181 -0
  130. data/lib/couchbase/json_transcoder.rb +1 -1
  131. data/lib/couchbase/{common_options.rb → logger.rb} +24 -11
  132. data/lib/couchbase/management/query_index_manager.rb +1 -1
  133. data/lib/couchbase/management/user_manager.rb +3 -0
  134. data/lib/couchbase/options.rb +2094 -0
  135. data/lib/couchbase/query_options.rb +1 -144
  136. data/lib/couchbase/scope.rb +8 -25
  137. data/lib/couchbase/search_options.rb +0 -93
  138. data/lib/couchbase/version.rb +20 -1
  139. data/lib/couchbase/view_options.rb +1 -91
  140. metadata +19 -7
@@ -90,7 +90,6 @@ struct endpoint_ping_info {
90
90
  struct ping_result {
91
91
  std::string id;
92
92
  std::string sdk;
93
- std::uint64_t config_rev;
94
93
  std::map<service_type, std::vector<endpoint_ping_info>> services{};
95
94
 
96
95
  const int version{ 2 };
@@ -242,8 +241,6 @@ struct traits<couchbase::diag::ping_result> {
242
241
  { "version", r.version },
243
242
  { "id", r.id },
244
243
  { "sdk", r.sdk },
245
- /* the highest known configuration revision */
246
- { "config_rev", r.config_rev },
247
244
  { "services", services },
248
245
  };
249
246
  }
@@ -109,7 +109,7 @@ class dns_client
109
109
  resp.targets.emplace_back(
110
110
  dns_srv_response::address{ fmt::format("{}", fmt::join(answer.target.labels, ".")), answer.port });
111
111
  }
112
- return handler(resp);
112
+ return handler(std::move(resp));
113
113
  });
114
114
  });
115
115
  deadline_.expires_after(timeout);
@@ -179,7 +179,7 @@ class dns_client
179
179
  resp.targets.emplace_back(dns_srv_response::address{
180
180
  fmt::format("{}", fmt::join(answer.target.labels, ".")), answer.port });
181
181
  }
182
- return handler(resp);
182
+ return handler(std::move(resp));
183
183
  });
184
184
  });
185
185
  });
@@ -41,8 +41,11 @@ struct http_command : public std::enable_shared_from_this<http_command<Request>>
41
41
  template<typename Handler>
42
42
  void send_to(std::shared_ptr<io::http_session> session, Handler&& handler)
43
43
  {
44
- encoded.type = Request::type;
45
- request.encode_to(encoded, session->http_context());
44
+ encoded.type = request.type;
45
+ auto encoding_ec = request.encode_to(encoded, session->http_context());
46
+ if (encoding_ec) {
47
+ return handler(make_response(encoding_ec, request, {}));
48
+ }
46
49
  encoded.headers["client-context-id"] = request.client_context_id;
47
50
  auto log_prefix = session->log_prefix();
48
51
  spdlog::trace(R"({} HTTP request: {}, method={}, path="{}", client_context_id="{}", timeout={}ms)",
@@ -77,7 +80,7 @@ struct http_command : public std::enable_shared_from_this<http_command<Request>>
77
80
  resp.status_code,
78
81
  spdlog::to_hex(resp.body));
79
82
  try {
80
- handler(make_response(ec, self->request, resp));
83
+ handler(make_response(ec, self->request, std::move(msg)));
81
84
  } catch (priv::retry_http_request) {
82
85
  self->send_to(session, std::forward<Handler>(handler));
83
86
  }
@@ -60,14 +60,7 @@ class http_session : public std::enable_shared_from_this<http_session>
60
60
  , credentials_(credentials)
61
61
  , hostname_(hostname)
62
62
  , service_(service)
63
- , user_agent_(fmt::format("ruby/{}.{}.{}/{}; client/{}; session/{}; {}",
64
- BACKEND_VERSION_MAJOR,
65
- BACKEND_VERSION_MINOR,
66
- BACKEND_VERSION_PATCH,
67
- BACKEND_GIT_REVISION,
68
- client_id_,
69
- id_,
70
- BACKEND_SYSTEM))
63
+ , user_agent_(fmt::format("{}; client/{}; session/{}; {}", couchbase::sdk_id(), client_id_, id_, BACKEND_SYSTEM))
71
64
  , log_prefix_(fmt::format("[{}/{}]", client_id_, id_))
72
65
  , http_ctx_(std::move(http_ctx))
73
66
  {
@@ -92,14 +85,7 @@ class http_session : public std::enable_shared_from_this<http_session>
92
85
  , credentials_(credentials)
93
86
  , hostname_(hostname)
94
87
  , service_(service)
95
- , user_agent_(fmt::format("ruby/{}.{}.{}/{}; client/{}; session/{}; {}",
96
- BACKEND_VERSION_MAJOR,
97
- BACKEND_VERSION_MINOR,
98
- BACKEND_VERSION_PATCH,
99
- BACKEND_GIT_REVISION,
100
- client_id_,
101
- id_,
102
- BACKEND_SYSTEM))
88
+ , user_agent_(fmt::format("{}; client/{}; session/{}; {}", couchbase::sdk_id(), client_id_, id_, BACKEND_SYSTEM))
103
89
  , log_prefix_(fmt::format("[{}/{}]", client_id_, id_))
104
90
  , http_ctx_(std::move(http_ctx))
105
91
  {
@@ -115,6 +101,16 @@ class http_session : public std::enable_shared_from_this<http_session>
115
101
  return http_ctx_;
116
102
  }
117
103
 
104
+ std::string remote_address() const
105
+ {
106
+ return fmt::format("{}:{}", endpoint_address_, endpoint_.port());
107
+ }
108
+
109
+ std::string local_address() const
110
+ {
111
+ return fmt::format("{}:{}", local_endpoint_address_, local_endpoint_.port());
112
+ }
113
+
118
114
  [[nodiscard]] diag::endpoint_diag_info diag_info() const
119
115
  {
120
116
  return { type_,
@@ -122,8 +118,8 @@ class http_session : public std::enable_shared_from_this<http_session>
122
118
  last_active_.time_since_epoch().count() == 0 ? std::nullopt
123
119
  : std::make_optional(std::chrono::duration_cast<std::chrono::microseconds>(
124
120
  std::chrono::steady_clock::now() - last_active_)),
125
- fmt::format("{}:{}", endpoint_address_, endpoint_.port()),
126
- fmt::format("{}:{}", local_endpoint_address_, local_endpoint_.port()),
121
+ remote_address(),
122
+ local_address(),
127
123
  state_ };
128
124
  }
129
125
 
@@ -20,6 +20,8 @@
20
20
  #include <io/http_session.hxx>
21
21
  #include <service_type.hxx>
22
22
  #include <io/http_context.hxx>
23
+ #include <operations/http_noop.hxx>
24
+ #include <io/http_command.hxx>
23
25
 
24
26
  #include <random>
25
27
 
@@ -55,12 +57,91 @@ class http_session_manager : public std::enable_shared_from_this<http_session_ma
55
57
 
56
58
  for (const auto& list : busy_sessions_) {
57
59
  for (const auto& session : list.second) {
58
- res.services[list.first].emplace_back(session->diag_info());
60
+ if (session) {
61
+ res.services[list.first].emplace_back(session->diag_info());
62
+ }
59
63
  }
60
64
  }
61
65
  for (const auto& list : idle_sessions_) {
62
66
  for (const auto& session : list.second) {
63
- res.services[list.first].emplace_back(session->diag_info());
67
+ if (session) {
68
+ res.services[list.first].emplace_back(session->diag_info());
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ template<typename Collector>
75
+ void ping(std::set<service_type> services, std::shared_ptr<Collector> collector, const couchbase::cluster_credentials& credentials)
76
+ {
77
+ std::array<service_type, 4> known_types{ service_type::query, service_type::analytics, service_type::search, service_type::views };
78
+ for (auto& node : config_.nodes) {
79
+ for (auto type : known_types) {
80
+ if (services.find(type) == services.end()) {
81
+ continue;
82
+ }
83
+ std::uint16_t port = 0;
84
+ port = node.port_or(options_.network, type, options_.enable_tls, 0);
85
+ if (port != 0) {
86
+ std::scoped_lock lock(sessions_mutex_);
87
+ std::shared_ptr<http_session> session;
88
+ session = options_.enable_tls ? std::make_shared<http_session>(type,
89
+ client_id_,
90
+ ctx_,
91
+ tls_,
92
+ credentials,
93
+ node.hostname_for(options_.network),
94
+ std::to_string(port),
95
+ http_context{ config_, options_, query_cache_ })
96
+ : std::make_shared<http_session>(type,
97
+ client_id_,
98
+ ctx_,
99
+ credentials,
100
+ node.hostname_for(options_.network),
101
+ std::to_string(port),
102
+ http_context{ config_, options_, query_cache_ });
103
+ session->start();
104
+ session->on_stop([type, id = session->id(), self = this->shared_from_this()]() {
105
+ for (auto& s : self->busy_sessions_[type]) {
106
+ if (s && s->id() == id) {
107
+ s.reset();
108
+ }
109
+ }
110
+ for (auto& s : self->idle_sessions_[type]) {
111
+ if (s && s->id() == id) {
112
+ s.reset();
113
+ }
114
+ }
115
+ });
116
+ busy_sessions_[type].push_back(session);
117
+ operations::http_noop_request request{};
118
+ request.type = type;
119
+ auto cmd = std::make_shared<operations::http_command<operations::http_noop_request>>(ctx_, request);
120
+ cmd->send_to(session,
121
+ [start = std::chrono::steady_clock::now(),
122
+ self = shared_from_this(),
123
+ type,
124
+ session,
125
+ handler = collector->build_reporter()](operations::http_noop_response&& resp) mutable {
126
+ diag::ping_state state = diag::ping_state::ok;
127
+ std::optional<std::string> error{};
128
+ if (resp.ec) {
129
+ state = diag::ping_state::error;
130
+ error.emplace(fmt::format(
131
+ "code={}, message={}, http_code={}", resp.ec.value(), resp.ec.message(), resp.status_code));
132
+ }
133
+ handler(diag::endpoint_ping_info{
134
+ type,
135
+ session->id(),
136
+ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start),
137
+ session->remote_address(),
138
+ session->local_address(),
139
+ state,
140
+ {},
141
+ error });
142
+ self->check_in(type, session);
143
+ });
144
+ }
64
145
  }
65
146
  }
66
147
  }
@@ -167,7 +167,10 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
167
167
  }
168
168
  }
169
169
  }
170
- request.encode_to(encoded);
170
+ auto encoding_ec = request.encode_to(encoded, session_->context());
171
+ if (encoding_ec) {
172
+ return invoke_handler(encoding_ec);
173
+ }
171
174
 
172
175
  session_->write_and_subscribe(
173
176
  request.opaque,
@@ -0,0 +1,37 @@
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 <vector>
21
+ #include <configuration.hxx>
22
+ #include <protocol/hello_feature.hxx>
23
+
24
+ namespace couchbase
25
+ {
26
+
27
+ struct mcbp_context {
28
+ const std::optional<configuration>& config;
29
+ const std::vector<protocol::hello_feature>& supported_features;
30
+
31
+ [[nodiscard]] bool supports_feature(protocol::hello_feature feature)
32
+ {
33
+ return std::find(supported_features.begin(), supported_features.end(), feature) != supported_features.end();
34
+ }
35
+ };
36
+
37
+ } // namespace couchbase
@@ -29,6 +29,7 @@
29
29
  #include <io/mcbp_parser.hxx>
30
30
  #include <io/streams.hxx>
31
31
  #include <io/retry_orchestrator.hxx>
32
+ #include <io/mcbp_context.hxx>
32
33
 
33
34
  #include <timeout_defaults.hxx>
34
35
 
@@ -36,6 +37,7 @@
36
37
  #include <protocol/client_request.hxx>
37
38
  #include <protocol/client_response.hxx>
38
39
  #include <protocol/server_request.hxx>
40
+ #include <protocol/cmd_noop.hxx>
39
41
  #include <protocol/cmd_hello.hxx>
40
42
  #include <protocol/cmd_sasl_list_mechs.hxx>
41
43
  #include <protocol/cmd_sasl_auth.hxx>
@@ -127,10 +129,8 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
127
129
  { "SCRAM-SHA512", "SCRAM-SHA256", "SCRAM-SHA1", "PLAIN" })
128
130
  {
129
131
  tao::json::value user_agent{
130
- { "a",
131
- fmt::format(
132
- "ruby/{}.{}.{}/{}", BACKEND_VERSION_MAJOR, BACKEND_VERSION_MINOR, BACKEND_VERSION_PATCH, BACKEND_GIT_REVISION) },
133
- { "i", fmt::format("{}/{}", session_->client_id_, session_->id_) }
132
+ { "a", couchbase::sdk_id() },
133
+ { "i", fmt::format("{}/{}", session_->client_id_, session_->id_) },
134
134
  };
135
135
  protocol::client_request<protocol::hello_request_body> hello_req;
136
136
  hello_req.opaque(session_->next_opaque());
@@ -387,6 +387,7 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
387
387
  resp.opaque());
388
388
  }
389
389
  } break;
390
+ case protocol::client_opcode::noop:
390
391
  case protocol::client_opcode::get_collections_manifest:
391
392
  case protocol::client_opcode::get_collection_id:
392
393
  case protocol::client_opcode::get:
@@ -396,6 +397,8 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
396
397
  case protocol::client_opcode::insert:
397
398
  case protocol::client_opcode::replace:
398
399
  case protocol::client_opcode::upsert:
400
+ case protocol::client_opcode::append:
401
+ case protocol::client_opcode::prepend:
399
402
  case protocol::client_opcode::remove:
400
403
  case protocol::client_opcode::observe:
401
404
  case protocol::client_opcode::unlock:
@@ -405,8 +408,9 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
405
408
  case protocol::client_opcode::subdoc_multi_mutation: {
406
409
  std::uint32_t opaque = msg.header.opaque;
407
410
  std::uint16_t status = ntohs(msg.header.specific);
411
+ session_->command_handlers_mutex_.lock();
408
412
  auto handler = session_->command_handlers_.find(opaque);
409
- if (handler != session_->command_handlers_.end()) {
413
+ if (handler != session_->command_handlers_.end() && handler->second) {
410
414
  auto ec = session_->map_status_code(opcode, status);
411
415
  spdlog::trace("{} MCBP invoke operation handler: opcode={}, opaque={}, status={}, ec={}",
412
416
  session_->log_prefix_,
@@ -414,10 +418,12 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
414
418
  opaque,
415
419
  protocol::status_to_string(status),
416
420
  ec.message());
417
- auto fun = handler->second;
421
+ auto fun = std::move(handler->second);
418
422
  session_->command_handlers_.erase(handler);
423
+ session_->command_handlers_mutex_.unlock();
419
424
  fun(ec, retry_reason::do_not_retry, std::move(msg));
420
425
  } else {
426
+ session_->command_handlers_mutex_.unlock();
421
427
  spdlog::debug("{} unexpected orphan response: opcode={}, opaque={}, status={}",
422
428
  session_->log_prefix_,
423
429
  opcode,
@@ -537,6 +543,16 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
537
543
  return log_prefix_;
538
544
  }
539
545
 
546
+ std::string remote_address() const
547
+ {
548
+ return fmt::format("{}:{}", endpoint_address_, endpoint_.port());
549
+ }
550
+
551
+ std::string local_address() const
552
+ {
553
+ return fmt::format("{}:{}", local_endpoint_address_, local_endpoint_.port());
554
+ }
555
+
540
556
  [[nodiscard]] diag::endpoint_diag_info diag_info() const
541
557
  {
542
558
  return { service_type::kv,
@@ -544,12 +560,44 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
544
560
  last_active_.time_since_epoch().count() == 0 ? std::nullopt
545
561
  : std::make_optional(std::chrono::duration_cast<std::chrono::microseconds>(
546
562
  std::chrono::steady_clock::now() - last_active_)),
547
- fmt::format("{}:{}", endpoint_address_, endpoint_.port()),
548
- fmt::format("{}:{}", local_endpoint_address_, local_endpoint_.port()),
563
+ remote_address(),
564
+ local_address(),
549
565
  state_,
550
566
  bucket_name_ };
551
567
  }
552
568
 
569
+ template<typename Handler>
570
+ void ping(Handler&& handler)
571
+ {
572
+ protocol::client_request<protocol::mcbp_noop_request_body> req;
573
+ req.opaque(next_opaque());
574
+ write_and_subscribe(req.opaque(),
575
+ req.data(false),
576
+ [start = std::chrono::steady_clock::now(), self = shared_from_this(), handler](
577
+ std::error_code ec, retry_reason reason, io::mcbp_message&& /* msg */) {
578
+ diag::ping_state state = diag::ping_state::ok;
579
+ std::optional<std::string> error{};
580
+ if (ec) {
581
+ state = diag::ping_state::error;
582
+ error.emplace(fmt::format("code={}, message={}, reason={}", ec.value(), ec.message(), reason));
583
+ }
584
+ handler(diag::endpoint_ping_info{
585
+ service_type::kv,
586
+ self->id_,
587
+ std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start),
588
+ self->remote_address(),
589
+ self->local_address(),
590
+ state,
591
+ self->bucket_name_,
592
+ error });
593
+ });
594
+ }
595
+
596
+ [[nodiscard]] mcbp_context context() const
597
+ {
598
+ return { config_, supported_features_ };
599
+ }
600
+
553
601
  void bootstrap(std::function<void(std::error_code, configuration)>&& handler, bool retry_on_bucket_not_found = false)
554
602
  {
555
603
  retry_bootstrap_on_bucket_not_found_ = retry_on_bucket_not_found;
@@ -648,11 +696,18 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
648
696
  if (handler_) {
649
697
  handler_->stop();
650
698
  }
651
- for (auto& handler : command_handlers_) {
652
- spdlog::debug("{} MCBP cancel operation during session close, opaque={}, ec={}", log_prefix_, handler.first, ec.message());
653
- handler.second(ec, reason, {});
699
+ {
700
+ std::scoped_lock lock(command_handlers_mutex_);
701
+ for (auto& handler : command_handlers_) {
702
+ if (handler.second) {
703
+ spdlog::debug(
704
+ "{} MCBP cancel operation during session close, opaque={}, ec={}", log_prefix_, handler.first, ec.message());
705
+ auto fun = std::move(handler.second);
706
+ fun(ec, reason, {});
707
+ }
708
+ }
709
+ command_handlers_.clear();
654
710
  }
655
- command_handlers_.clear();
656
711
  config_listeners_.clear();
657
712
  if (on_stop_handler_) {
658
713
  on_stop_handler_(reason);
@@ -700,7 +755,10 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
700
755
  handler(std::make_error_code(error::common_errc::request_canceled), retry_reason::socket_closed_while_in_flight, {});
701
756
  return;
702
757
  }
703
- command_handlers_.emplace(opaque, std::move(handler));
758
+ {
759
+ std::scoped_lock lock(command_handlers_mutex_);
760
+ command_handlers_.emplace(opaque, std::move(handler));
761
+ }
704
762
  if (bootstrapped_ && stream_->is_open()) {
705
763
  write_and_flush(data);
706
764
  } else {
@@ -715,13 +773,19 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
715
773
  if (stopped_) {
716
774
  return false;
717
775
  }
776
+ command_handlers_mutex_.lock();
718
777
  auto handler = command_handlers_.find(opaque);
719
778
  if (handler != command_handlers_.end()) {
720
779
  spdlog::debug("{} MCBP cancel operation, opaque={}, ec={} ({})", log_prefix_, opaque, ec.value(), ec.message());
721
- handler->second(ec, reason, {});
722
- command_handlers_.erase(handler);
723
- return true;
780
+ if (handler->second) {
781
+ auto fun = std::move(handler->second);
782
+ command_handlers_.erase(handler);
783
+ command_handlers_mutex_.unlock();
784
+ fun(ec, reason, {});
785
+ return true;
786
+ }
724
787
  }
788
+ command_handlers_mutex_.unlock();
725
789
  return false;
726
790
  }
727
791
 
@@ -1142,22 +1206,19 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
1142
1206
  self->last_active_ = std::chrono::steady_clock::now();
1143
1207
  if (ec) {
1144
1208
  if (stream_id != self->stream_->id()) {
1145
- spdlog::error(
1146
- R"({} ignore IO error while reading from the socket: {} ({}), pending_handlers={}, old_id="{}", new_id="{}")",
1147
- self->log_prefix_,
1148
- ec.value(),
1149
- ec.message(),
1150
- self->command_handlers_.size(),
1151
- stream_id,
1152
- self->stream_->id());
1209
+ spdlog::error(R"({} ignore IO error while reading from the socket: {} ({}), old_id="{}", new_id="{}")",
1210
+ self->log_prefix_,
1211
+ ec.value(),
1212
+ ec.message(),
1213
+ stream_id,
1214
+ self->stream_->id());
1153
1215
  return;
1154
1216
  }
1155
- spdlog::error(R"({} IO error while reading from the socket("{}"): {} ({}), pending_handlers={})",
1217
+ spdlog::error(R"({} IO error while reading from the socket("{}"): {} ({}))",
1156
1218
  self->log_prefix_,
1157
1219
  self->stream_->id(),
1158
1220
  ec.value(),
1159
- ec.message(),
1160
- self->command_handlers_.size());
1221
+ ec.message());
1161
1222
  return self->stop(retry_reason::socket_closed_while_in_flight);
1162
1223
  }
1163
1224
  self->parser_.feed(self->input_buffer_.data(), self->input_buffer_.data() + ssize_t(bytes_transferred));
@@ -1212,12 +1273,11 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
1212
1273
  }
1213
1274
  self->last_active_ = std::chrono::steady_clock::now();
1214
1275
  if (ec) {
1215
- spdlog::error(R"({} IO error while writing to the socket("{}"): {} ({}), pending_handlers={})",
1276
+ spdlog::error(R"({} IO error while writing to the socket("{}"): {} ({}))",
1216
1277
  self->log_prefix_,
1217
1278
  self->stream_->id(),
1218
1279
  ec.value(),
1219
- ec.message(),
1220
- self->command_handlers_.size());
1280
+ ec.message());
1221
1281
  return self->stop(retry_reason::socket_closed_while_in_flight);
1222
1282
  }
1223
1283
  {
@@ -1242,6 +1302,7 @@ class mcbp_session : public std::enable_shared_from_this<mcbp_session>
1242
1302
  mcbp_parser parser_;
1243
1303
  std::unique_ptr<message_handler> handler_;
1244
1304
  std::function<void(std::error_code, const configuration&)> bootstrap_handler_{};
1305
+ std::mutex command_handlers_mutex_{};
1245
1306
  std::map<uint32_t, std::function<void(std::error_code, retry_reason, io::mcbp_message&&)>> command_handlers_{};
1246
1307
  std::vector<std::function<void(const configuration&)>> config_listeners_{};
1247
1308
  std::function<void(io::retry_reason)> on_stop_handler_{};