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
@@ -0,0 +1,753 @@
1
+ /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
+ /*
3
+ * Copyright 2024-Present Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
6
+ * except in compliance with the License. You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software distributed under
11
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12
+ * ANY KIND, either express or implied. See the License for the specific language governing
13
+ * permissions and limitations under the License.
14
+ */
15
+
16
+ #include "app_telemetry_meter.hxx"
17
+
18
+ #include "logger/logger.hxx"
19
+ #include "meta/version.hxx"
20
+ #include "topology/configuration.hxx"
21
+ #include "utils/json.hxx"
22
+
23
+ #include <spdlog/fmt/bin_to_hex.h>
24
+ #include <spdlog/fmt/bundled/core.h>
25
+ #include <tao/json/to_string.hpp>
26
+ #include <tao/json/value.hpp>
27
+
28
+ #include <array>
29
+ #include <atomic>
30
+ #include <chrono>
31
+ #include <map>
32
+ #include <mutex>
33
+ #include <utility>
34
+
35
+ namespace couchbase::core
36
+ {
37
+ class app_telemetry_meter_impl
38
+ {
39
+ public:
40
+ app_telemetry_meter_impl() = default;
41
+ app_telemetry_meter_impl(app_telemetry_meter_impl&&) = default;
42
+ app_telemetry_meter_impl(const app_telemetry_meter_impl&) = delete;
43
+ auto operator=(app_telemetry_meter_impl&&) -> app_telemetry_meter_impl& = default;
44
+ auto operator=(const app_telemetry_meter_impl&) -> app_telemetry_meter_impl& = delete;
45
+ virtual ~app_telemetry_meter_impl() = default;
46
+
47
+ virtual auto enabled() -> bool = 0;
48
+ virtual auto nothing_to_report() -> bool = 0;
49
+ virtual void update_config(const topology::configuration& config) = 0;
50
+ virtual auto value_recorder(const std::string& node_uuid, const std::string& bucket_name)
51
+ -> std::shared_ptr<app_telemetry_value_recorder> = 0;
52
+ virtual void generate_to(std::vector<std::byte>& output_buffer, const std::string& agent) = 0;
53
+ };
54
+
55
+ namespace detail
56
+ {
57
+ class byte_appender
58
+ {
59
+ public:
60
+ using iterator_category = std::output_iterator_tag;
61
+ using value_type = void;
62
+
63
+ explicit byte_appender(std::vector<std::byte>& output)
64
+ : buffer_{ output }
65
+ {
66
+ }
67
+
68
+ auto operator=(char ch) -> byte_appender&
69
+ {
70
+ buffer_.push_back(static_cast<std::byte>(ch));
71
+ return *this;
72
+ }
73
+
74
+ auto operator*() -> byte_appender&
75
+ {
76
+ return *this;
77
+ }
78
+
79
+ auto operator++() const -> byte_appender
80
+ {
81
+ return *this;
82
+ }
83
+
84
+ auto operator++(int) const -> byte_appender
85
+ {
86
+ return *this;
87
+ }
88
+
89
+ private:
90
+ std::vector<std::byte>& buffer_;
91
+ };
92
+ } // namespace detail
93
+ } // namespace couchbase::core
94
+
95
+ template<>
96
+ struct fmt::detail::is_output_iterator<couchbase::core::detail::byte_appender, char>
97
+ : std::true_type {
98
+ };
99
+
100
+ namespace couchbase::core
101
+ {
102
+ namespace
103
+ {
104
+ struct node_labels {
105
+ std::string node;
106
+ std::optional<std::string> alt_node;
107
+ };
108
+
109
+ struct kv_non_durable_histogram {
110
+ const char* name;
111
+ std::atomic_uint64_t le_1ms{};
112
+ std::atomic_uint64_t le_10ms{};
113
+ std::atomic_uint64_t le_100ms{};
114
+ std::atomic_uint64_t le_500ms{};
115
+ std::atomic_uint64_t le_1s{};
116
+ std::atomic_uint64_t le_2_5s{};
117
+ std::atomic_uint64_t inf{};
118
+ std::atomic_uint64_t sum{};
119
+ std::atomic_uint64_t count{};
120
+
121
+ void generate_to(detail::byte_appender& output,
122
+ const std::string& node_uuid,
123
+ const node_labels& labels,
124
+ const std::string& bucket,
125
+ const std::string& agent)
126
+ {
127
+ if (count > 0) {
128
+ std::string lbuf{};
129
+ fmt::format_to(back_inserter(lbuf), "node_uuid=\"{}\"", node_uuid);
130
+ if (!labels.node.empty()) {
131
+ fmt::format_to(back_inserter(lbuf), ",node=\"{}\"", labels.node);
132
+ }
133
+ if (const auto& alt = labels.alt_node; alt && !alt->empty()) {
134
+ fmt::format_to(back_inserter(lbuf), ",alt_node=\"{}\"", alt.value());
135
+ }
136
+ if (!bucket.empty()) {
137
+ fmt::format_to(back_inserter(lbuf), ",bucket=\"{}\"", bucket);
138
+ }
139
+ fmt::format_to(back_inserter(lbuf), ",agent={}", agent);
140
+ fmt::format_to(output, "{}_bucket{{le=\"1\",{}}} {}\n", name, lbuf, le_1ms.load());
141
+ fmt::format_to(output, "{}_bucket{{le=\"10\",{}}} {}\n", name, lbuf, le_10ms.load());
142
+ fmt::format_to(output, "{}_bucket{{le=\"100\",{}}} {}\n", name, lbuf, le_100ms.load());
143
+ fmt::format_to(output, "{}_bucket{{le=\"500\",{}}} {}\n", name, lbuf, le_500ms.load());
144
+ fmt::format_to(output, "{}_bucket{{le=\"1000\",{}}} {}\n", name, lbuf, le_1s.load());
145
+ fmt::format_to(output, "{}_bucket{{le=\"2500\",{}}} {}\n", name, lbuf, le_2_5s.load());
146
+ fmt::format_to(output, "{}_bucket{{le=\"+Inf\",{}}} {}\n", name, lbuf, inf.load());
147
+ fmt::format_to(output, "{}_sum{{{}}} {}\n", name, lbuf, sum.load());
148
+ fmt::format_to(output, "{}_count{{{}}} {}\n", name, lbuf, count.load());
149
+ }
150
+ }
151
+ };
152
+
153
+ struct kv_durable_histogram {
154
+ const char* name;
155
+ std::atomic_uint64_t le_10ms{};
156
+ std::atomic_uint64_t le_100ms{};
157
+ std::atomic_uint64_t le_500ms{};
158
+ std::atomic_uint64_t le_1s{};
159
+ std::atomic_uint64_t le_2s{};
160
+ std::atomic_uint64_t le_10s{};
161
+ std::atomic_uint64_t inf{};
162
+ std::atomic_uint64_t sum{};
163
+ std::atomic_uint64_t count{};
164
+
165
+ void generate_to(detail::byte_appender& output,
166
+ const std::string& node_uuid,
167
+ const node_labels& labels,
168
+ const std::string& bucket,
169
+ const std::string& agent)
170
+ {
171
+ if (count > 0) {
172
+ std::string lbuf{};
173
+ fmt::format_to(back_inserter(lbuf), "node_uuid=\"{}\"", node_uuid);
174
+ if (!labels.node.empty()) {
175
+ fmt::format_to(back_inserter(lbuf), ",node=\"{}\"", labels.node);
176
+ }
177
+ if (const auto& alt = labels.alt_node; alt && !alt->empty()) {
178
+ fmt::format_to(back_inserter(lbuf), ",alt_node=\"{}\"", alt.value());
179
+ }
180
+ if (!bucket.empty()) {
181
+ fmt::format_to(back_inserter(lbuf), ",bucket=\"{}\"", bucket);
182
+ }
183
+ fmt::format_to(back_inserter(lbuf), ",agent={}", agent);
184
+ fmt::format_to(output, "{}_bucket{{le=\"10\",{}}} {}\n", name, lbuf, le_10ms.load());
185
+ fmt::format_to(output, "{}_bucket{{le=\"100\",{}}} {}\n", name, lbuf, le_100ms.load());
186
+ fmt::format_to(output, "{}_bucket{{le=\"500\",{}}} {}\n", name, lbuf, le_500ms.load());
187
+ fmt::format_to(output, "{}_bucket{{le=\"1000\",{}}} {}\n", name, lbuf, le_1s.load());
188
+ fmt::format_to(output, "{}_bucket{{le=\"2000\",{}}} {}\n", name, lbuf, le_2s.load());
189
+ fmt::format_to(output, "{}_bucket{{le=\"10000\",{}}} {}\n", name, lbuf, le_10s.load());
190
+ fmt::format_to(output, "{}_bucket{{le=\"+Inf\",{}}} {}\n", name, lbuf, inf.load());
191
+ fmt::format_to(output, "{}_sum{{{}}} {}\n", name, lbuf, sum.load());
192
+ fmt::format_to(output, "{}_count{{{}}} {}\n", name, lbuf, count.load());
193
+ }
194
+ }
195
+ };
196
+
197
+ struct http_histogram {
198
+ const char* name;
199
+ std::atomic_uint64_t le_100ms{};
200
+ std::atomic_uint64_t le_1s{};
201
+ std::atomic_uint64_t le_10s{};
202
+ std::atomic_uint64_t le_30s{};
203
+ std::atomic_uint64_t le_75s{};
204
+ std::atomic_uint64_t inf{};
205
+ std::atomic_uint64_t sum{};
206
+ std::atomic_uint64_t count{};
207
+
208
+ void generate_to(detail::byte_appender& output,
209
+ const std::string& node_uuid,
210
+ const node_labels& labels,
211
+ const std::string& bucket,
212
+ const std::string& agent)
213
+ {
214
+ if (count > 0) {
215
+ std::string lbuf{};
216
+ fmt::format_to(back_inserter(lbuf), "node_uuid=\"{}\"", node_uuid);
217
+ if (!labels.node.empty()) {
218
+ fmt::format_to(back_inserter(lbuf), ",node=\"{}\"", labels.node);
219
+ }
220
+ if (const auto& alt = labels.alt_node; alt && !alt->empty()) {
221
+ fmt::format_to(back_inserter(lbuf), ",alt_node=\"{}\"", alt.value());
222
+ }
223
+ if (!bucket.empty()) {
224
+ fmt::format_to(back_inserter(lbuf), ",bucket=\"{}\"", bucket);
225
+ }
226
+ fmt::format_to(back_inserter(lbuf), ",agent={}", agent);
227
+ fmt::format_to(output, "{}_bucket{{le=\"100\",{}}} {}\n", name, lbuf, le_100ms.load());
228
+ fmt::format_to(output, "{}_bucket{{le=\"1000\",{}}} {}\n", name, lbuf, le_1s.load());
229
+ fmt::format_to(output, "{}_bucket{{le=\"10000\",{}}} {}\n", name, lbuf, le_10s.load());
230
+ fmt::format_to(output, "{}_bucket{{le=\"30000\",{}}} {}\n", name, lbuf, le_30s.load());
231
+ fmt::format_to(output, "{}_bucket{{le=\"75000\",{}}} {}\n", name, lbuf, le_75s.load());
232
+ fmt::format_to(output, "{}_bucket{{le=\"+Inf\",{}}} {}\n", name, lbuf, inf.load());
233
+ fmt::format_to(output, "{}_sum{{{}}} {}\n", name, lbuf, sum.load() / 1000);
234
+ fmt::format_to(output, "{}_count{{{}}} {}\n", name, lbuf, count.load());
235
+ }
236
+ }
237
+ };
238
+
239
+ constexpr auto max_number_of_counters{ static_cast<std::size_t>(
240
+ app_telemetry_counter::number_of_elements) };
241
+
242
+ constexpr auto
243
+ is_valid_app_telemetry_counter(std::size_t name) -> bool
244
+ {
245
+ return name > static_cast<std::size_t>(app_telemetry_counter::unknown) &&
246
+ name < static_cast<std::size_t>(app_telemetry_counter::number_of_elements);
247
+ }
248
+
249
+ constexpr auto
250
+ app_telemetry_counter_name(std::size_t name) -> const char*
251
+ {
252
+ switch (static_cast<app_telemetry_counter>(name)) {
253
+
254
+ case app_telemetry_counter::kv_r_timedout:
255
+ return "sdk_kv_r_timedout";
256
+ case app_telemetry_counter::kv_r_canceled:
257
+ return "sdk_kv_r_canceled";
258
+ case app_telemetry_counter::kv_r_total:
259
+ return "sdk_kv_r_total";
260
+
261
+ case app_telemetry_counter::query_r_timedout:
262
+ return "sdk_query_r_timedout";
263
+ case app_telemetry_counter::query_r_canceled:
264
+ return "sdk_query_r_canceled";
265
+ case app_telemetry_counter::query_r_total:
266
+ return "sdk_query_r_total";
267
+
268
+ case app_telemetry_counter::search_r_timedout:
269
+ return "sdk_search_r_timedout";
270
+ case app_telemetry_counter::search_r_canceled:
271
+ return "sdk_search_r_canceled";
272
+ case app_telemetry_counter::search_r_total:
273
+ return "sdk_search_r_total";
274
+
275
+ case app_telemetry_counter::analytics_r_timedout:
276
+ return "sdk_analytics_r_timedout";
277
+ case app_telemetry_counter::analytics_r_canceled:
278
+ return "sdk_analytics_r_canceled";
279
+ case app_telemetry_counter::analytics_r_total:
280
+ return "sdk_analytics_r_total";
281
+
282
+ case app_telemetry_counter::management_r_timedout:
283
+ return "sdk_management_r_timedout";
284
+ case app_telemetry_counter::management_r_canceled:
285
+ return "sdk_management_r_canceled";
286
+ case app_telemetry_counter::management_r_total:
287
+ return "sdk_management_r_total";
288
+
289
+ case app_telemetry_counter::eventing_r_timedout:
290
+ return "sdk_eventing_r_timedout";
291
+ case app_telemetry_counter::eventing_r_canceled:
292
+ return "sdk_eventing_r_canceled";
293
+ case app_telemetry_counter::eventing_r_total:
294
+ return "sdk_eventing_r_total";
295
+
296
+ case app_telemetry_counter::unknown:
297
+ case app_telemetry_counter::number_of_elements:
298
+ break;
299
+ }
300
+ return "";
301
+ }
302
+
303
+ class null_app_telemetry_value_recorder : public app_telemetry_value_recorder
304
+ {
305
+ public:
306
+ void record_latency(app_telemetry_latency /* name */,
307
+ std::chrono::milliseconds /* interval */) override
308
+ {
309
+ /* do nothing */
310
+ }
311
+
312
+ void update_counter(app_telemetry_counter /* name */) override
313
+ {
314
+ /* do nothing */
315
+ }
316
+ };
317
+
318
+ class null_app_telemetry_meter_impl : public app_telemetry_meter_impl
319
+ {
320
+ private:
321
+ std::shared_ptr<null_app_telemetry_value_recorder> instance_{
322
+ std::make_shared<null_app_telemetry_value_recorder>()
323
+ };
324
+
325
+ public:
326
+ void update_config(const topology::configuration& /* config */) override
327
+ {
328
+ /* do nothing */
329
+ }
330
+
331
+ auto value_recorder(const std::string& /* node_uuid */, const std::string& /* bucket_name */)
332
+ -> std::shared_ptr<app_telemetry_value_recorder> override
333
+ {
334
+ return instance_;
335
+ }
336
+
337
+ auto enabled() -> bool override
338
+ {
339
+ return false;
340
+ }
341
+
342
+ auto nothing_to_report() -> bool override
343
+ {
344
+ return true;
345
+ }
346
+
347
+ void generate_to(std::vector<std::byte>& /* output_buffer */,
348
+ const std::string& /* agent */) override
349
+ {
350
+ /* do nothing */
351
+ }
352
+ };
353
+
354
+ class default_app_telemetry_value_recorder : public app_telemetry_value_recorder
355
+ {
356
+ public:
357
+ default_app_telemetry_value_recorder(std::string node_uuid, std::string bucket_name)
358
+ : node_uuid_{ std::move(node_uuid) }
359
+ , bucket_name_{ std::move(bucket_name) }
360
+ {
361
+ }
362
+
363
+ void record_latency(app_telemetry_latency name, std::chrono::milliseconds interval) override
364
+ {
365
+ // NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index)
366
+ switch (name) {
367
+ case app_telemetry_latency::unknown:
368
+ case app_telemetry_latency::number_of_elements:
369
+ return;
370
+ case app_telemetry_latency::kv_retrieval:
371
+ ++kv_retrieval_.count;
372
+ kv_retrieval_.sum += static_cast<std::uint64_t>(interval.count());
373
+ if (interval <= std::chrono::milliseconds{ 1 }) {
374
+ ++kv_retrieval_.le_1ms;
375
+ }
376
+ if (interval <= std::chrono::milliseconds{ 10 }) {
377
+ ++kv_retrieval_.le_10ms;
378
+ }
379
+ if (interval <= std::chrono::milliseconds{ 100 }) {
380
+ ++kv_retrieval_.le_100ms;
381
+ }
382
+ if (interval <= std::chrono::milliseconds{ 500 }) {
383
+ ++kv_retrieval_.le_500ms;
384
+ }
385
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
386
+ ++kv_retrieval_.le_1s;
387
+ }
388
+ if (interval <= std::chrono::milliseconds{ 2500 }) {
389
+ ++kv_retrieval_.le_2_5s;
390
+ }
391
+ ++kv_retrieval_.inf;
392
+ break;
393
+
394
+ case app_telemetry_latency::kv_mutation_nondurable:
395
+ ++kv_mutation_nondurable_.count;
396
+ kv_mutation_nondurable_.sum += static_cast<std::uint64_t>(interval.count());
397
+ if (interval <= std::chrono::milliseconds{ 1 }) {
398
+ ++kv_mutation_nondurable_.le_1ms;
399
+ }
400
+ if (interval <= std::chrono::milliseconds{ 10 }) {
401
+ ++kv_mutation_nondurable_.le_10ms;
402
+ }
403
+ if (interval <= std::chrono::milliseconds{ 100 }) {
404
+ ++kv_mutation_nondurable_.le_100ms;
405
+ }
406
+ if (interval <= std::chrono::milliseconds{ 500 }) {
407
+ ++kv_mutation_nondurable_.le_500ms;
408
+ }
409
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
410
+ ++kv_mutation_nondurable_.le_1s;
411
+ }
412
+ if (interval <= std::chrono::milliseconds{ 2500 }) {
413
+ ++kv_mutation_nondurable_.le_2_5s;
414
+ }
415
+ ++kv_mutation_nondurable_.inf;
416
+ break;
417
+
418
+ case app_telemetry_latency::kv_mutation_durable:
419
+ ++kv_mutation_durable_.count;
420
+ kv_mutation_durable_.sum += static_cast<std::uint64_t>(interval.count());
421
+ if (interval <= std::chrono::milliseconds{ 10 }) {
422
+ ++kv_mutation_durable_.le_10ms;
423
+ }
424
+ if (interval <= std::chrono::milliseconds{ 100 }) {
425
+ ++kv_mutation_durable_.le_100ms;
426
+ }
427
+ if (interval <= std::chrono::milliseconds{ 500 }) {
428
+ ++kv_mutation_durable_.le_500ms;
429
+ }
430
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
431
+ ++kv_mutation_durable_.le_1s;
432
+ }
433
+ if (interval <= std::chrono::milliseconds{ 2000 }) {
434
+ ++kv_mutation_durable_.le_2s;
435
+ }
436
+ if (interval <= std::chrono::milliseconds{ 10000 }) {
437
+ ++kv_mutation_durable_.le_10s;
438
+ }
439
+ ++kv_mutation_durable_.inf;
440
+ break;
441
+
442
+ case app_telemetry_latency::query:
443
+ ++query_.count;
444
+ query_.sum += static_cast<std::uint64_t>(interval.count());
445
+ if (interval <= std::chrono::milliseconds{ 100 }) {
446
+ ++query_.le_100ms;
447
+ }
448
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
449
+ ++query_.le_1s;
450
+ }
451
+ if (interval <= std::chrono::milliseconds{ 10000 }) {
452
+ ++query_.le_10s;
453
+ }
454
+ if (interval <= std::chrono::milliseconds{ 30000 }) {
455
+ ++query_.le_30s;
456
+ }
457
+ if (interval <= std::chrono::milliseconds{ 75000 }) {
458
+ ++query_.le_75s;
459
+ }
460
+ ++query_.inf;
461
+ break;
462
+
463
+ case app_telemetry_latency::search:
464
+ ++search_.count;
465
+ search_.sum += static_cast<std::uint64_t>(interval.count());
466
+ if (interval <= std::chrono::milliseconds{ 100 }) {
467
+ ++search_.le_100ms;
468
+ }
469
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
470
+ ++search_.le_1s;
471
+ }
472
+ if (interval <= std::chrono::milliseconds{ 10000 }) {
473
+ ++search_.le_10s;
474
+ }
475
+ if (interval <= std::chrono::milliseconds{ 30000 }) {
476
+ ++search_.le_30s;
477
+ }
478
+ if (interval <= std::chrono::milliseconds{ 75000 }) {
479
+ ++search_.le_75s;
480
+ }
481
+ ++search_.inf;
482
+ break;
483
+
484
+ case app_telemetry_latency::analytics:
485
+ ++analytics_.count;
486
+ analytics_.sum += static_cast<std::uint64_t>(interval.count());
487
+ if (interval <= std::chrono::milliseconds{ 100 }) {
488
+ ++analytics_.le_100ms;
489
+ }
490
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
491
+ ++analytics_.le_1s;
492
+ }
493
+ if (interval <= std::chrono::milliseconds{ 10000 }) {
494
+ ++analytics_.le_10s;
495
+ }
496
+ if (interval <= std::chrono::milliseconds{ 30000 }) {
497
+ ++analytics_.le_30s;
498
+ }
499
+ if (interval <= std::chrono::milliseconds{ 75000 }) {
500
+ ++analytics_.le_75s;
501
+ }
502
+ ++analytics_.inf;
503
+ break;
504
+
505
+ case app_telemetry_latency::management:
506
+ ++management_.count;
507
+ management_.sum += static_cast<std::uint64_t>(interval.count());
508
+ if (interval <= std::chrono::milliseconds{ 100 }) {
509
+ ++management_.le_100ms;
510
+ }
511
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
512
+ ++management_.le_1s;
513
+ }
514
+ if (interval <= std::chrono::milliseconds{ 10000 }) {
515
+ ++management_.le_10s;
516
+ }
517
+ if (interval <= std::chrono::milliseconds{ 30000 }) {
518
+ ++management_.le_30s;
519
+ }
520
+ if (interval <= std::chrono::milliseconds{ 75000 }) {
521
+ ++management_.le_75s;
522
+ }
523
+ ++management_.inf;
524
+ break;
525
+
526
+ case app_telemetry_latency::eventing:
527
+ ++eventing_.count;
528
+ eventing_.sum += static_cast<std::uint64_t>(interval.count());
529
+ if (interval <= std::chrono::milliseconds{ 100 }) {
530
+ ++eventing_.le_100ms;
531
+ }
532
+ if (interval <= std::chrono::milliseconds{ 1000 }) {
533
+ ++eventing_.le_1s;
534
+ }
535
+ if (interval <= std::chrono::milliseconds{ 10000 }) {
536
+ ++eventing_.le_10s;
537
+ }
538
+ if (interval <= std::chrono::milliseconds{ 30000 }) {
539
+ ++eventing_.le_30s;
540
+ }
541
+ if (interval <= std::chrono::milliseconds{ 75000 }) {
542
+ ++eventing_.le_75s;
543
+ }
544
+ ++eventing_.inf;
545
+
546
+ break;
547
+ }
548
+ // NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index)
549
+ }
550
+
551
+ void update_counter(app_telemetry_counter name) override
552
+ {
553
+ switch (name) {
554
+ case app_telemetry_counter::unknown:
555
+ case app_telemetry_counter::number_of_elements:
556
+ return;
557
+ default:
558
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
559
+ ++counters_[static_cast<std::size_t>(name)];
560
+ break;
561
+ }
562
+ }
563
+
564
+ private:
565
+ friend class default_app_telemetry_meter_impl;
566
+
567
+ std::string node_uuid_;
568
+ std::string bucket_name_;
569
+ std::array<std::atomic_uint64_t, max_number_of_counters> counters_{};
570
+
571
+ kv_non_durable_histogram kv_retrieval_{ "sdk_kv_retrieval_duration_milliseconds" };
572
+ kv_non_durable_histogram kv_mutation_nondurable_{
573
+ "sdk_kv_mutation_nondurable_duration_milliseconds"
574
+ };
575
+ kv_durable_histogram kv_mutation_durable_{ "sdk_kv_mutation_durable_duration_milliseconds" };
576
+ http_histogram query_{ "sdk_query_duration_milliseconds" };
577
+ http_histogram search_{ "sdk_search_duration_milliseconds" };
578
+ http_histogram analytics_{ "sdk_analytics_duration_milliseconds" };
579
+ http_histogram management_{ "sdk_management_duration_milliseconds" };
580
+ http_histogram eventing_{ "sdk_eventing_duration_milliseconds" };
581
+ };
582
+
583
+ class default_app_telemetry_meter_impl : public app_telemetry_meter_impl
584
+ {
585
+ public:
586
+ auto value_recorder(const std::string& node_uuid, const std::string& bucket_name)
587
+ -> std::shared_ptr<app_telemetry_value_recorder> override
588
+ {
589
+ const std::lock_guard<std::mutex> lock(mutex_);
590
+
591
+ if (auto node = recorders_.find(node_uuid); node != recorders_.end()) {
592
+ if (auto bucket = node->second.find(bucket_name); bucket != node->second.end()) {
593
+ return bucket->second;
594
+ }
595
+ }
596
+ auto recorder = std::make_shared<default_app_telemetry_value_recorder>(node_uuid, bucket_name);
597
+ recorders_[node_uuid][bucket_name] = recorder;
598
+ return recorder;
599
+ }
600
+
601
+ void update_config(const topology::configuration& config) override
602
+ {
603
+ for (const auto& node : config.nodes) {
604
+ std::optional<std::string> alt_node{};
605
+ if (auto it = node.alt.find("external"); it != node.alt.end()) {
606
+ if (!it->second.hostname.empty()) {
607
+ alt_node = it->second.hostname;
608
+ }
609
+ }
610
+ labels_cache_[node.node_uuid] = node_labels{
611
+ node.hostname,
612
+ alt_node,
613
+ };
614
+ }
615
+ }
616
+
617
+ auto enabled() -> bool override
618
+ {
619
+ return true;
620
+ }
621
+
622
+ auto nothing_to_report() -> bool override
623
+ {
624
+ const std::lock_guard<std::mutex> lock(mutex_);
625
+ return recorders_.empty();
626
+ }
627
+
628
+ void generate_to(std::vector<std::byte>& buffer, const std::string& agent) override
629
+ {
630
+ auto now = std::chrono::duration_cast<std::chrono::milliseconds>(
631
+ std::chrono::system_clock::now().time_since_epoch())
632
+ .count();
633
+ detail::byte_appender output{ buffer };
634
+ for (const auto& [node_uuid, buckets] : recorders_) {
635
+ auto labels = labels_cache_[node_uuid];
636
+ for (const auto& [bucket, recorder] : buckets) {
637
+
638
+ for (std::size_t i = 0; i < recorder->counters_.size(); ++i) {
639
+ if (is_valid_app_telemetry_counter(i)) {
640
+ // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
641
+ std::uint64_t value = recorder->counters_[i];
642
+ if (value == 0) {
643
+ continue;
644
+ }
645
+
646
+ fmt::format_to(
647
+ output, "{}{{node_uuid=\"{}\"", app_telemetry_counter_name(i), node_uuid);
648
+ if (!labels.node.empty()) {
649
+ fmt::format_to(output, ",node=\"{}\"", labels.node);
650
+ }
651
+ if (const auto& alt = labels.alt_node; alt && !alt->empty()) {
652
+ fmt::format_to(output, ",alt_node=\"{}\"", alt.value());
653
+ }
654
+ if (!bucket.empty()) {
655
+ fmt::format_to(output, ",bucket=\"{}\"", bucket);
656
+ }
657
+ fmt::format_to(output, ",agent={}}} {} {}\n", agent, value, now);
658
+ }
659
+ }
660
+
661
+ recorder->kv_retrieval_.generate_to(output, node_uuid, labels, bucket, agent);
662
+ recorder->kv_mutation_nondurable_.generate_to(output, node_uuid, labels, bucket, agent);
663
+ recorder->kv_mutation_durable_.generate_to(output, node_uuid, labels, bucket, agent);
664
+ recorder->query_.generate_to(output, node_uuid, labels, bucket, agent);
665
+ recorder->search_.generate_to(output, node_uuid, labels, bucket, agent);
666
+ recorder->analytics_.generate_to(output, node_uuid, labels, bucket, agent);
667
+ recorder->management_.generate_to(output, node_uuid, labels, bucket, agent);
668
+ recorder->eventing_.generate_to(output, node_uuid, labels, bucket, agent);
669
+ }
670
+ }
671
+ }
672
+
673
+ private:
674
+ std::mutex mutex_{};
675
+ // node_uuid -> bucket_name -> recorders
676
+ std::map<std::string,
677
+ std::map<std::string, std::shared_ptr<default_app_telemetry_value_recorder>>>
678
+ recorders_{};
679
+ std::map<std::string, node_labels> labels_cache_{};
680
+ };
681
+
682
+ auto
683
+ generate_agent_string(const std::string& extra = {}) -> std::string
684
+ {
685
+ constexpr auto uuid{ "00000000-0000-0000-0000-000000000000" };
686
+ auto hello = meta::user_agent_for_mcbp(uuid, uuid, extra);
687
+ auto json = utils::json::parse(hello.data(), hello.size());
688
+ return utils::json::generate(json["a"]);
689
+ }
690
+
691
+ } // namespace
692
+
693
+ app_telemetry_meter::app_telemetry_meter()
694
+ : impl_{ std::make_unique<default_app_telemetry_meter_impl>() }
695
+ {
696
+ agent_ = generate_agent_string();
697
+ }
698
+
699
+ void
700
+ app_telemetry_meter::disable()
701
+ {
702
+ if (!impl_->enabled()) {
703
+ return;
704
+ }
705
+ CB_LOG_DEBUG("Disable app telemetry meter. {}",
706
+ tao::json::to_string(tao::json::value{
707
+ { "nothing_to_report", impl_->nothing_to_report() },
708
+ }));
709
+ impl_ = std::make_unique<null_app_telemetry_meter_impl>();
710
+ }
711
+
712
+ void
713
+ app_telemetry_meter::enable()
714
+ {
715
+ if (impl_->enabled()) {
716
+ return;
717
+ }
718
+ CB_LOG_DEBUG("Enable app telemetry meter.");
719
+ impl_ = std::make_unique<default_app_telemetry_meter_impl>();
720
+ }
721
+
722
+ void
723
+ app_telemetry_meter::update_agent(const std::string& extra)
724
+ {
725
+ agent_ = generate_agent_string(extra);
726
+ }
727
+
728
+ app_telemetry_meter::~app_telemetry_meter() = default;
729
+
730
+ void
731
+ app_telemetry_meter::update_config(const topology::configuration& config)
732
+ {
733
+ return impl_->update_config(config);
734
+ }
735
+
736
+ auto
737
+ app_telemetry_meter::value_recorder(const std::string& node_uuid, const std::string& bucket_name)
738
+ -> std::shared_ptr<app_telemetry_value_recorder>
739
+ {
740
+ return impl_->value_recorder(node_uuid, bucket_name);
741
+ }
742
+
743
+ void
744
+ app_telemetry_meter::generate_report(std::vector<std::byte>& output_buffer)
745
+ {
746
+ if (impl_->nothing_to_report()) {
747
+ return;
748
+ }
749
+ auto old_impl = std::move(impl_);
750
+ impl_ = std::make_unique<default_app_telemetry_meter_impl>();
751
+ old_impl->generate_to(output_buffer, agent_);
752
+ }
753
+ } // namespace couchbase::core