couchbase 4.2.6-dev.1 → 4.2.7

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 (67) hide show
  1. package/CONTRIBUTING.md +97 -0
  2. package/deps/couchbase-cxx-client/.github/workflows/windows.yml +0 -3
  3. package/deps/couchbase-cxx-client/CMakeLists.txt +4 -0
  4. package/deps/couchbase-cxx-client/bin/build-tests.rb +1 -1
  5. package/deps/couchbase-cxx-client/core/impl/lookup_in.cxx +1 -0
  6. package/deps/couchbase-cxx-client/core/impl/lookup_in_all_replicas.cxx +176 -0
  7. package/deps/couchbase-cxx-client/core/impl/lookup_in_all_replicas.hxx +80 -0
  8. package/deps/couchbase-cxx-client/core/impl/lookup_in_any_replica.cxx +167 -0
  9. package/deps/couchbase-cxx-client/core/impl/lookup_in_any_replica.hxx +75 -0
  10. package/deps/couchbase-cxx-client/core/impl/lookup_in_replica.cxx +97 -0
  11. package/deps/couchbase-cxx-client/core/impl/lookup_in_replica.hxx +67 -0
  12. package/deps/couchbase-cxx-client/core/io/dns_client.cxx +48 -10
  13. package/deps/couchbase-cxx-client/core/io/http_session.hxx +24 -1
  14. package/deps/couchbase-cxx-client/core/io/mcbp_session.cxx +22 -1
  15. package/deps/couchbase-cxx-client/core/logger/custom_rotating_file_sink.cxx +1 -1
  16. package/deps/couchbase-cxx-client/core/logger/logger.cxx +80 -20
  17. package/deps/couchbase-cxx-client/core/logger/logger.hxx +31 -0
  18. package/deps/couchbase-cxx-client/core/meta/features.hxx +19 -0
  19. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  20. package/deps/couchbase-cxx-client/core/operations/document_lookup_in_any_replica.hxx +188 -0
  21. package/deps/couchbase-cxx-client/core/operations.hxx +2 -0
  22. package/deps/couchbase-cxx-client/core/protocol/cmd_hello.hxx +1 -0
  23. package/deps/couchbase-cxx-client/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  24. package/deps/couchbase-cxx-client/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  25. package/deps/couchbase-cxx-client/core/protocol/hello_feature.hxx +6 -0
  26. package/deps/couchbase-cxx-client/core/protocol/hello_feature_fmt.hxx +3 -0
  27. package/deps/couchbase-cxx-client/core/range_scan_orchestrator.cxx +22 -1
  28. package/deps/couchbase-cxx-client/core/topology/capabilities.hxx +2 -0
  29. package/deps/couchbase-cxx-client/core/topology/capabilities_fmt.hxx +6 -0
  30. package/deps/couchbase-cxx-client/core/topology/configuration.hxx +10 -0
  31. package/deps/couchbase-cxx-client/core/topology/configuration_json.hxx +4 -1
  32. package/deps/couchbase-cxx-client/couchbase/collection.hxx +111 -0
  33. package/deps/couchbase-cxx-client/couchbase/get_and_lock_options.hxx +2 -2
  34. package/deps/couchbase-cxx-client/couchbase/get_and_touch_options.hxx +2 -2
  35. package/deps/couchbase-cxx-client/couchbase/get_options.hxx +2 -2
  36. package/deps/couchbase-cxx-client/couchbase/insert_options.hxx +3 -3
  37. package/deps/couchbase-cxx-client/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  38. package/deps/couchbase-cxx-client/couchbase/lookup_in_any_replica_options.hxx +101 -0
  39. package/deps/couchbase-cxx-client/couchbase/lookup_in_options.hxx +2 -2
  40. package/deps/couchbase-cxx-client/couchbase/lookup_in_replica_result.hxx +74 -0
  41. package/deps/couchbase-cxx-client/couchbase/lookup_in_result.hxx +26 -0
  42. package/deps/couchbase-cxx-client/couchbase/mutate_in_options.hxx +2 -2
  43. package/deps/couchbase-cxx-client/couchbase/remove_options.hxx +2 -2
  44. package/deps/couchbase-cxx-client/couchbase/replace_options.hxx +3 -3
  45. package/deps/couchbase-cxx-client/couchbase/touch_options.hxx +2 -2
  46. package/deps/couchbase-cxx-client/couchbase/unlock_options.hxx +2 -2
  47. package/deps/couchbase-cxx-client/couchbase/upsert_options.hxx +3 -3
  48. package/deps/couchbase-cxx-client/docs/cbc-analytics.md +3 -2
  49. package/deps/couchbase-cxx-client/docs/cbc-get.md +3 -2
  50. package/deps/couchbase-cxx-client/docs/cbc-pillowfight.md +3 -2
  51. package/deps/couchbase-cxx-client/docs/cbc-query.md +3 -2
  52. package/deps/couchbase-cxx-client/test/test_integration_subdoc.cxx +655 -0
  53. package/deps/couchbase-cxx-client/test/utils/logger.cxx +7 -0
  54. package/deps/couchbase-cxx-client/tools/utils.cxx +9 -2
  55. package/dist/binding.d.ts +47 -0
  56. package/dist/collection.d.ts +53 -1
  57. package/dist/collection.js +139 -1
  58. package/dist/couchbase.d.ts +15 -0
  59. package/dist/couchbase.js +22 -1
  60. package/dist/crudoptypes.d.ts +24 -0
  61. package/dist/crudoptypes.js +14 -1
  62. package/package.json +13 -13
  63. package/src/binding.cpp +28 -0
  64. package/src/connection.cpp +4 -0
  65. package/src/connection.hpp +2 -0
  66. package/src/connection_autogen.cpp +28 -0
  67. package/src/jstocbpp_autogen.hpp +262 -0
@@ -0,0 +1,97 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-Present 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
+ #include "lookup_in_replica.hxx"
19
+ #include "core/impl/subdoc/path_flags.hxx"
20
+
21
+ #include <couchbase/error_codes.hxx>
22
+
23
+ namespace couchbase::core::impl
24
+ {
25
+ std::error_code
26
+ lookup_in_replica_request::encode_to(lookup_in_replica_request::encoded_request_type& encoded, mcbp_context&& /* context */)
27
+ {
28
+ for (std::size_t i = 0; i < specs.size(); ++i) {
29
+ specs[i].original_index_ = i;
30
+ }
31
+ std::stable_sort(specs.begin(), specs.end(), [](const auto& lhs, const auto& rhs) {
32
+ /* move XATTRs to the beginning of the vector */
33
+ return core::impl::subdoc::has_xattr_path_flag(lhs.flags_) && !core::impl::subdoc::has_xattr_path_flag(rhs.flags_);
34
+ });
35
+
36
+ encoded.opaque(opaque);
37
+ encoded.partition(partition);
38
+ encoded.body().id(id);
39
+ encoded.body().read_replica(true);
40
+ encoded.body().specs(specs);
41
+ return {};
42
+ }
43
+
44
+ lookup_in_replica_response
45
+ lookup_in_replica_request::make_response(key_value_error_context&& ctx, const encoded_response_type& encoded) const
46
+ {
47
+
48
+ bool deleted = false;
49
+ couchbase::cas cas{};
50
+ std::vector<lookup_in_replica_response::entry> fields{};
51
+ std::error_code ec = ctx.ec();
52
+ std::optional<std::size_t> first_error_index{};
53
+ std::optional<std::string> first_error_path{};
54
+
55
+ if (encoded.status() == key_value_status_code::subdoc_success_deleted ||
56
+ encoded.status() == key_value_status_code::subdoc_multi_path_failure_deleted) {
57
+ deleted = true;
58
+ }
59
+ if (!ctx.ec()) {
60
+ fields.resize(specs.size());
61
+ for (size_t i = 0; i < specs.size(); ++i) {
62
+ const auto& req_entry = specs[i];
63
+ fields[i].original_index = req_entry.original_index_;
64
+ fields[i].path = req_entry.path_;
65
+ fields[i].opcode = static_cast<protocol::subdoc_opcode>(req_entry.opcode_);
66
+ fields[i].status = key_value_status_code::success;
67
+ }
68
+ for (size_t i = 0; i < encoded.body().fields().size(); ++i) {
69
+ const auto& res_entry = encoded.body().fields()[i];
70
+ fields[i].status = res_entry.status;
71
+ fields[i].ec =
72
+ protocol::map_status_code(protocol::client_opcode::subdoc_multi_mutation, static_cast<std::uint16_t>(res_entry.status));
73
+ if (!fields[i].ec && !ctx.ec()) {
74
+ ec = fields[i].ec;
75
+ }
76
+ if (!first_error_index && !fields[i].ec) {
77
+ first_error_index = i;
78
+ first_error_path = fields[i].path;
79
+ }
80
+ fields[i].exists =
81
+ res_entry.status == key_value_status_code::success || res_entry.status == key_value_status_code::subdoc_success_deleted;
82
+ fields[i].value = utils::to_binary(res_entry.value);
83
+ }
84
+ if (!ec) {
85
+ cas = encoded.cas();
86
+ }
87
+ std::sort(fields.begin(), fields.end(), [](const auto& lhs, const auto& rhs) { return lhs.original_index < rhs.original_index; });
88
+ }
89
+
90
+ return lookup_in_replica_response{
91
+ make_subdocument_error_context(ctx, ec, first_error_path, first_error_index, deleted),
92
+ cas,
93
+ std::move(fields),
94
+ deleted,
95
+ };
96
+ }
97
+ } // namespace couchbase::core::impl
@@ -0,0 +1,67 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2020-Present 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 "core/error_context/key_value.hxx"
21
+ #include "core/impl/subdoc/command.hxx"
22
+ #include "core/io/mcbp_context.hxx"
23
+ #include "core/io/retry_context.hxx"
24
+ #include "core/protocol/client_request.hxx"
25
+ #include "core/protocol/cmd_lookup_in_replica.hxx"
26
+ #include "core/public_fwd.hxx"
27
+ #include "core/timeout_defaults.hxx"
28
+
29
+ #include <couchbase/lookup_in_result.hxx>
30
+ #include <couchbase/subdocument_error_context.hxx>
31
+
32
+ namespace couchbase::core::impl
33
+ {
34
+ struct lookup_in_replica_response {
35
+ struct entry {
36
+ std::string path;
37
+ couchbase::codec::binary value;
38
+ std::size_t original_index;
39
+ bool exists;
40
+ protocol::subdoc_opcode opcode;
41
+ key_value_status_code status;
42
+ std::error_code ec{};
43
+ };
44
+ subdocument_error_context ctx{};
45
+ couchbase::cas cas{};
46
+ std::vector<entry> fields{};
47
+ bool deleted{ false };
48
+ };
49
+
50
+ struct lookup_in_replica_request {
51
+ using response_type = lookup_in_replica_response;
52
+ using encoded_request_type = protocol::client_request<protocol::lookup_in_replica_request_body>;
53
+ using encoded_response_type = protocol::client_response<protocol::lookup_in_replica_response_body>;
54
+
55
+ document_id id;
56
+ std::vector<couchbase::core::impl::subdoc::command> specs{};
57
+ std::optional<std::chrono::milliseconds> timeout{};
58
+ std::shared_ptr<couchbase::tracing::request_span> parent_span{ nullptr };
59
+ std::uint16_t partition{};
60
+ std::uint32_t opaque{};
61
+ io::retry_context<false> retries{};
62
+
63
+ [[nodiscard]] std::error_code encode_to(encoded_request_type& encoded, mcbp_context&& context);
64
+
65
+ [[nodiscard]] lookup_in_replica_response make_response(key_value_error_context&& ctx, const encoded_response_type& encoded) const;
66
+ };
67
+ } // namespace couchbase::core::impl
@@ -72,16 +72,25 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
72
72
 
73
73
  void execute(std::chrono::milliseconds total_timeout, std::chrono::milliseconds udp_timeout)
74
74
  {
75
- CB_LOG_TRACE("Query DNS-SRV (UDP) address=\"{}:{}\", udp_timeout={}, total_timeout={}{:a}",
75
+ CB_LOG_TRACE("Query DNS-SRV (UDP) address=\"{}:{}\", udp_timeout={}, total_timeout={}",
76
76
  address_.to_string(),
77
77
  port_,
78
78
  udp_timeout,
79
- total_timeout,
80
- spdlog::to_hex(send_buf_));
79
+ total_timeout);
81
80
  asio::ip::udp::endpoint endpoint(address_, port_);
82
81
  udp_.open(endpoint.protocol());
82
+ CB_LOG_PROTOCOL("[DNS, UDP, OUT] host=\"{}\", port={}, buffer_size={}{:a}",
83
+ address_.to_string(),
84
+ port_,
85
+ send_buf_.size(),
86
+ spdlog::to_hex(send_buf_));
83
87
  udp_.async_send_to(
84
- asio::buffer(send_buf_), endpoint, [self = shared_from_this()](std::error_code ec1, std::size_t /* bytes_transferred */) mutable {
88
+ asio::buffer(send_buf_), endpoint, [self = shared_from_this()](std::error_code ec1, std::size_t bytes_transferred1) mutable {
89
+ CB_LOG_PROTOCOL("[DNS, UDP, OUT] host=\"{}\", port={}, rc={}, bytes_sent={}",
90
+ self->address_.to_string(),
91
+ self->port_,
92
+ ec1 ? ec1.message() : "ok",
93
+ bytes_transferred1);
85
94
  if (ec1) {
86
95
  self->udp_deadline_.cancel();
87
96
  CB_LOG_DEBUG("DNS UDP write operation has got error, retrying with TCP, address=\"{}:{}\", ec={}",
@@ -94,6 +103,13 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
94
103
  self->recv_buf_.resize(512);
95
104
  self->udp_.async_receive_from(
96
105
  asio::buffer(self->recv_buf_), self->udp_sender_, [self](std::error_code ec2, std::size_t bytes_transferred) mutable {
106
+ CB_LOG_PROTOCOL("[DNS, UDP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
107
+ self->address_.to_string(),
108
+ self->port_,
109
+ ec2 ? ec2.message() : "ok",
110
+ bytes_transferred,
111
+ spdlog::to_hex(self->recv_buf_.data(), self->recv_buf_.data() + bytes_transferred));
112
+
97
113
  self->udp_deadline_.cancel();
98
114
  if (ec2) {
99
115
  CB_LOG_DEBUG("DNS UDP read operation has got error, retrying with TCP, address=\"{}:{}\", ec={}",
@@ -168,10 +184,18 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
168
184
  auto send_size = static_cast<std::uint16_t>(self->send_buf_.size());
169
185
  self->send_buf_.insert(self->send_buf_.begin(), static_cast<std::uint8_t>(send_size & 0xffU));
170
186
  self->send_buf_.insert(self->send_buf_.begin(), static_cast<std::uint8_t>(send_size >> 8U));
171
- CB_LOG_TRACE(
172
- "Query DNS-SRV (TCP) address=\"{}:{}\"{:a}", self->address_.to_string(), self->port_, spdlog::to_hex(self->send_buf_));
187
+ CB_LOG_PROTOCOL("[DNS, TCP, OUT] host=\"{}\", port={}, buffer_size={}{:a}",
188
+ self->address_.to_string(),
189
+ self->port_,
190
+ self->send_buf_.size(),
191
+ spdlog::to_hex(self->send_buf_));
173
192
  asio::async_write(
174
- self->tcp_, asio::buffer(self->send_buf_), [self](std::error_code ec2, std::size_t /* bytes_transferred */) mutable {
193
+ self->tcp_, asio::buffer(self->send_buf_), [self](std::error_code ec2, std::size_t bytes_transferred2) mutable {
194
+ CB_LOG_PROTOCOL("[DNS, TCP, OUT] host=\"{}\", port={}, rc={}, bytes_sent={}",
195
+ self->address_.to_string(),
196
+ self->port_,
197
+ ec2 ? ec2.message() : "ok",
198
+ bytes_transferred2);
175
199
  if (ec2) {
176
200
  CB_LOG_DEBUG("DNS TCP write operation has been aborted, address=\"{}:{}\", ec={}",
177
201
  self->address_.to_string(),
@@ -186,7 +210,14 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
186
210
  asio::async_read(
187
211
  self->tcp_,
188
212
  asio::buffer(&self->recv_buf_size_, sizeof(std::uint16_t)),
189
- [self](std::error_code ec3, std::size_t /* bytes_transferred */) mutable {
213
+ [self](std::error_code ec3, std::size_t bytes_transferred3) mutable {
214
+ CB_LOG_PROTOCOL("[DNS, TCP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
215
+ self->address_.to_string(),
216
+ self->port_,
217
+ ec3 ? ec3.message() : "ok",
218
+ bytes_transferred3,
219
+ spdlog::to_hex(reinterpret_cast<std::uint8_t*>(&self->recv_buf_size_),
220
+ reinterpret_cast<std::uint8_t*>(&self->recv_buf_size_) + bytes_transferred3));
190
221
  if (ec3) {
191
222
  CB_LOG_DEBUG("DNS TCP buf size read operation has been aborted, address=\"{}:{}\", ec={}",
192
223
  self->address_.to_string(),
@@ -199,8 +230,15 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
199
230
  self->recv_buf_.resize(self->recv_buf_size_);
200
231
  CB_LOG_DEBUG("DNS TCP schedule read of {} bytes", self->recv_buf_size_);
201
232
  asio::async_read(
202
- self->tcp_, asio::buffer(self->recv_buf_), [self](std::error_code ec4, std::size_t bytes_transferred) mutable {
233
+ self->tcp_, asio::buffer(self->recv_buf_), [self](std::error_code ec4, std::size_t bytes_transferred4) mutable {
203
234
  self->deadline_.cancel();
235
+ CB_LOG_PROTOCOL("[DNS, TCP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
236
+ self->address_.to_string(),
237
+ self->port_,
238
+ ec4 ? ec4.message() : "ok",
239
+ bytes_transferred4,
240
+ spdlog::to_hex(self->recv_buf_.data(), self->recv_buf_.data() + bytes_transferred4));
241
+
204
242
  if (ec4) {
205
243
  CB_LOG_DEBUG("DNS TCP read operation has been aborted, address=\"{}:{}\", ec={}",
206
244
  self->address_.to_string(),
@@ -208,7 +246,7 @@ class dns_srv_command : public std::enable_shared_from_this<dns_srv_command>
208
246
  ec4.message());
209
247
  return self->handler_({ ec4 });
210
248
  }
211
- self->recv_buf_.resize(bytes_transferred);
249
+ self->recv_buf_.resize(bytes_transferred4);
212
250
  const dns_message message = dns_codec::decode(self->recv_buf_);
213
251
  dns_srv_response resp{ ec4 };
214
252
  resp.targets.reserve(message.answers.size());
@@ -31,7 +31,10 @@
31
31
 
32
32
  #include <couchbase/error_codes.hxx>
33
33
 
34
+ #include <spdlog/fmt/bin_to_hex.h>
35
+
34
36
  #include <asio.hpp>
37
+
35
38
  #include <list>
36
39
  #include <memory>
37
40
  #include <utility>
@@ -448,7 +451,20 @@ class http_session : public std::enable_shared_from_this<http_session>
448
451
  stream_->async_read_some(
449
452
  asio::buffer(input_buffer_), [self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
450
453
  if (ec == asio::error::operation_aborted || self->stopped_) {
454
+ CB_LOG_PROTOCOL("[HTTP, IN] type={}, host=\"{}\", rc={}, bytes_received={}",
455
+ self->type_,
456
+ self->info_.remote_address(),
457
+ ec ? ec.message() : "ok",
458
+ bytes_transferred);
451
459
  return;
460
+ } else {
461
+ CB_LOG_PROTOCOL("[HTTP, IN] type={}, host=\"{}\", rc={}, bytes_received={}{:a}",
462
+ self->type_,
463
+ self->info_.remote_address(),
464
+ ec ? ec.message() : "ok",
465
+ bytes_transferred,
466
+ spdlog::to_hex(self->input_buffer_.data(),
467
+ self->input_buffer_.data() + static_cast<std::ptrdiff_t>(bytes_transferred)));
452
468
  }
453
469
  self->last_active_ = std::chrono::steady_clock::now();
454
470
  if (ec) {
@@ -495,9 +511,16 @@ class http_session : public std::enable_shared_from_this<http_session>
495
511
  std::vector<asio::const_buffer> buffers;
496
512
  buffers.reserve(writing_buffer_.size());
497
513
  for (auto& buf : writing_buffer_) {
514
+ CB_LOG_PROTOCOL(
515
+ "[HTTP, OUT] type={}, host=\"{}\", buffer_size={}{:a}", type_, info_.remote_address(), buf.size(), spdlog::to_hex(buf));
498
516
  buffers.emplace_back(asio::buffer(buf));
499
517
  }
500
- stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t /* bytes_transferred */) {
518
+ stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
519
+ CB_LOG_PROTOCOL("[HTTP, OUT] type={}, host=\"{}\", rc={}, bytes_sent={}",
520
+ self->type_,
521
+ self->info_.remote_address(),
522
+ ec ? ec.message() : "ok",
523
+ bytes_transferred);
501
524
  if (ec == asio::error::operation_aborted || self->stopped_) {
502
525
  return;
503
526
  }
@@ -1365,7 +1365,20 @@ class mcbp_session_impl
1365
1365
  asio::buffer(input_buffer_),
1366
1366
  [self = shared_from_this(), stream_id = stream_->id()](std::error_code ec, std::size_t bytes_transferred) {
1367
1367
  if (ec == asio::error::operation_aborted || self->stopped_) {
1368
+ CB_LOG_PROTOCOL("[MCBP, IN] host=\"{}\", port={}, rc={}, bytes_received={}",
1369
+ self->endpoint_address_,
1370
+ self->endpoint_.port(),
1371
+ ec ? ec.message() : "ok",
1372
+ bytes_transferred);
1368
1373
  return;
1374
+ } else {
1375
+ CB_LOG_PROTOCOL("[MCBP, IN] host=\"{}\", port={}, rc={}, bytes_received={}{:a}",
1376
+ self->endpoint_address_,
1377
+ self->endpoint_.port(),
1378
+ ec ? ec.message() : "ok",
1379
+ bytes_transferred,
1380
+ spdlog::to_hex(self->input_buffer_.data(),
1381
+ self->input_buffer_.data() + static_cast<std::ptrdiff_t>(bytes_transferred)));
1369
1382
  }
1370
1383
  self->last_active_ = std::chrono::steady_clock::now();
1371
1384
  if (ec) {
@@ -1433,13 +1446,21 @@ class mcbp_session_impl
1433
1446
  std::vector<asio::const_buffer> buffers;
1434
1447
  buffers.reserve(writing_buffer_.size());
1435
1448
  for (auto& buf : writing_buffer_) {
1449
+ CB_LOG_PROTOCOL(
1450
+ "[MCBP, OUT] host=\"{}\", port={}, buffer_size={}{:a}", endpoint_address_, endpoint_.port(), buf.size(), spdlog::to_hex(buf));
1436
1451
  buffers.emplace_back(asio::buffer(buf));
1437
1452
  }
1438
- stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t /*unused*/) {
1453
+ stream_->async_write(buffers, [self = shared_from_this()](std::error_code ec, std::size_t bytes_transferred) {
1454
+ CB_LOG_PROTOCOL("[MCBP, OUT] host=\"{}\", port={}, rc={}, bytes_sent={}",
1455
+ self->endpoint_address_,
1456
+ self->endpoint_.port(),
1457
+ ec ? ec.message() : "ok",
1458
+ bytes_transferred);
1439
1459
  if (ec == asio::error::operation_aborted || self->stopped_) {
1440
1460
  return;
1441
1461
  }
1442
1462
  self->last_active_ = std::chrono::steady_clock::now();
1463
+
1443
1464
  if (ec) {
1444
1465
  CB_LOG_ERROR(R"({} IO error while writing to the socket("{}"): {} ({}))",
1445
1466
  self->log_prefix_,
@@ -127,7 +127,7 @@ custom_rotating_file_sink<Mutex>::add_hook(const std::string& hook)
127
127
 
128
128
  // Payload shouldn't contain anything yet, overwrite it
129
129
  Expects(msg.payload.size() == 0);
130
- msg.payload = hook;
130
+ msg.payload = hookToAdd;
131
131
 
132
132
  spdlog::memory_buf_t formatted;
133
133
  formatter->format(msg, formatted);
@@ -22,7 +22,8 @@
22
22
  #include <spdlog/sinks/stdout_color_sinks.h>
23
23
  #include <spdlog/spdlog.h>
24
24
 
25
- static const std::string logger_name{ "couchbase_cxx_client_file_logger" };
25
+ static const std::string file_logger_name{ "couchbase_cxx_client_file_logger" };
26
+ static const std::string protocol_logger_name{ "couchbase_cxx_client_protocol_logger" };
26
27
 
27
28
  /**
28
29
  * Custom log pattern which the loggers will use.
@@ -39,7 +40,12 @@ static const std::string log_pattern{ "[%Y-%m-%d %T.%e] [%P,%t] [%^%l%$] %oms, %
39
40
  * messages and send them to the sinks, which do the actual writing (to file,
40
41
  * to stream etc.) or further processing.
41
42
  */
42
- static std::shared_ptr<spdlog::logger> file_logger;
43
+ static std::shared_ptr<spdlog::logger> file_logger{};
44
+
45
+ /**
46
+ * Instance of the protocol logger.
47
+ */
48
+ static std::shared_ptr<spdlog::logger> protocol_logger{};
43
49
 
44
50
  namespace couchbase::core::logger
45
51
  {
@@ -146,13 +152,11 @@ is_initialized()
146
152
  return file_logger != nullptr;
147
153
  }
148
154
 
149
- /**
150
- * Initialises the loggers. Called if the logger configuration is
151
- * specified in a separate settings object.
152
- */
153
- std::optional<std::string>
154
- create_file_logger(const configuration& logger_settings)
155
+ std::pair<std::optional<std::string>, std::shared_ptr<spdlog::logger>>
156
+ create_file_logger_impl(const std::string logger_name, const configuration& logger_settings)
155
157
  {
158
+ std::shared_ptr<spdlog::logger> logger{};
159
+
156
160
  auto fname = logger_settings.filename;
157
161
  auto buffersz = logger_settings.buffer_size;
158
162
  auto cyclesz = logger_settings.cycle_size;
@@ -218,7 +222,7 @@ create_file_logger(const configuration& logger_settings)
218
222
  spdlog::drop(logger_name);
219
223
 
220
224
  if (logger_settings.unit_test) {
221
- file_logger = std::make_shared<spdlog::logger>(logger_name, sink);
225
+ logger = std::make_shared<spdlog::logger>(logger_name, sink);
222
226
  } else {
223
227
  // Create the default thread pool for async logging
224
228
  spdlog::init_thread_pool(buffersz, 1);
@@ -226,23 +230,71 @@ create_file_logger(const configuration& logger_settings)
226
230
  // Get the thread pool so that we can actually construct the
227
231
  // object with already created sinks...
228
232
  auto tp = spdlog::thread_pool();
229
- file_logger = std::make_shared<spdlog::async_logger>(logger_name, sink, tp, spdlog::async_overflow_policy::block);
233
+ logger = std::make_shared<spdlog::async_logger>(logger_name, sink, tp, spdlog::async_overflow_policy::block);
230
234
  }
231
235
 
232
- file_logger->set_pattern(log_pattern);
233
- file_logger->set_level(translate_level(logger_settings.log_level));
236
+ logger->set_pattern(log_pattern);
237
+ logger->set_level(translate_level(logger_settings.log_level));
234
238
 
235
239
  // Set the flushing interval policy
236
240
  spdlog::flush_every(std::chrono::seconds(1));
237
241
 
238
- spdlog::register_logger(file_logger);
242
+ spdlog::register_logger(logger);
239
243
  } catch (const spdlog::spdlog_ex& ex) {
240
244
  std::string msg = std::string{ "Log initialization failed: " } + ex.what();
241
- return std::optional<std::string>{ msg };
245
+ return { msg, {} };
246
+ }
247
+ return { {}, logger };
248
+ }
249
+
250
+ /**
251
+ * Initialises the loggers. Called if the logger configuration is
252
+ * specified in a separate settings object.
253
+ */
254
+ std::optional<std::string>
255
+ create_file_logger(const configuration& logger_settings)
256
+ {
257
+ auto [error, logger] = create_file_logger_impl(file_logger_name, logger_settings);
258
+ if (error) {
259
+ return error;
260
+ }
261
+ file_logger = std::move(logger);
262
+ return {};
263
+ }
264
+
265
+ std::optional<std::string>
266
+ create_protocol_logger(const configuration& logger_settings)
267
+ {
268
+ if (logger_settings.filename.empty()) {
269
+ return "File name is missing";
270
+ }
271
+ auto config = logger_settings;
272
+ config.log_level = couchbase::core::logger::level::trace;
273
+ auto [error, logger] = create_file_logger_impl(protocol_logger_name, config);
274
+ if (error) {
275
+ return error;
242
276
  }
277
+ protocol_logger = std::move(logger);
243
278
  return {};
244
279
  }
245
280
 
281
+ bool
282
+ should_log_protocol()
283
+ {
284
+ return protocol_logger != nullptr;
285
+ }
286
+
287
+ namespace detail
288
+ {
289
+ void
290
+ log_protocol(const char* file, int line, const char* function, std::string_view msg)
291
+ {
292
+ if (should_log_protocol()) {
293
+ return protocol_logger->log(spdlog::source_loc{ file, line, function }, spdlog::level::level_enum::trace, msg);
294
+ }
295
+ }
296
+ } // namespace detail
297
+
246
298
  spdlog::logger*
247
299
  get()
248
300
  {
@@ -252,17 +304,20 @@ get()
252
304
  void
253
305
  reset()
254
306
  {
255
- spdlog::drop(logger_name);
307
+ spdlog::drop(file_logger_name);
256
308
  file_logger.reset();
309
+
310
+ spdlog::drop(protocol_logger_name);
311
+ protocol_logger.reset();
257
312
  }
258
313
 
259
314
  void
260
315
  create_blackhole_logger()
261
316
  {
262
317
  // delete if already exists
263
- spdlog::drop(logger_name);
318
+ spdlog::drop(file_logger_name);
264
319
 
265
- file_logger = std::make_shared<spdlog::logger>(logger_name, std::make_shared<spdlog::sinks::null_sink_mt>());
320
+ file_logger = std::make_shared<spdlog::logger>(file_logger_name, std::make_shared<spdlog::sinks::null_sink_mt>());
266
321
 
267
322
  file_logger->set_level(spdlog::level::off);
268
323
  file_logger->set_pattern(log_pattern);
@@ -274,11 +329,11 @@ void
274
329
  create_console_logger()
275
330
  {
276
331
  // delete if already exists
277
- spdlog::drop(logger_name);
332
+ spdlog::drop(file_logger_name);
278
333
 
279
334
  auto stderrsink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
280
335
 
281
- file_logger = std::make_shared<spdlog::logger>(logger_name, stderrsink);
336
+ file_logger = std::make_shared<spdlog::logger>(file_logger_name, stderrsink);
282
337
  file_logger->set_level(spdlog::level::info);
283
338
  file_logger->set_pattern(log_pattern);
284
339
 
@@ -322,8 +377,13 @@ void
322
377
  set_log_levels(level lvl)
323
378
  {
324
379
  auto level = translate_level(lvl);
325
- // Apply the function to each registered spdlog::logger
380
+ // Apply the function to each registered spdlog::logger except protocol logger
326
381
  spdlog::apply_all([level](std::shared_ptr<spdlog::logger> l) {
382
+ if (l->name() == protocol_logger_name) {
383
+ l->set_level(spdlog::level::trace);
384
+ return;
385
+ }
386
+
327
387
  try {
328
388
  l->set_level(level);
329
389
  } catch (const spdlog::spdlog_ex& e) {
@@ -54,6 +54,17 @@ level_from_str(const std::string& str);
54
54
  std::optional<std::string>
55
55
  create_file_logger(const configuration& logger_settings);
56
56
 
57
+ /**
58
+ * Protocol logger writes only communication logs with the nodes.
59
+ *
60
+ * It accepts the same settings as a file logger, and writes only to the filesystem.
61
+ *
62
+ * @param logger_settings
63
+ * @return
64
+ */
65
+ std::optional<std::string>
66
+ create_protocol_logger(const configuration& logger_settings);
67
+
57
68
  /**
58
69
  * Initialize the logger with the blackhole logger object
59
70
  *
@@ -144,6 +155,9 @@ set_log_levels(level lvl);
144
155
  bool
145
156
  should_log(level lvl);
146
157
 
158
+ bool
159
+ should_log_protocol();
160
+
147
161
  namespace detail
148
162
  {
149
163
  /**
@@ -153,6 +167,9 @@ namespace detail
153
167
  */
154
168
  void
155
169
  log(const char* file, int line, const char* function, level lvl, std::string_view msg);
170
+
171
+ void
172
+ log_protocol(const char* file, int line, const char* function, std::string_view msg);
156
173
  } // namespace detail
157
174
 
158
175
  /**
@@ -168,6 +185,13 @@ log(const char* file, int line, const char* function, level lvl, const String& m
168
185
  detail::log(file, line, function, lvl, fmt::format(msg, std::forward<Args>(args)...));
169
186
  }
170
187
 
188
+ template<typename String, typename... Args>
189
+ inline void
190
+ log_protocol(const char* file, int line, const char* function, const String& msg, Args&&... args)
191
+ {
192
+ detail::log_protocol(file, line, function, fmt::format(msg, std::forward<Args>(args)...));
193
+ }
194
+
171
195
  /**
172
196
  * Tell the logger to flush its buffers
173
197
  */
@@ -214,6 +238,13 @@ is_initialized();
214
238
  #define CB_LOG_CRITICAL(...) \
215
239
  COUCHBASE_LOG(__FILE__, __LINE__, COUCHBASE_LOGGER_FUNCTION, couchbase::core::logger::level::critical, __VA_ARGS__)
216
240
 
241
+ #define CB_LOG_PROTOCOL(...) \
242
+ do { \
243
+ if (couchbase::core::logger::should_log_protocol()) { \
244
+ couchbase::core::logger::log_protocol(__FILE__, __LINE__, COUCHBASE_LOGGER_FUNCTION, __VA_ARGS__); \
245
+ } \
246
+ } while (false)
247
+
217
248
  /**
218
249
  * Convenience macros which log with the given level, and message, if the given
219
250
  * level is currently enabled.
@@ -35,3 +35,22 @@
35
35
  * passing TLS trust certificate by value
36
36
  */
37
37
  #define COUCHBASE_CXX_CLIENT_CAN_PASS_TLS_TRUST_CERTIFICATE_BY_VALUE 1
38
+
39
+ /**
40
+ * Range scan is available in the core
41
+ * couchbase::core::range_scan_orchestrator and relevant options in the core API
42
+ */
43
+ #define COUCHBASE_CXX_CLIENT_CORE_HAS_RANGE_SCAN 1
44
+
45
+ /**
46
+ * Query with reads from replica is available:
47
+ * - use_replica field in couchbase::core::operations::query_request
48
+ * - couchbase::query_options::use_replica()
49
+ */
50
+ #define COUCHBASE_CXX_CLIENT_QUERY_READ_FROM_REPLICA 1
51
+
52
+ /**
53
+ * Subdoc read from replica is available in the core
54
+ * couchbase::core::lookup_in_replica support
55
+ */
56
+ #define COUCHBASE_CXX_CLIENT_CORE_HAS_SUBDOC_READ_REPLICA 1