couchbase 3.4.3 → 3.4.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (148) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/ext/couchbase/CMakeLists.txt +15 -1
  4. data/ext/couchbase/core/bucket.cxx +183 -152
  5. data/ext/couchbase/core/bucket.hxx +17 -4
  6. data/ext/couchbase/core/cluster.hxx +34 -13
  7. data/ext/couchbase/core/cluster_options.hxx +3 -0
  8. data/ext/couchbase/core/crud_component.cxx +51 -22
  9. data/ext/couchbase/core/error_context/key_value.cxx +2 -1
  10. data/ext/couchbase/core/error_context/key_value.hxx +10 -12
  11. data/ext/couchbase/core/impl/build_deferred_query_indexes.cxx +115 -50
  12. data/ext/couchbase/core/impl/cluster.cxx +6 -0
  13. data/ext/couchbase/core/impl/create_bucket.cxx +155 -0
  14. data/ext/couchbase/core/impl/create_query_index.cxx +172 -59
  15. data/ext/couchbase/core/impl/dns_srv_tracker.cxx +2 -1
  16. data/ext/couchbase/core/impl/drop_bucket.cxx +66 -0
  17. data/ext/couchbase/core/impl/drop_query_index.cxx +138 -59
  18. data/ext/couchbase/core/impl/flush_bucket.cxx +66 -0
  19. data/ext/couchbase/core/impl/get_all_buckets.cxx +163 -0
  20. data/ext/couchbase/core/impl/get_all_query_indexes.cxx +67 -37
  21. data/ext/couchbase/core/impl/get_bucket.cxx +153 -0
  22. data/ext/couchbase/core/impl/internal_manager_error_context.cxx +113 -0
  23. data/ext/couchbase/core/impl/internal_manager_error_context.hxx +60 -0
  24. data/ext/couchbase/core/impl/key_value_error_category.cxx +2 -4
  25. data/ext/couchbase/core/impl/key_value_error_context.cxx +98 -0
  26. data/ext/couchbase/core/impl/lookup_in.cxx +1 -0
  27. data/ext/couchbase/core/impl/lookup_in_all_replicas.cxx +176 -0
  28. data/ext/couchbase/core/impl/lookup_in_all_replicas.hxx +80 -0
  29. data/ext/couchbase/core/impl/lookup_in_any_replica.cxx +167 -0
  30. data/ext/couchbase/core/impl/lookup_in_any_replica.hxx +75 -0
  31. data/ext/couchbase/core/impl/lookup_in_replica.cxx +97 -0
  32. data/ext/couchbase/core/impl/lookup_in_replica.hxx +67 -0
  33. data/ext/couchbase/core/impl/manager_error_context.cxx +100 -0
  34. data/ext/couchbase/core/impl/query.cxx +1 -0
  35. data/ext/couchbase/core/impl/query_error_context.cxx +75 -0
  36. data/ext/couchbase/core/impl/update_bucket.cxx +130 -0
  37. data/ext/couchbase/core/impl/watch_query_indexes.cxx +53 -29
  38. data/ext/couchbase/core/io/dns_client.cxx +111 -40
  39. data/ext/couchbase/core/io/dns_config.cxx +5 -4
  40. data/ext/couchbase/core/io/http_session.hxx +24 -1
  41. data/ext/couchbase/core/io/mcbp_command.hxx +9 -2
  42. data/ext/couchbase/core/io/mcbp_session.cxx +80 -43
  43. data/ext/couchbase/core/io/mcbp_session.hxx +4 -3
  44. data/ext/couchbase/core/logger/custom_rotating_file_sink.cxx +1 -1
  45. data/ext/couchbase/core/logger/logger.cxx +80 -20
  46. data/ext/couchbase/core/logger/logger.hxx +31 -0
  47. data/ext/couchbase/core/meta/features.hxx +25 -0
  48. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +192 -0
  49. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +188 -0
  50. data/ext/couchbase/core/operations/document_query.cxx +11 -0
  51. data/ext/couchbase/core/operations/document_query.hxx +1 -0
  52. data/ext/couchbase/core/operations.hxx +2 -0
  53. data/ext/couchbase/core/origin.cxx +270 -0
  54. data/ext/couchbase/core/origin.hxx +2 -0
  55. data/ext/couchbase/core/protocol/client_response.hxx +1 -0
  56. data/ext/couchbase/core/protocol/cmd_hello.hxx +1 -0
  57. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.cxx +107 -0
  58. data/ext/couchbase/core/protocol/cmd_lookup_in_replica.hxx +137 -0
  59. data/ext/couchbase/core/protocol/hello_feature.hxx +6 -0
  60. data/ext/couchbase/core/protocol/hello_feature_fmt.hxx +3 -0
  61. data/ext/couchbase/core/protocol/status.cxx +2 -2
  62. data/ext/couchbase/core/range_scan_options.cxx +3 -27
  63. data/ext/couchbase/core/range_scan_options.hxx +13 -17
  64. data/ext/couchbase/core/range_scan_orchestrator.cxx +388 -170
  65. data/ext/couchbase/core/range_scan_orchestrator.hxx +13 -2
  66. data/ext/couchbase/core/range_scan_orchestrator_options.hxx +5 -3
  67. data/ext/couchbase/core/scan_options.hxx +0 -19
  68. data/ext/couchbase/core/scan_result.cxx +19 -5
  69. data/ext/couchbase/core/scan_result.hxx +5 -2
  70. data/ext/couchbase/core/timeout_defaults.hxx +2 -3
  71. data/ext/couchbase/core/topology/capabilities.hxx +3 -0
  72. data/ext/couchbase/core/topology/capabilities_fmt.hxx +8 -0
  73. data/ext/couchbase/core/topology/collections_manifest_fmt.hxx +1 -1
  74. data/ext/couchbase/core/topology/configuration.hxx +15 -0
  75. data/ext/couchbase/core/topology/configuration_json.hxx +6 -1
  76. data/ext/couchbase/core/utils/connection_string.cxx +62 -47
  77. data/ext/couchbase/core/utils/connection_string.hxx +1 -0
  78. data/ext/couchbase/couchbase/analytics_error_context.hxx +1 -1
  79. data/ext/couchbase/couchbase/behavior_options.hxx +19 -2
  80. data/ext/couchbase/couchbase/bucket_manager.hxx +135 -0
  81. data/ext/couchbase/couchbase/build_query_index_options.hxx +0 -30
  82. data/ext/couchbase/couchbase/cluster.hxx +14 -0
  83. data/ext/couchbase/couchbase/collection.hxx +111 -0
  84. data/ext/couchbase/couchbase/collection_query_index_manager.hxx +7 -48
  85. data/ext/couchbase/couchbase/create_bucket_options.hxx +41 -0
  86. data/ext/couchbase/couchbase/create_primary_query_index_options.hxx +0 -29
  87. data/ext/couchbase/couchbase/create_query_index_options.hxx +0 -33
  88. data/ext/couchbase/couchbase/drop_bucket_options.hxx +41 -0
  89. data/ext/couchbase/couchbase/drop_primary_query_index_options.hxx +0 -30
  90. data/ext/couchbase/couchbase/drop_query_index_options.hxx +0 -31
  91. data/ext/couchbase/couchbase/error_codes.hxx +1 -2
  92. data/ext/couchbase/couchbase/error_context.hxx +10 -2
  93. data/ext/couchbase/couchbase/flush_bucket_options.hxx +41 -0
  94. data/ext/couchbase/{core/topology/error_map_fmt.hxx → couchbase/fmt/key_value_error_map_attribute.hxx} +21 -21
  95. data/ext/couchbase/couchbase/get_all_buckets_options.hxx +44 -0
  96. data/ext/couchbase/couchbase/get_all_query_indexes_options.hxx +0 -30
  97. data/ext/couchbase/couchbase/get_and_lock_options.hxx +2 -2
  98. data/ext/couchbase/couchbase/get_and_touch_options.hxx +2 -2
  99. data/ext/couchbase/couchbase/get_bucket_options.hxx +43 -0
  100. data/ext/couchbase/couchbase/get_options.hxx +2 -2
  101. data/ext/couchbase/couchbase/insert_options.hxx +3 -3
  102. data/ext/couchbase/couchbase/key_value_error_context.hxx +7 -2
  103. data/ext/couchbase/couchbase/lookup_in_all_replicas_options.hxx +109 -0
  104. data/ext/couchbase/couchbase/lookup_in_any_replica_options.hxx +101 -0
  105. data/ext/couchbase/couchbase/lookup_in_options.hxx +2 -2
  106. data/ext/couchbase/couchbase/lookup_in_replica_result.hxx +74 -0
  107. data/ext/couchbase/couchbase/lookup_in_result.hxx +26 -0
  108. data/ext/couchbase/couchbase/management/bucket_settings.hxx +116 -0
  109. data/ext/couchbase/couchbase/manager_error_context.hxx +29 -53
  110. data/ext/couchbase/couchbase/mutate_in_options.hxx +2 -2
  111. data/ext/couchbase/couchbase/query_error_context.hxx +3 -1
  112. data/ext/couchbase/couchbase/query_index_manager.hxx +16 -83
  113. data/ext/couchbase/couchbase/query_options.hxx +18 -0
  114. data/ext/couchbase/couchbase/remove_options.hxx +2 -2
  115. data/ext/couchbase/couchbase/replace_options.hxx +3 -3
  116. data/ext/couchbase/couchbase/security_options.hxx +15 -0
  117. data/ext/couchbase/couchbase/subdocument_error_context.hxx +4 -2
  118. data/ext/couchbase/couchbase/touch_options.hxx +2 -2
  119. data/ext/couchbase/couchbase/unlock_options.hxx +2 -2
  120. data/ext/couchbase/couchbase/update_bucket_options.hxx +41 -0
  121. data/ext/couchbase/couchbase/upsert_options.hxx +3 -3
  122. data/ext/couchbase/couchbase/watch_query_indexes_options.hxx +0 -31
  123. data/ext/couchbase/test/CMakeLists.txt +1 -0
  124. data/ext/couchbase/test/test_integration_collections.cxx +6 -0
  125. data/ext/couchbase/test/test_integration_crud.cxx +5 -0
  126. data/ext/couchbase/test/test_integration_examples.cxx +137 -1
  127. data/ext/couchbase/test/test_integration_management.cxx +709 -266
  128. data/ext/couchbase/test/test_integration_query.cxx +19 -7
  129. data/ext/couchbase/test/test_integration_range_scan.cxx +351 -112
  130. data/ext/couchbase/test/test_integration_search.cxx +10 -1
  131. data/ext/couchbase/test/test_integration_subdoc.cxx +655 -0
  132. data/ext/couchbase/test/test_transaction_public_async_api.cxx +13 -12
  133. data/ext/couchbase/test/test_transaction_public_blocking_api.cxx +27 -21
  134. data/ext/couchbase/test/test_unit_connection_string.cxx +29 -0
  135. data/ext/couchbase/test/test_unit_query.cxx +75 -0
  136. data/ext/couchbase.cxx +583 -29
  137. data/ext/revisions.rb +3 -3
  138. data/lib/couchbase/cluster.rb +1 -1
  139. data/lib/couchbase/collection.rb +108 -0
  140. data/lib/couchbase/collection_options.rb +100 -0
  141. data/lib/couchbase/errors.rb +5 -0
  142. data/lib/couchbase/key_value_scan.rb +125 -0
  143. data/lib/couchbase/options.rb +151 -0
  144. data/lib/couchbase/scope.rb +1 -1
  145. data/lib/couchbase/utils/time.rb +14 -1
  146. data/lib/couchbase/version.rb +1 -1
  147. metadata +41 -7
  148. data/ext/couchbase/core/impl/collection_query_index_manager.cxx +0 -93
@@ -379,7 +379,7 @@ class mcbp_session_impl
379
379
  Expects(protocol::is_valid_server_request_opcode(msg.header.opcode));
380
380
  switch (static_cast<protocol::server_opcode>(msg.header.opcode)) {
381
381
  case protocol::server_opcode::cluster_map_change_notification: {
382
- protocol::cmd_info info{ session_->endpoint_address_, session_->endpoint_.port() };
382
+ protocol::cmd_info info{ session_->bootstrap_hostname_, session_->bootstrap_port_number_ };
383
383
  if (session_->origin_.options().dump_configuration) {
384
384
  std::string_view config_text{ reinterpret_cast<const char*>(msg.body.data()), msg.body.size() };
385
385
  CB_LOG_TRACE(
@@ -404,7 +404,7 @@ class mcbp_session_impl
404
404
  CB_LOG_WARNING("{} unexpected server request: opcode={:x}, opaque={}{:a}{:a}",
405
405
  session_->log_prefix_,
406
406
  msg.header.opcode,
407
- msg.header.opaque,
407
+ utils::byte_swap(msg.header.opaque),
408
408
  spdlog::to_hex(msg.header_data()),
409
409
  spdlog::to_hex(msg.body));
410
410
  }
@@ -416,7 +416,7 @@ class mcbp_session_impl
416
416
  session_->log_prefix_,
417
417
  magic,
418
418
  msg.header.opcode,
419
- msg.header.opaque,
419
+ utils::byte_swap(msg.header.opaque),
420
420
  spdlog::to_hex(msg.header_data()),
421
421
  spdlog::to_hex(msg.body));
422
422
  break;
@@ -473,7 +473,7 @@ class mcbp_session_impl
473
473
  Expects(protocol::is_valid_client_opcode(msg.header.opcode));
474
474
  switch (auto opcode = static_cast<protocol::client_opcode>(msg.header.opcode)) {
475
475
  case protocol::client_opcode::get_cluster_config: {
476
- protocol::cmd_info info{ session_->endpoint_address_, session_->endpoint_.port() };
476
+ protocol::cmd_info info{ session_->bootstrap_hostname_, session_->bootstrap_port_number_ };
477
477
  if (session_->origin_.options().dump_configuration) {
478
478
  std::string_view config_text{ reinterpret_cast<const char*>(msg.body.data()), msg.body.size() };
479
479
  CB_LOG_TRACE("{} configuration from get_cluster_config response (size={}, endpoint=\"{}:{}\"), {}",
@@ -781,13 +781,10 @@ class mcbp_session_impl
781
781
  return;
782
782
  }
783
783
  std::tie(bootstrap_hostname_, bootstrap_port_) = origin_.next_address();
784
- log_prefix_ = fmt::format("[{}/{}/{}/{}] <{}:{}>",
785
- client_id_,
786
- id_,
787
- stream_->log_prefix(),
788
- bucket_name_.value_or("-"),
789
- bootstrap_hostname_,
790
- bootstrap_port_);
784
+ bootstrap_port_number_ = gsl::narrow_cast<std::uint16_t>(std::stoul(bootstrap_port_, nullptr, 10));
785
+ bootstrap_address_ = fmt::format("{}:{}", bootstrap_hostname_, bootstrap_port_);
786
+ log_prefix_ =
787
+ fmt::format("[{}/{}/{}/{}] <{}>", client_id_, id_, stream_->log_prefix(), bucket_name_.value_or("-"), bootstrap_address_);
791
788
  CB_LOG_DEBUG("{} attempt to establish MCBP connection", log_prefix_);
792
789
 
793
790
  async_resolve(origin_.options().use_ip_protocol,
@@ -807,7 +804,7 @@ class mcbp_session_impl
807
804
  return stopped_;
808
805
  }
809
806
 
810
- void on_stop(utils::movable_function<void(retry_reason)> handler)
807
+ void on_stop(utils::movable_function<void()> handler)
811
808
  {
812
809
  on_stop_handler_ = std::move(handler);
813
810
  }
@@ -863,7 +860,7 @@ class mcbp_session_impl
863
860
  config_listeners_.clear();
864
861
  state_ = diag::endpoint_state::disconnected;
865
862
  if (auto on_stop = std::move(on_stop_handler_); on_stop) {
866
- on_stop(reason);
863
+ on_stop();
867
864
  }
868
865
  }
869
866
 
@@ -874,7 +871,7 @@ class mcbp_session_impl
874
871
  }
875
872
  std::uint32_t opaque{ 0 };
876
873
  std::memcpy(&opaque, buf.data() + 12, sizeof(opaque));
877
- CB_LOG_TRACE("{} MCBP send, opaque={}, {:n}", log_prefix_, opaque, spdlog::to_hex(buf.begin(), buf.begin() + 24));
874
+ CB_LOG_TRACE("{} MCBP send, opaque={}, {:n}", log_prefix_, utils::byte_swap(opaque), spdlog::to_hex(buf.begin(), buf.begin() + 24));
878
875
  std::scoped_lock lock(output_buffer_mutex_);
879
876
  output_buffer_.emplace_back(std::move(buf));
880
877
  }
@@ -1058,6 +1055,11 @@ class mcbp_session_impl
1058
1055
  return config_->index_for_this_node();
1059
1056
  }
1060
1057
 
1058
+ [[nodiscard]] const std::string& bootstrap_address() const
1059
+ {
1060
+ return bootstrap_address_;
1061
+ }
1062
+
1061
1063
  [[nodiscard]] const std::string& bootstrap_hostname() const
1062
1064
  {
1063
1065
  return bootstrap_hostname_;
@@ -1068,6 +1070,11 @@ class mcbp_session_impl
1068
1070
  return bootstrap_port_;
1069
1071
  }
1070
1072
 
1073
+ [[nodiscard]] std::uint16_t bootstrap_port_number() const
1074
+ {
1075
+ return bootstrap_port_number_;
1076
+ }
1077
+
1071
1078
  [[nodiscard]] std::uint32_t next_opaque()
1072
1079
  {
1073
1080
  return ++opaque_;
@@ -1161,15 +1168,15 @@ class mcbp_session_impl
1161
1168
  CB_LOG_TRACE("{} configuration from not_my_vbucket response (size={}, endpoint=\"{}:{}\"), {}",
1162
1169
  log_prefix_,
1163
1170
  config_text.size(),
1164
- endpoint_address_,
1165
- endpoint_.port(),
1171
+ bootstrap_hostname_,
1172
+ bootstrap_port_number_,
1166
1173
  config_text);
1167
1174
  }
1168
- auto config = protocol::parse_config(config_text, endpoint_address_, endpoint_.port());
1175
+ auto config = protocol::parse_config(config_text, bootstrap_hostname_, bootstrap_port_number_);
1169
1176
  CB_LOG_DEBUG("{} received not_my_vbucket status for {}, opaque={} with config rev={} in the payload",
1170
1177
  log_prefix_,
1171
1178
  protocol::client_opcode(msg.header.opcode),
1172
- msg.header.opaque,
1179
+ utils::byte_swap(msg.header.opaque),
1173
1180
  config.rev_str());
1174
1181
  update_configuration(std::move(config));
1175
1182
  }
@@ -1204,6 +1211,12 @@ class mcbp_session_impl
1204
1211
  CB_LOG_DEBUG(R"({} server returned {} ({}), it must be transient condition, retrying)", log_prefix_, ec.value(), ec.message());
1205
1212
  return initiate_bootstrap();
1206
1213
  }
1214
+ if (!origin_.exhausted() && ec == errc::common::authentication_failure) {
1215
+ CB_LOG_DEBUG(
1216
+ R"({} server returned authentication_failure, but the bootstrap list is not exhausted yet. It must be transient condition, retrying)",
1217
+ log_prefix_);
1218
+ return initiate_bootstrap();
1219
+ }
1207
1220
 
1208
1221
  if (!bootstrapped_ && bootstrap_callback_) {
1209
1222
  bootstrap_deadline_.cancel();
@@ -1266,16 +1279,15 @@ class mcbp_session_impl
1266
1279
  }
1267
1280
  last_active_ = std::chrono::steady_clock::now();
1268
1281
  if (it != endpoints_.end()) {
1269
- CB_LOG_DEBUG("{} connecting to {}:{}, timeout={}ms",
1270
- log_prefix_,
1271
- it->endpoint().address().to_string(),
1272
- it->endpoint().port(),
1273
- origin_.options().connect_timeout.count());
1282
+ auto hostname = it->endpoint().address().to_string();
1283
+ auto port = it->endpoint().port();
1284
+ CB_LOG_DEBUG("{} connecting to {}:{}, timeout={}ms", log_prefix_, hostname, port, origin_.options().connect_timeout.count());
1274
1285
  connection_deadline_.expires_after(origin_.options().connect_timeout);
1275
- connection_deadline_.async_wait([self = shared_from_this()](const auto timer_ec) {
1286
+ connection_deadline_.async_wait([self = shared_from_this(), hostname, port](const auto timer_ec) {
1276
1287
  if (timer_ec == asio::error::operation_aborted || self->stopped_) {
1277
1288
  return;
1278
1289
  }
1290
+ CB_LOG_DEBUG("{} unable to connect to {}:{} in time, reconnecting", self->log_prefix_, hostname, port);
1279
1291
  return self->stream_->close([self](std::error_code) { self->initiate_bootstrap(); });
1280
1292
  });
1281
1293
  stream_->async_connect(it->endpoint(),
@@ -1353,7 +1365,20 @@ class mcbp_session_impl
1353
1365
  asio::buffer(input_buffer_),
1354
1366
  [self = shared_from_this(), stream_id = stream_->id()](std::error_code ec, std::size_t bytes_transferred) {
1355
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);
1356
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)));
1357
1382
  }
1358
1383
  self->last_active_ = std::chrono::steady_clock::now();
1359
1384
  if (ec) {
@@ -1382,8 +1407,10 @@ class mcbp_session_impl
1382
1407
  if (self->stopped_) {
1383
1408
  return;
1384
1409
  }
1385
- CB_LOG_TRACE(
1386
- "{} MCBP recv, opaque={}, {:n}", self->log_prefix_, msg.header.opaque, spdlog::to_hex(msg.header_data()));
1410
+ CB_LOG_TRACE("{} MCBP recv, opaque={}, {:n}",
1411
+ self->log_prefix_,
1412
+ utils::byte_swap(msg.header.opaque),
1413
+ spdlog::to_hex(msg.header_data()));
1387
1414
  if (self->bootstrapped_) {
1388
1415
  self->handler_->handle(std::move(msg));
1389
1416
  } else {
@@ -1419,13 +1446,21 @@ class mcbp_session_impl
1419
1446
  std::vector<asio::const_buffer> buffers;
1420
1447
  buffers.reserve(writing_buffer_.size());
1421
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));
1422
1451
  buffers.emplace_back(asio::buffer(buf));
1423
1452
  }
1424
- 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);
1425
1459
  if (ec == asio::error::operation_aborted || self->stopped_) {
1426
1460
  return;
1427
1461
  }
1428
1462
  self->last_active_ = std::chrono::steady_clock::now();
1463
+
1429
1464
  if (ec) {
1430
1465
  CB_LOG_ERROR(R"({} IO error while writing to the socket("{}"): {} ({}))",
1431
1466
  self->log_prefix_,
@@ -1462,7 +1497,7 @@ class mcbp_session_impl
1462
1497
  std::mutex command_handlers_mutex_{};
1463
1498
  std::map<std::uint32_t, command_handler> command_handlers_{};
1464
1499
  std::vector<std::shared_ptr<config_listener>> config_listeners_{};
1465
- utils::movable_function<void(retry_reason)> on_stop_handler_{};
1500
+ utils::movable_function<void()> on_stop_handler_{};
1466
1501
 
1467
1502
  std::atomic_bool bootstrapped_{ false };
1468
1503
  std::atomic_bool stopped_{ false };
@@ -1482,6 +1517,8 @@ class mcbp_session_impl
1482
1517
  std::mutex writing_buffer_mutex_{};
1483
1518
  std::string bootstrap_hostname_{};
1484
1519
  std::string bootstrap_port_{};
1520
+ std::string bootstrap_address_{};
1521
+ std::uint16_t bootstrap_port_number_{};
1485
1522
  asio::ip::tcp::endpoint endpoint_{}; // connected endpoint
1486
1523
  std::string endpoint_address_{}; // cached string with endpoint address
1487
1524
  asio::ip::tcp::endpoint local_endpoint_{};
@@ -1582,22 +1619,16 @@ mcbp_session::supports_feature(protocol::hello_feature feature)
1582
1619
  return impl_->supports_feature(feature);
1583
1620
  }
1584
1621
 
1585
- // const std::string&
1586
- // mcbp_session::id() const
1587
- // {
1588
- // return impl_->id();
1589
- // }
1590
- std::string
1622
+ const std::string&
1591
1623
  mcbp_session::id() const
1592
1624
  {
1593
- if (impl_) {
1594
- return fmt::format("{}, {}, {}, refcnt={}",
1595
- reinterpret_cast<const void*>(this),
1596
- reinterpret_cast<const void*>(impl_.get()),
1597
- impl_->id(),
1598
- impl_.use_count());
1599
- }
1600
- return fmt::format("{}, nullptr", reinterpret_cast<const void*>(this));
1625
+ return impl_->id();
1626
+ }
1627
+
1628
+ const std::string&
1629
+ mcbp_session::bootstrap_address() const
1630
+ {
1631
+ return impl_->bootstrap_address();
1601
1632
  }
1602
1633
 
1603
1634
  std::string
@@ -1624,6 +1655,12 @@ mcbp_session::bootstrap_port() const
1624
1655
  return impl_->bootstrap_port();
1625
1656
  }
1626
1657
 
1658
+ std::uint16_t
1659
+ mcbp_session::bootstrap_port_number() const
1660
+ {
1661
+ return impl_->bootstrap_port_number();
1662
+ }
1663
+
1627
1664
  void
1628
1665
  mcbp_session::write_and_subscribe(std::uint32_t opaque, std::vector<std::byte>&& data, command_handler&& handler)
1629
1666
  {
@@ -1637,7 +1674,7 @@ mcbp_session::bootstrap(utils::movable_function<void(std::error_code, topology::
1637
1674
  }
1638
1675
 
1639
1676
  void
1640
- mcbp_session::on_stop(utils::movable_function<void(retry_reason)> handler)
1677
+ mcbp_session::on_stop(utils::movable_function<void()> handler)
1641
1678
  {
1642
1679
  return impl_->on_stop(std::move(handler));
1643
1680
  }
@@ -101,17 +101,18 @@ class mcbp_session
101
101
  [[nodiscard]] mcbp_context context() const;
102
102
  [[nodiscard]] bool supports_feature(protocol::hello_feature feature);
103
103
  [[nodiscard]] std::vector<protocol::hello_feature> supported_features() const;
104
- //[[nodiscard]] const std::string& id() const;
105
- [[nodiscard]] std::string id() const;
104
+ [[nodiscard]] const std::string& id() const;
106
105
  [[nodiscard]] std::string remote_address() const;
107
106
  [[nodiscard]] std::string local_address() const;
107
+ [[nodiscard]] const std::string& bootstrap_address() const;
108
108
  [[nodiscard]] const std::string& bootstrap_hostname() const;
109
109
  [[nodiscard]] const std::string& bootstrap_port() const;
110
+ [[nodiscard]] std::uint16_t bootstrap_port_number() const;
110
111
  void write_and_subscribe(std::shared_ptr<mcbp::queue_request>, std::shared_ptr<response_handler> handler);
111
112
  void write_and_subscribe(std::uint32_t opaque, std::vector<std::byte>&& data, command_handler&& handler);
112
113
  void bootstrap(utils::movable_function<void(std::error_code, topology::configuration)>&& handler,
113
114
  bool retry_on_bucket_not_found = false);
114
- void on_stop(utils::movable_function<void(retry_reason)> handler);
115
+ void on_stop(utils::movable_function<void()> handler);
115
116
  void stop(retry_reason reason);
116
117
  [[nodiscard]] std::size_t index() const;
117
118
  [[nodiscard]] bool has_config() const;
@@ -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.
@@ -29,3 +29,28 @@
29
29
  * couchbase::core::meta::sdk_version() function is available
30
30
  */
31
31
  #define COUCHBASE_CXX_CLIENT_HAS_SDK_SEMVER 1
32
+
33
+ /**
34
+ * couchbase::core::cluster_options and couchbase::security_options support
35
+ * passing TLS trust certificate by value
36
+ */
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