couchbase 3.0.1 → 3.0.2

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 (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_{};