couchbase 3.5.7 → 3.6.0

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/ext/cache/extconf_include.rb +3 -3
  4. data/ext/cache/mozilla-ca-bundle.crt +3 -165
  5. data/ext/cache/mozilla-ca-bundle.sha256 +1 -1
  6. data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/CMakeLists.txt +14 -10
  7. data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy.cc +7 -4
  8. data/ext/couchbase/CMakeLists.txt +12 -1
  9. data/ext/couchbase/cmake/Profiler.cmake +15 -0
  10. data/ext/couchbase/cmake/ThirdPartyDependencies.cmake +2 -2
  11. data/ext/couchbase/cmake/couchbase_cxx_client.pc.in +1 -1
  12. data/ext/couchbase/core/app_telemetry_address.cxx +55 -0
  13. data/ext/couchbase/core/app_telemetry_address.hxx +39 -0
  14. data/ext/couchbase/core/app_telemetry_meter.cxx +753 -0
  15. data/ext/couchbase/core/app_telemetry_meter.hxx +198 -0
  16. data/ext/couchbase/core/app_telemetry_reporter.cxx +895 -0
  17. data/ext/couchbase/core/app_telemetry_reporter.hxx +59 -0
  18. data/ext/couchbase/core/bucket.cxx +77 -35
  19. data/ext/couchbase/core/bucket.hxx +17 -10
  20. data/ext/couchbase/core/cluster.cxx +54 -16
  21. data/ext/couchbase/core/cluster_credentials.cxx +27 -0
  22. data/ext/couchbase/core/cluster_credentials.hxx +36 -0
  23. data/ext/couchbase/core/cluster_options.hxx +12 -0
  24. data/ext/couchbase/core/collections_component.cxx +7 -5
  25. data/ext/couchbase/core/http_component.cxx +6 -0
  26. data/ext/couchbase/core/impl/binary_collection.cxx +4 -0
  27. data/ext/couchbase/core/impl/bucket_manager.cxx +2 -0
  28. data/ext/couchbase/core/impl/cluster.cxx +9 -0
  29. data/ext/couchbase/core/impl/collection.cxx +2 -0
  30. data/ext/couchbase/core/impl/error.cxx +1 -0
  31. data/ext/couchbase/core/impl/logger.cxx +51 -0
  32. data/ext/couchbase/core/impl/replica_utils.cxx +1 -1
  33. data/ext/couchbase/core/impl/transaction_get_multi_replicas_from_preferred_server_group_spec.cxx +32 -0
  34. data/ext/couchbase/core/impl/transaction_get_multi_spec.cxx +30 -0
  35. data/ext/couchbase/core/impl/transaction_op_error_category.cxx +2 -0
  36. data/ext/couchbase/core/io/config_tracker.cxx +6 -6
  37. data/ext/couchbase/core/io/http_command.hxx +35 -11
  38. data/ext/couchbase/core/io/http_session.cxx +10 -0
  39. data/ext/couchbase/core/io/http_session.hxx +4 -0
  40. data/ext/couchbase/core/io/http_session_manager.hxx +83 -34
  41. data/ext/couchbase/core/io/mcbp_command.hxx +41 -2
  42. data/ext/couchbase/core/io/mcbp_session.cxx +52 -19
  43. data/ext/couchbase/core/io/mcbp_session.hxx +3 -0
  44. data/ext/couchbase/core/logger/logger.cxx +46 -0
  45. data/ext/couchbase/core/logger/logger.hxx +41 -1
  46. data/ext/couchbase/core/management/bucket_settings.hxx +1 -0
  47. data/ext/couchbase/core/management/bucket_settings_json.hxx +4 -0
  48. data/ext/couchbase/core/meta/features.hxx +32 -0
  49. data/ext/couchbase/core/operations/document_analytics.cxx +9 -9
  50. data/ext/couchbase/core/operations/document_append.cxx +1 -0
  51. data/ext/couchbase/core/operations/document_append.hxx +1 -0
  52. data/ext/couchbase/core/operations/document_get_all_replicas.hxx +10 -2
  53. data/ext/couchbase/core/operations/document_lookup_in.cxx +4 -0
  54. data/ext/couchbase/core/operations/document_lookup_in_all_replicas.hxx +14 -2
  55. data/ext/couchbase/core/operations/document_lookup_in_any_replica.hxx +4 -0
  56. data/ext/couchbase/core/operations/document_mutate_in.cxx +4 -0
  57. data/ext/couchbase/core/operations/document_mutate_in.hxx +1 -0
  58. data/ext/couchbase/core/operations/document_prepend.cxx +1 -0
  59. data/ext/couchbase/core/operations/document_prepend.hxx +1 -0
  60. data/ext/couchbase/core/operations/document_query.cxx +12 -10
  61. data/ext/couchbase/core/operations/http_noop.cxx +1 -0
  62. data/ext/couchbase/core/operations/management/bucket_create.cxx +3 -0
  63. data/ext/couchbase/core/operations/management/bucket_update.cxx +3 -0
  64. data/ext/couchbase/core/origin.cxx +0 -5
  65. data/ext/couchbase/core/origin.hxx +2 -11
  66. data/ext/couchbase/core/platform/random.cc +6 -3
  67. data/ext/couchbase/core/platform/random.h +2 -2
  68. data/ext/couchbase/core/protocol/cmd_mutate_in.hxx +9 -0
  69. data/ext/couchbase/core/timeout_defaults.hxx +4 -0
  70. data/ext/couchbase/core/topology/configuration.cxx +10 -13
  71. data/ext/couchbase/core/topology/configuration.hxx +14 -15
  72. data/ext/couchbase/core/topology/configuration_json.hxx +6 -0
  73. data/ext/couchbase/core/transactions/async_attempt_context.hxx +22 -2
  74. data/ext/couchbase/core/transactions/attempt_context.hxx +25 -7
  75. data/ext/couchbase/core/transactions/attempt_context_impl.cxx +688 -238
  76. data/ext/couchbase/core/transactions/attempt_context_impl.hxx +91 -12
  77. data/ext/couchbase/core/transactions/exceptions.cxx +5 -0
  78. data/ext/couchbase/core/transactions/exceptions.hxx +20 -0
  79. data/ext/couchbase/core/transactions/exceptions_fmt.hxx +3 -0
  80. data/ext/couchbase/core/transactions/forward_compat.cxx +71 -6
  81. data/ext/couchbase/core/transactions/forward_compat.hxx +45 -59
  82. data/ext/couchbase/core/transactions/get_multi_orchestrator.cxx +616 -0
  83. data/ext/couchbase/core/transactions/get_multi_orchestrator.hxx +61 -0
  84. data/ext/couchbase/core/transactions/internal/doc_record.cxx +8 -0
  85. data/ext/couchbase/core/transactions/internal/doc_record.hxx +16 -5
  86. data/ext/couchbase/core/transactions/internal/exceptions_internal.hxx +12 -0
  87. data/ext/couchbase/core/transactions/internal/transaction_context.hxx +13 -0
  88. data/ext/couchbase/core/transactions/internal/transaction_fields.hxx +1 -0
  89. data/ext/couchbase/core/transactions/staged_mutation.cxx +277 -96
  90. data/ext/couchbase/core/transactions/staged_mutation.hxx +28 -76
  91. data/ext/couchbase/core/transactions/transaction_context.cxx +33 -0
  92. data/ext/couchbase/core/transactions/transaction_get_multi_mode.hxx +28 -0
  93. data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +27 -0
  94. data/ext/couchbase/core/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +71 -0
  95. data/ext/couchbase/core/transactions/transaction_get_multi_result.hxx +66 -0
  96. data/ext/couchbase/core/transactions/transaction_links.hxx +10 -0
  97. data/ext/couchbase/core/transactions/transactions.cxx +8 -3
  98. data/ext/couchbase/core/utils/connection_string.cxx +4 -0
  99. data/ext/couchbase/core/utils/url_codec.cxx +26 -0
  100. data/ext/couchbase/core/utils/url_codec.hxx +11 -0
  101. data/ext/couchbase/core/websocket_codec.cxx +647 -0
  102. data/ext/couchbase/core/websocket_codec.hxx +77 -0
  103. data/ext/couchbase/couchbase/analytics_options.hxx +70 -6
  104. data/ext/couchbase/couchbase/application_telemetry_options.hxx +124 -0
  105. data/ext/couchbase/couchbase/cluster_options.hxx +17 -0
  106. data/ext/couchbase/couchbase/error_codes.hxx +1 -0
  107. data/ext/couchbase/couchbase/logger.hxx +16 -0
  108. data/ext/couchbase/couchbase/management/bucket_settings.hxx +1 -0
  109. data/ext/couchbase/couchbase/query_options.hxx +70 -6
  110. data/ext/couchbase/couchbase/transactions/async_attempt_context.hxx +29 -5
  111. data/ext/couchbase/couchbase/transactions/attempt_context.hxx +24 -7
  112. data/ext/couchbase/couchbase/transactions/transaction_get_multi_mode.hxx +47 -0
  113. data/ext/couchbase/couchbase/transactions/transaction_get_multi_options.hxx +44 -0
  114. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_mode.hxx +46 -0
  115. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_options.hxx +48 -0
  116. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_result.hxx +109 -0
  117. data/ext/couchbase/couchbase/transactions/transaction_get_multi_replicas_from_preferred_server_group_spec.hxx +47 -0
  118. data/ext/couchbase/couchbase/transactions/transaction_get_multi_result.hxx +102 -0
  119. data/ext/couchbase/couchbase/transactions/transaction_get_multi_spec.hxx +45 -0
  120. data/ext/rcb_buckets.cxx +26 -0
  121. data/lib/active_support/cache/couchbase_store.rb +1 -1
  122. data/lib/couchbase/cluster.rb +1 -1
  123. data/lib/couchbase/collection.rb +1 -1
  124. data/lib/couchbase/collection_options.rb +2 -2
  125. data/lib/couchbase/management/analytics_index_manager.rb +4 -4
  126. data/lib/couchbase/management/bucket_manager.rb +8 -2
  127. data/lib/couchbase/protostellar/cluster.rb +2 -2
  128. data/lib/couchbase/protostellar/collection.rb +1 -1
  129. data/lib/couchbase/protostellar/management/collection_query_index_manager.rb +1 -1
  130. data/lib/couchbase/protostellar/request_generator/admin/bucket.rb +4 -4
  131. data/lib/couchbase/protostellar/request_generator/admin/collection.rb +6 -6
  132. data/lib/couchbase/protostellar/request_generator/admin/query.rb +13 -13
  133. data/lib/couchbase/protostellar/request_generator/kv.rb +25 -25
  134. data/lib/couchbase/protostellar/request_generator/query.rb +4 -4
  135. data/lib/couchbase/protostellar/request_generator/search.rb +25 -25
  136. data/lib/couchbase/protostellar/response_converter/search.rb +1 -1
  137. data/lib/couchbase/protostellar/retry/reason.rb +1 -1
  138. data/lib/couchbase/protostellar/timeouts.rb +1 -1
  139. data/lib/couchbase/scope.rb +1 -1
  140. data/lib/couchbase/transcoder_flags.rb +1 -1
  141. data/lib/couchbase/utils/stdlib_logger_adapter.rb +1 -1
  142. data/lib/couchbase/version.rb +1 -1
  143. metadata +47 -19
  144. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/COPYING +0 -0
  145. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/SnappyConfig.cmake.in +0 -0
  146. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/cmake/config.h.in +0 -0
  147. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.cc +0 -0
  148. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-c.h +0 -0
  149. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-internal.h +0 -0
  150. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.cc +0 -0
  151. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-sinksource.h +0 -0
  152. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.cc +0 -0
  153. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-internal.h +0 -0
  154. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy-stubs-public.h.in +0 -0
  155. /data/ext/cache/snappy/{585305c8dbb8f762f2c2e17f937f1cf3ac6cbc9c → 3cde171792b3607f75c14e5011eaf69da4857bd8}/snappy/snappy.h +0 -0
@@ -19,6 +19,7 @@
19
19
 
20
20
  #include <couchbase/build_config.hxx>
21
21
 
22
+ #include "core/app_telemetry_meter.hxx"
22
23
  #include "core/config_listener.hxx"
23
24
  #ifdef COUCHBASE_CXX_CLIENT_COLUMNAR
24
25
  #include "core/columnar/bootstrap_notification_subscriber.hxx"
@@ -74,6 +75,11 @@ public:
74
75
  meter_ = std::move(meter);
75
76
  }
76
77
 
78
+ void set_app_telemetry_meter(std::shared_ptr<core::app_telemetry_meter> app_telemetry_meter)
79
+ {
80
+ app_telemetry_meter_ = std::move(app_telemetry_meter);
81
+ }
82
+
77
83
  auto configuration_capabilities() const -> configuration_capabilities
78
84
  {
79
85
  std::scoped_lock config_lock(config_mutex_);
@@ -203,7 +209,7 @@ public:
203
209
  std::uint16_t port = node.port_or(options_.network, type, options_.enable_tls, 0);
204
210
  if (port != 0) {
205
211
  const auto& hostname = node.hostname_for(options_.network);
206
- auto session = create_session(type, credentials, hostname, port);
212
+ auto session = create_session(type, credentials, hostname, port, node.node_uuid);
207
213
  if (session->is_connected()) {
208
214
  std::scoped_lock lock(sessions_mutex_);
209
215
  busy_sessions_[type].push_back(session);
@@ -217,11 +223,17 @@ public:
217
223
  request,
218
224
  tracer_,
219
225
  meter_,
226
+ app_telemetry_meter_,
220
227
  options_.default_timeout_for(request.type),
221
228
  dispatch_timeout_);
222
229
  #else
223
230
  auto cmd = std::make_shared<operations::http_command<operations::http_noop_request>>(
224
- ctx_, request, tracer_, meter_, options_.default_timeout_for(request.type));
231
+ ctx_,
232
+ request,
233
+ tracer_,
234
+ meter_,
235
+ app_telemetry_meter_,
236
+ options_.default_timeout_for(request.type));
225
237
  #endif
226
238
 
227
239
  cmd->start([start = std::chrono::steady_clock::now(),
@@ -283,6 +295,7 @@ public:
283
295
  error });
284
296
  self->check_in(type, cmd->session_);
285
297
  });
298
+
286
299
  cmd->set_command_session(session);
287
300
  if (!session->is_connected()) {
288
301
  connect_then_send(session, cmd, {}, true);
@@ -300,10 +313,13 @@ public:
300
313
  const std::string& undesired_node = {})
301
314
  -> std::pair<std::error_code, std::shared_ptr<http_session>>
302
315
  {
316
+ std::string node_uuid{};
317
+
303
318
  if (preferred_node.empty() && !undesired_node.empty()) {
304
- auto [hostname, port] = pick_random_node(type, undesired_node);
319
+ auto [hostname, port, uuid] = pick_random_node(type, undesired_node);
305
320
  if (port != 0) {
306
321
  preferred_node = fmt::format("{}:{}", hostname, port);
322
+ node_uuid = uuid;
307
323
  }
308
324
  }
309
325
 
@@ -343,7 +359,7 @@ public:
343
359
  break;
344
360
  }
345
361
  } else {
346
- session = create_session(type, credentials, hostname, port);
362
+ session = create_session(type, credentials, hostname, port, node_uuid);
347
363
  break;
348
364
  }
349
365
  }
@@ -355,12 +371,12 @@ public:
355
371
  session.reset();
356
372
  }
357
373
  if (!session) {
358
- auto [hostname, port] =
374
+ auto [hostname, port, uuid] =
359
375
  preferred_node.empty() ? next_node(type) : lookup_node(type, preferred_node);
360
376
  if (port == 0) {
361
377
  return { errc::common::service_not_available, nullptr };
362
378
  }
363
- session = create_session(type, credentials, hostname, port);
379
+ session = create_session(type, credentials, hostname, port, uuid);
364
380
  }
365
381
  if (session->is_connected()) {
366
382
  busy_sessions_[type].push_back(session);
@@ -472,13 +488,19 @@ public:
472
488
  request,
473
489
  tracer_,
474
490
  meter_,
491
+ app_telemetry_meter_,
475
492
  options_.default_timeout_for(request.type),
476
493
  dispatch_timeout_);
477
494
  cmd->start([self = shared_from_this(), cmd, handler = std::forward<Handler>(handler)](
478
495
  error_union err, io::http_response&& msg) mutable {
479
496
  #else
480
497
  auto cmd = std::make_shared<operations::http_command<Request>>(
481
- ctx_, request, tracer_, meter_, options_.default_timeout_for(request.type));
498
+ ctx_,
499
+ request,
500
+ tracer_,
501
+ meter_,
502
+ app_telemetry_meter_,
503
+ options_.default_timeout_for(request.type));
482
504
  cmd->start([self = shared_from_this(), cmd, handler = std::forward<Handler>(handler)](
483
505
  std::error_code ec, io::http_response&& msg) mutable {
484
506
  #endif
@@ -571,15 +593,15 @@ public:
571
593
 
572
594
  // stop this session and create a new one w/ new hostname + port
573
595
  session->stop();
574
- auto [hostname, port] = preferred_node.empty()
575
- ? self->next_node(session->type())
576
- : self->lookup_node(session->type(), preferred_node);
596
+ auto [hostname, port, node_uuid] = preferred_node.empty()
597
+ ? self->next_node(session->type())
598
+ : self->lookup_node(session->type(), preferred_node);
577
599
  if (port == 0) {
578
600
  cb(errc::common::service_not_available, {});
579
601
  return;
580
602
  }
581
603
  auto new_session =
582
- self->create_session(session->type(), session->credentials(), hostname, port);
604
+ self->create_session(session->type(), session->credentials(), hostname, port, node_uuid);
583
605
  if (new_session->is_connected()) {
584
606
  {
585
607
  const std::scoped_lock inner_lock(self->sessions_mutex_);
@@ -679,15 +701,15 @@ private:
679
701
  }
680
702
  // stop this session and create a new one w/ new hostname + port
681
703
  session->stop();
682
- auto [hostname, port] = preferred_node.empty()
683
- ? self->next_node(session->type())
684
- : self->lookup_node(session->type(), preferred_node);
704
+ auto [hostname, port, node_uuid] = preferred_node.empty()
705
+ ? self->next_node(session->type())
706
+ : self->lookup_node(session->type(), preferred_node);
685
707
  if (port == 0) {
686
708
  cmd->invoke_handler(errc::common::service_not_available, {});
687
709
  return;
688
710
  }
689
711
  auto new_session =
690
- self->create_session(session->type(), session->credentials(), hostname, port);
712
+ self->create_session(session->type(), session->credentials(), hostname, port, node_uuid);
691
713
  cmd->set_command_session(new_session);
692
714
  if (new_session->is_connected()) {
693
715
  std::scoped_lock inner_lock(self->sessions_mutex_);
@@ -707,13 +729,15 @@ private:
707
729
  auto create_session(service_type type,
708
730
  const couchbase::core::cluster_credentials& credentials,
709
731
  const std::string& hostname,
710
- std::uint16_t port) -> std::shared_ptr<http_session>
732
+ std::uint16_t port,
733
+ const std::string& node_uuid) -> std::shared_ptr<http_session>
711
734
  {
712
735
  std::shared_ptr<http_session> session;
713
736
  if (options_.enable_tls) {
714
737
  session = std::make_shared<http_session>(
715
738
  type,
716
739
  client_id_,
740
+ node_uuid,
717
741
  ctx_,
718
742
  tls_,
719
743
  credentials,
@@ -724,6 +748,7 @@ private:
724
748
  session = std::make_shared<http_session>(
725
749
  type,
726
750
  client_id_,
751
+ node_uuid,
727
752
  ctx_,
728
753
  credentials,
729
754
  hostname,
@@ -763,6 +788,7 @@ private:
763
788
  request,
764
789
  tracer_,
765
790
  meter_,
791
+ app_telemetry_meter_,
766
792
  options_.default_timeout_for(request.type),
767
793
  dispatch_timeout_);
768
794
  cmd->start([self = shared_from_this(), cmd, handler = std::forward<Handler>(handler)](
@@ -852,7 +878,7 @@ private:
852
878
  }
853
879
  #endif
854
880
 
855
- auto next_node(service_type type) -> std::pair<std::string, std::uint16_t>
881
+ auto next_node(service_type type) -> std::tuple<std::string, std::uint16_t, std::string>
856
882
  {
857
883
  std::scoped_lock lock(config_mutex_);
858
884
  auto candidates = config_.nodes.size();
@@ -863,10 +889,18 @@ private:
863
889
  next_index_ = (next_index_ + 1) % config_.nodes.size();
864
890
  std::uint16_t port = node.port_or(options_.network, type, options_.enable_tls, 0);
865
891
  if (port != 0) {
866
- return { node.hostname_for(options_.network), port };
892
+ return {
893
+ node.hostname_for(options_.network),
894
+ port,
895
+ node.node_uuid,
896
+ };
867
897
  }
868
898
  }
869
- return { "", static_cast<std::uint16_t>(0U) };
899
+ return {
900
+ "",
901
+ static_cast<std::uint16_t>(0U),
902
+ "",
903
+ };
870
904
  }
871
905
 
872
906
  auto split_host_port(const std::string& address) -> std::pair<std::string, std::uint16_t>
@@ -880,24 +914,30 @@ private:
880
914
  return { hostname, port };
881
915
  }
882
916
 
883
- auto lookup_node(service_type type,
884
- const std::string& preferred_node) -> std::pair<std::string, std::uint16_t>
917
+ auto lookup_node(service_type type, const std::string& preferred_node)
918
+ -> std::tuple<std::string, std::uint16_t, std::string>
885
919
  {
886
920
  std::scoped_lock lock(config_mutex_);
887
921
  auto [hostname, port] = split_host_port(preferred_node);
888
- if (std::none_of(config_.nodes.begin(),
889
- config_.nodes.end(),
890
- [this, type, &h = hostname, &p = port](const auto& node) {
891
- return node.hostname_for(options_.network) == h &&
892
- node.port_or(options_.network, type, options_.enable_tls, 0) == p;
893
- })) {
894
- return { "", static_cast<std::uint16_t>(0U) };
922
+ for (const auto& node : config_.nodes) {
923
+ if (node.hostname_for(options_.network) == hostname &&
924
+ node.port_or(options_.network, type, options_.enable_tls, 0) == port) {
925
+ return {
926
+ hostname,
927
+ port,
928
+ node.node_uuid,
929
+ };
930
+ }
895
931
  }
896
- return { hostname, port };
932
+ return {
933
+ "",
934
+ static_cast<std::uint16_t>(0U),
935
+ "",
936
+ };
897
937
  }
898
938
 
899
- auto pick_random_node(service_type type,
900
- const std::string& undesired_node) -> std::pair<std::string, std::uint16_t>
939
+ auto pick_random_node(service_type type, const std::string& undesired_node)
940
+ -> std::tuple<std::string, std::uint16_t, std::string>
901
941
  {
902
942
  std::vector<topology::configuration::node> candidate_nodes{};
903
943
  {
@@ -913,7 +953,11 @@ private:
913
953
 
914
954
  if (candidate_nodes.empty()) {
915
955
  // Could not find any other nodes
916
- return { "", static_cast<std::uint16_t>(0U) };
956
+ return {
957
+ "",
958
+ static_cast<std::uint16_t>(0U),
959
+ "",
960
+ };
917
961
  }
918
962
 
919
963
  std::vector<topology::configuration::node> selected{};
@@ -922,8 +966,12 @@ private:
922
966
  std::back_inserter(selected),
923
967
  1,
924
968
  std::mt19937{ std::random_device{}() });
925
- return { selected.at(0).hostname_for(options_.network),
926
- selected.at(0).port_or(options_.network, type, options_.enable_tls, 0) };
969
+ const auto& first_selected = selected.at(0);
970
+ return {
971
+ first_selected.hostname_for(options_.network),
972
+ first_selected.port_or(options_.network, type, options_.enable_tls, 0),
973
+ first_selected.node_uuid,
974
+ };
927
975
  }
928
976
 
929
977
  std::string client_id_;
@@ -931,6 +979,7 @@ private:
931
979
  asio::ssl::context& tls_;
932
980
  std::shared_ptr<tracing::tracer_wrapper> tracer_{ nullptr };
933
981
  std::shared_ptr<metrics::meter_wrapper> meter_{ nullptr };
982
+ std::shared_ptr<core::app_telemetry_meter> app_telemetry_meter_{ nullptr };
934
983
  cluster_options options_{};
935
984
 
936
985
  topology::configuration config_{};
@@ -17,6 +17,7 @@
17
17
 
18
18
  #pragma once
19
19
 
20
+ #include "core/app_telemetry_meter.hxx"
20
21
  #include "core/document_id_fmt.hxx"
21
22
  #include "core/error_context/key_value_error_map_info.hxx"
22
23
  #include "core/metrics/meter_wrapper.hxx"
@@ -146,7 +147,12 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
146
147
  span_ = nullptr;
147
148
  }
148
149
  if (handler) {
150
+ const auto& node_uuid = session_ ? session_->node_uuid() : "";
151
+ auto telemetry_recorder =
152
+ manager_->app_telemetry_meter()->value_recorder(node_uuid, manager_->name());
153
+ telemetry_recorder->update_counter(app_telemetry_counter::kv_r_total);
149
154
  if (ec == errc::common::unambiguous_timeout || ec == errc::common::ambiguous_timeout) {
155
+ telemetry_recorder->update_counter(app_telemetry_counter::kv_r_timedout);
150
156
  auto time_left = deadline.expiry() - std::chrono::steady_clock::now();
151
157
  CB_LOG_TRACE(R"([{}] timeout operation id="{}", {}, key="{}", partition={}, time_left={})",
152
158
  session_ ? session_->log_prefix() : manager_->log_prefix(),
@@ -155,6 +161,8 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
155
161
  request.id,
156
162
  request.partition,
157
163
  time_left);
164
+ } else if (ec == errc::common::request_canceled) {
165
+ telemetry_recorder->update_counter(app_telemetry_counter::kv_r_canceled);
158
166
  }
159
167
  handler(ec, std::move(msg));
160
168
  }
@@ -267,6 +275,35 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
267
275
  retry_reason reason,
268
276
  io::mcbp_message&& msg,
269
277
  std::optional<key_value_error_map_info> /* error_info */) mutable {
278
+ {
279
+ auto latency = std::chrono::duration_cast<std::chrono::milliseconds>(
280
+ std::chrono::steady_clock::now() - start);
281
+ auto category = app_telemetry_latency::kv_retrieval;
282
+ switch (encoded_request_type::body_type::opcode) {
283
+ case protocol::client_opcode::upsert:
284
+ case protocol::client_opcode::insert:
285
+ case protocol::client_opcode::replace:
286
+ case protocol::client_opcode::remove:
287
+ case protocol::client_opcode::increment:
288
+ case protocol::client_opcode::decrement:
289
+ case protocol::client_opcode::append:
290
+ case protocol::client_opcode::prepend:
291
+ category = app_telemetry_latency::kv_mutation_nondurable;
292
+ if constexpr (io::mcbp_traits::supports_durability_v<Request>) {
293
+ if (self->request.durability_level != durability_level::none) {
294
+ category = app_telemetry_latency::kv_mutation_durable;
295
+ }
296
+ }
297
+ break;
298
+ default:
299
+ break;
300
+ }
301
+
302
+ auto telemetry_recorder = self->manager_->app_telemetry_meter()->value_recorder(
303
+ self->session_->node_uuid(), self->manager_->name());
304
+ telemetry_recorder->record_latency(category, latency);
305
+ }
306
+
270
307
  metrics::metric_attributes attrs{
271
308
  service_type::key_value,
272
309
  self->request.observability_identifier,
@@ -279,16 +316,18 @@ struct mcbp_command : public std::enable_shared_from_this<mcbp_command<Manager,
279
316
 
280
317
  self->retry_backoff.cancel();
281
318
  if (ec == asio::error::operation_aborted) {
282
- if (self->span_->uses_tags())
319
+ if (self->span_->uses_tags()) {
283
320
  self->span_->add_tag(tracing::attributes::orphan, "aborted");
321
+ }
284
322
  return self->invoke_handler(make_error_code(self->request.retries.idempotent()
285
323
  ? errc::common::unambiguous_timeout
286
324
  : errc::common::ambiguous_timeout));
287
325
  }
288
326
  if (ec == errc::common::request_canceled) {
289
327
  if (!self->request.retries.idempotent() && !allows_non_idempotent_retry(reason)) {
290
- if (self->span_->uses_tags())
328
+ if (self->span_->uses_tags()) {
291
329
  self->span_->add_tag(tracing::attributes::orphan, "canceled");
330
+ }
292
331
  return self->invoke_handler(ec);
293
332
  }
294
333
  return io::retry_orchestrator::maybe_retry(self->manager_, self, reason, ec);
@@ -414,10 +414,11 @@ class mcbp_session_impl
414
414
  const protocol::client_response<protocol::sasl_list_mechs_response_body> resp(
415
415
  std::move(msg));
416
416
  if (resp.status() != key_value_status_code::success) {
417
- auto error_msg =
418
- fmt::format("unexpected message status during bootstrap: {} (opaque={})",
419
- resp.error_message(),
420
- resp.opaque());
417
+ auto error_msg = fmt::format(
418
+ "unexpected message status during bootstrap: {} (opaque={}, context={})",
419
+ resp.error_message(),
420
+ resp.opaque(),
421
+ resp.error_info() ? resp.error_info()->context() : "");
421
422
  last_bootstrap_error_ = { errc::common::authentication_failure,
422
423
  std::move(error_msg),
423
424
  session_->bootstrap_hostname(),
@@ -443,8 +444,11 @@ class mcbp_session_impl
443
444
  req.body().sasl_data(sasl_payload);
444
445
  session_->write_and_flush(req.data());
445
446
  } else {
446
- auto error_msg = fmt::format(
447
- "unable to authenticate: (sasl_code={}, opaque={})", sasl_code, resp.opaque());
447
+ auto error_msg =
448
+ fmt::format("unable to authenticate: (sasl_code={}, opaque={}, context={})",
449
+ sasl_code,
450
+ resp.opaque(),
451
+ resp.error_info() ? resp.error_info()->context() : "");
448
452
  last_bootstrap_error_ = { errc::common::authentication_failure,
449
453
  std::move(error_msg),
450
454
  session_->bootstrap_hostname(),
@@ -453,11 +457,12 @@ class mcbp_session_impl
453
457
  return complete(errc::common::authentication_failure);
454
458
  }
455
459
  } else {
456
- auto error_msg =
457
- fmt::format("{} unexpected message status during bootstrap: {} (opaque={})",
458
- session_->log_prefix_,
459
- resp.error_message(),
460
- resp.opaque());
460
+ auto error_msg = fmt::format(
461
+ "{} unexpected message status during bootstrap: {} (opaque={}, context={})",
462
+ session_->log_prefix_,
463
+ resp.error_message(),
464
+ resp.opaque(),
465
+ resp.error_info() ? resp.error_info()->context() : "");
461
466
  last_bootstrap_error_ = { errc::common::authentication_failure,
462
467
  std::move(error_msg),
463
468
  session_->bootstrap_hostname(),
@@ -473,10 +478,11 @@ class mcbp_session_impl
473
478
  return auth_success();
474
479
  }
475
480
  auto error_msg =
476
- fmt::format("unable to authenticate (opcode={}, status={}, opaque={})",
481
+ fmt::format("unable to authenticate (opcode={}, status={}, opaque={}, context={})",
477
482
  opcode,
478
483
  resp.status(),
479
- resp.opaque());
484
+ resp.opaque(),
485
+ resp.error_info() ? resp.error_info()->context() : "");
480
486
  last_bootstrap_error_ = { errc::common::authentication_failure,
481
487
  std::move(error_msg),
482
488
  session_->bootstrap_hostname(),
@@ -858,19 +864,21 @@ class mcbp_session_impl
858
864
  public:
859
865
  mcbp_session_impl() = delete;
860
866
  mcbp_session_impl(std::string_view client_id,
867
+ std::string_view node_uuid,
861
868
  asio::io_context& ctx,
862
869
  couchbase::core::origin origin,
863
870
  std::shared_ptr<impl::bootstrap_state_listener> state_listener,
864
871
  std::optional<std::string> bucket_name = {},
865
872
  std::vector<protocol::hello_feature> known_features = {})
866
873
  : client_id_(client_id)
874
+ , node_uuid_(node_uuid)
867
875
  , ctx_(ctx)
868
876
  , resolver_(ctx_)
869
877
  , stream_(std::make_unique<plain_stream_impl>(ctx_))
870
878
  , bootstrap_deadline_(ctx_)
871
879
  , connection_deadline_(ctx_)
872
880
  , retry_backoff_(ctx_)
873
- , ping_deadline_(ctx_)
881
+ , ping_timeout_(ctx_)
874
882
  , origin_{ std::move(origin) }
875
883
  , bucket_name_{ std::move(bucket_name) }
876
884
  , supported_features_{ std::move(known_features) }
@@ -883,6 +891,7 @@ public:
883
891
  }
884
892
 
885
893
  mcbp_session_impl(std::string_view client_id,
894
+ std::string_view node_uuid,
886
895
  asio::io_context& ctx,
887
896
  asio::ssl::context& tls,
888
897
  couchbase::core::origin origin,
@@ -890,13 +899,14 @@ public:
890
899
  std::optional<std::string> bucket_name = {},
891
900
  std::vector<protocol::hello_feature> known_features = {})
892
901
  : client_id_(client_id)
902
+ , node_uuid_(node_uuid)
893
903
  , ctx_(ctx)
894
904
  , resolver_(ctx_)
895
905
  , stream_(std::make_unique<tls_stream_impl>(ctx_, tls))
896
906
  , bootstrap_deadline_(ctx_)
897
907
  , connection_deadline_(ctx_)
898
908
  , retry_backoff_(ctx_)
899
- , ping_deadline_(ctx_)
909
+ , ping_timeout_(ctx_)
900
910
  , origin_(std::move(origin))
901
911
  , bucket_name_(std::move(bucket_name))
902
912
  , supported_features_(std::move(known_features))
@@ -998,8 +1008,8 @@ public:
998
1008
  error,
999
1009
  });
1000
1010
  });
1001
- ping_deadline_.expires_after(timeout.value_or(origin_.options().key_value_timeout));
1002
- ping_deadline_.async_wait(
1011
+ ping_timeout_.expires_after(timeout.value_or(origin_.options().key_value_timeout));
1012
+ ping_timeout_.async_wait(
1003
1013
  [self = this->shared_from_this(), opaque = req.opaque()](std::error_code ec) {
1004
1014
  if (ec == asio::error::operation_aborted) {
1005
1015
  return;
@@ -1154,6 +1164,11 @@ public:
1154
1164
  return id_;
1155
1165
  }
1156
1166
 
1167
+ [[nodiscard]] auto node_uuid() const -> const std::string&
1168
+ {
1169
+ return node_uuid_;
1170
+ }
1171
+
1157
1172
  [[nodiscard]] auto is_stopped() const -> bool
1158
1173
  {
1159
1174
  return stopped_;
@@ -1194,7 +1209,7 @@ public:
1194
1209
  bootstrap_deadline_.cancel();
1195
1210
  connection_deadline_.cancel();
1196
1211
  retry_backoff_.cancel();
1197
- ping_deadline_.cancel();
1212
+ ping_timeout_.cancel();
1198
1213
  resolver_.cancel();
1199
1214
  stream_->close([](std::error_code) {
1200
1215
  });
@@ -1711,6 +1726,13 @@ private:
1711
1726
  if (ec) {
1712
1727
  return stop(retry_reason::node_not_available);
1713
1728
  }
1729
+ if (node_uuid_.empty() && config_.has_value()) {
1730
+ for (const auto& node : config_.value().nodes) {
1731
+ if (node.this_node) {
1732
+ node_uuid_ = node.node_uuid;
1733
+ }
1734
+ }
1735
+ }
1714
1736
  state_ = diag::endpoint_state::connected;
1715
1737
  const std::scoped_lock lock(pending_buffer_mutex_);
1716
1738
  bootstrapped_ = true;
@@ -2020,6 +2042,7 @@ private:
2020
2042
  }
2021
2043
 
2022
2044
  const std::string client_id_;
2045
+ std::string node_uuid_;
2023
2046
  const std::string id_{ uuid::to_string(uuid::random()) };
2024
2047
  asio::io_context& ctx_;
2025
2048
  asio::ip::tcp::resolver resolver_;
@@ -2027,7 +2050,7 @@ private:
2027
2050
  asio::steady_timer bootstrap_deadline_;
2028
2051
  asio::steady_timer connection_deadline_;
2029
2052
  asio::steady_timer retry_backoff_;
2030
- asio::steady_timer ping_deadline_;
2053
+ asio::steady_timer ping_timeout_;
2031
2054
  couchbase::core::origin origin_;
2032
2055
  std::optional<std::string> bucket_name_;
2033
2056
  mcbp_parser parser_;
@@ -2092,12 +2115,14 @@ private:
2092
2115
  };
2093
2116
 
2094
2117
  mcbp_session::mcbp_session(const std::string& client_id,
2118
+ const std::string& node_uuid,
2095
2119
  asio::io_context& ctx,
2096
2120
  core::origin origin,
2097
2121
  std::shared_ptr<impl::bootstrap_state_listener> state_listener,
2098
2122
  std::optional<std::string> bucket_name,
2099
2123
  std::vector<protocol::hello_feature> known_features)
2100
2124
  : impl_{ std::make_shared<mcbp_session_impl>(client_id,
2125
+ node_uuid,
2101
2126
  ctx,
2102
2127
  std::move(origin),
2103
2128
  std::move(state_listener),
@@ -2107,6 +2132,7 @@ mcbp_session::mcbp_session(const std::string& client_id,
2107
2132
  }
2108
2133
 
2109
2134
  mcbp_session::mcbp_session(const std::string& client_id,
2135
+ const std::string& node_uuid,
2110
2136
  asio::io_context& ctx,
2111
2137
  asio::ssl::context& tls,
2112
2138
  core::origin origin,
@@ -2114,6 +2140,7 @@ mcbp_session::mcbp_session(const std::string& client_id,
2114
2140
  std::optional<std::string> bucket_name,
2115
2141
  std::vector<protocol::hello_feature> known_features)
2116
2142
  : impl_{ std::make_shared<mcbp_session_impl>(client_id,
2143
+ node_uuid,
2117
2144
  ctx,
2118
2145
  tls,
2119
2146
  std::move(origin),
@@ -2177,6 +2204,12 @@ mcbp_session::id() const -> const std::string&
2177
2204
  return impl_->id();
2178
2205
  }
2179
2206
 
2207
+ auto
2208
+ mcbp_session::node_uuid() const -> const std::string&
2209
+ {
2210
+ return impl_->node_uuid();
2211
+ }
2212
+
2180
2213
  auto
2181
2214
  mcbp_session::bootstrap_address() const -> const std::string&
2182
2215
  {
@@ -90,6 +90,7 @@ public:
90
90
  auto operator=(mcbp_session&& other) -> mcbp_session& = default;
91
91
 
92
92
  mcbp_session(const std::string& client_id,
93
+ const std::string& node_uuid,
93
94
  asio::io_context& ctx,
94
95
  couchbase::core::origin origin,
95
96
  std::shared_ptr<impl::bootstrap_state_listener> state_listener,
@@ -97,6 +98,7 @@ public:
97
98
  std::vector<protocol::hello_feature> known_features = {});
98
99
 
99
100
  mcbp_session(const std::string& client_id,
101
+ const std::string& node_uuid,
100
102
  asio::io_context& ctx,
101
103
  asio::ssl::context& tls,
102
104
  couchbase::core::origin origin,
@@ -115,6 +117,7 @@ public:
115
117
  [[nodiscard]] auto supports_feature(protocol::hello_feature feature) -> bool;
116
118
  [[nodiscard]] auto supported_features() const -> std::vector<protocol::hello_feature>;
117
119
  [[nodiscard]] auto id() const -> const std::string&;
120
+ [[nodiscard]] auto node_uuid() const -> const std::string&;
118
121
  [[nodiscard]] auto remote_address() const -> std::string;
119
122
  [[nodiscard]] auto local_address() const -> std::string;
120
123
  [[nodiscard]] auto bootstrap_address() const -> const std::string&;
@@ -59,6 +59,10 @@ std::shared_ptr<spdlog::logger> file_logger{};
59
59
  std::mutex file_logger_mutex;
60
60
  std::atomic_int file_logger_version{ 0 };
61
61
 
62
+ std::shared_ptr<couchbase::core::logger::log_callback> log_callback{};
63
+ std::mutex log_callback_mutex;
64
+ std::atomic_int log_callback_version{ 0 };
65
+
62
66
  auto
63
67
  get_file_logger() -> std::shared_ptr<spdlog::logger>
64
68
  {
@@ -72,6 +76,27 @@ get_file_logger() -> std::shared_ptr<spdlog::logger>
72
76
  return logger;
73
77
  }
74
78
 
79
+ auto
80
+ get_custom_callback() -> std::shared_ptr<couchbase::core::logger::log_callback>
81
+ {
82
+ thread_local std::shared_ptr<couchbase::core::logger::log_callback> callback{ nullptr };
83
+ thread_local int version{ -1 };
84
+ if (version != log_callback_version) {
85
+ const std::scoped_lock lock(log_callback_mutex);
86
+ callback = log_callback;
87
+ version = log_callback_version;
88
+ }
89
+ return callback;
90
+ }
91
+
92
+ void
93
+ update_callback_logger(const std::shared_ptr<couchbase::core::logger::log_callback>& new_callback)
94
+ {
95
+ const std::scoped_lock lock(log_callback_mutex);
96
+ log_callback = new_callback;
97
+ ++log_callback_version;
98
+ }
99
+
75
100
  void
76
101
  update_file_logger(const std::shared_ptr<spdlog::logger>& new_logger)
77
102
  {
@@ -181,6 +206,14 @@ log(const char* file, int line, const char* function, level lvl, std::string_vie
181
206
  spdlog::source_loc{ file, line, function }, translate_level(lvl), msg);
182
207
  }
183
208
  }
209
+
210
+ void
211
+ log_custom_logger(const char* file, int line, const char* function, level lvl, std::string_view msg)
212
+ {
213
+ if (auto callback = get_custom_callback()) {
214
+ (*callback)(msg, lvl, { file, function, line });
215
+ }
216
+ }
184
217
  } // namespace detail
185
218
 
186
219
  void
@@ -396,6 +429,19 @@ create_console_logger()
396
429
  update_file_logger(new_logger);
397
430
  }
398
431
 
432
+ void
433
+ register_log_callback(log_callback callback)
434
+ {
435
+ auto new_callback = std::make_shared<log_callback>(std::move(callback));
436
+ update_callback_logger(new_callback);
437
+ }
438
+
439
+ void
440
+ unregister_log_callback()
441
+ {
442
+ update_callback_logger(nullptr);
443
+ }
444
+
399
445
  void
400
446
  register_spdlog_logger(const std::shared_ptr<spdlog::logger>& l)
401
447
  {