Line data Source code
1 : //!
2 : //! This module provides metric definitions for the storage controller.
3 : //!
4 : //! All metrics are grouped in [`StorageControllerMetricGroup`]. [`StorageControllerMetrics`] holds
5 : //! the mentioned metrics and their encoder. It's globally available via the [`METRICS_REGISTRY`]
6 : //! constant.
7 : //!
8 : //! The rest of the code defines label group types and deals with converting outer types to labels.
9 : //!
10 : use bytes::Bytes;
11 : use measured::{label::LabelValue, metric::histogram, FixedCardinalityLabel, MetricGroup};
12 : use metrics::NeonMetrics;
13 : use once_cell::sync::Lazy;
14 : use std::sync::Mutex;
15 : use strum::IntoEnumIterator;
16 :
17 : use crate::{
18 : persistence::{DatabaseError, DatabaseOperation},
19 : service::LeadershipStatus,
20 : };
21 :
22 : pub(crate) static METRICS_REGISTRY: Lazy<StorageControllerMetrics> =
23 : Lazy::new(StorageControllerMetrics::default);
24 :
25 0 : pub fn preinitialize_metrics() {
26 0 : Lazy::force(&METRICS_REGISTRY);
27 0 : }
28 :
29 : pub(crate) struct StorageControllerMetrics {
30 : pub(crate) metrics_group: StorageControllerMetricGroup,
31 : encoder: Mutex<measured::text::BufferedTextEncoder>,
32 : }
33 :
34 8 : #[derive(measured::MetricGroup)]
35 : #[metric(new())]
36 : pub(crate) struct StorageControllerMetricGroup {
37 : /// Count of how many times we spawn a reconcile task
38 : pub(crate) storage_controller_reconcile_spawn: measured::Counter,
39 :
40 : /// Size of the in-memory map of tenant shards
41 : pub(crate) storage_controller_tenant_shards: measured::Gauge,
42 :
43 : /// Size of the in-memory map of pageserver_nodes
44 : pub(crate) storage_controller_pageserver_nodes: measured::Gauge,
45 :
46 : /// Reconciler tasks completed, broken down by success/failure/cancelled
47 : pub(crate) storage_controller_reconcile_complete:
48 : measured::CounterVec<ReconcileCompleteLabelGroupSet>,
49 :
50 : /// Count of how many times we make an optimization change to a tenant's scheduling
51 : pub(crate) storage_controller_schedule_optimization: measured::Counter,
52 :
53 : /// HTTP request status counters for handled requests
54 : pub(crate) storage_controller_http_request_status:
55 : measured::CounterVec<HttpRequestStatusLabelGroupSet>,
56 :
57 : /// HTTP request handler latency across all status codes
58 : #[metric(metadata = histogram::Thresholds::exponential_buckets(0.1, 2.0))]
59 : pub(crate) storage_controller_http_request_latency:
60 : measured::HistogramVec<HttpRequestLatencyLabelGroupSet, 5>,
61 :
62 : /// Count of HTTP requests to the pageserver that resulted in an error,
63 : /// broken down by the pageserver node id, request name and method
64 : pub(crate) storage_controller_pageserver_request_error:
65 : measured::CounterVec<PageserverRequestLabelGroupSet>,
66 :
67 : /// Latency of HTTP requests to the pageserver, broken down by pageserver
68 : /// node id, request name and method. This include both successful and unsuccessful
69 : /// requests.
70 : #[metric(metadata = histogram::Thresholds::exponential_buckets(0.1, 2.0))]
71 : pub(crate) storage_controller_pageserver_request_latency:
72 : measured::HistogramVec<PageserverRequestLabelGroupSet, 5>,
73 :
74 : /// Count of pass-through HTTP requests to the pageserver that resulted in an error,
75 : /// broken down by the pageserver node id, request name and method
76 : pub(crate) storage_controller_passthrough_request_error:
77 : measured::CounterVec<PageserverRequestLabelGroupSet>,
78 :
79 : /// Latency of pass-through HTTP requests to the pageserver, broken down by pageserver
80 : /// node id, request name and method. This include both successful and unsuccessful
81 : /// requests.
82 : #[metric(metadata = histogram::Thresholds::exponential_buckets(0.1, 2.0))]
83 : pub(crate) storage_controller_passthrough_request_latency:
84 : measured::HistogramVec<PageserverRequestLabelGroupSet, 5>,
85 :
86 : /// Count of errors in database queries, broken down by error type and operation.
87 : pub(crate) storage_controller_database_query_error:
88 : measured::CounterVec<DatabaseQueryErrorLabelGroupSet>,
89 :
90 : /// Latency of database queries, broken down by operation.
91 : #[metric(metadata = histogram::Thresholds::exponential_buckets(0.1, 2.0))]
92 : pub(crate) storage_controller_database_query_latency:
93 : measured::HistogramVec<DatabaseQueryLatencyLabelGroupSet, 5>,
94 :
95 : pub(crate) storage_controller_leadership_status: measured::GaugeVec<LeadershipStatusGroupSet>,
96 :
97 : /// HTTP request status counters for handled requests
98 : pub(crate) storage_controller_reconcile_long_running:
99 : measured::CounterVec<ReconcileLongRunningLabelGroupSet>,
100 : }
101 :
102 : impl StorageControllerMetrics {
103 0 : pub(crate) fn encode(&self, neon_metrics: &NeonMetrics) -> Bytes {
104 0 : let mut encoder = self.encoder.lock().unwrap();
105 0 : neon_metrics
106 0 : .collect_group_into(&mut *encoder)
107 0 : .unwrap_or_else(|infallible| match infallible {});
108 0 : self.metrics_group
109 0 : .collect_group_into(&mut *encoder)
110 0 : .unwrap_or_else(|infallible| match infallible {});
111 0 : encoder.finish()
112 0 : }
113 : }
114 :
115 : impl Default for StorageControllerMetrics {
116 8 : fn default() -> Self {
117 8 : let mut metrics_group = StorageControllerMetricGroup::new();
118 8 : metrics_group
119 8 : .storage_controller_reconcile_complete
120 8 : .init_all_dense();
121 8 :
122 8 : Self {
123 8 : metrics_group,
124 8 : encoder: Mutex::new(measured::text::BufferedTextEncoder::new()),
125 8 : }
126 8 : }
127 : }
128 :
129 24 : #[derive(measured::LabelGroup)]
130 : #[label(set = ReconcileCompleteLabelGroupSet)]
131 : pub(crate) struct ReconcileCompleteLabelGroup {
132 : pub(crate) status: ReconcileOutcome,
133 : }
134 :
135 16 : #[derive(measured::LabelGroup)]
136 : #[label(set = HttpRequestStatusLabelGroupSet)]
137 : pub(crate) struct HttpRequestStatusLabelGroup<'a> {
138 : #[label(dynamic_with = lasso::ThreadedRodeo, default)]
139 : pub(crate) path: &'a str,
140 : pub(crate) method: Method,
141 : pub(crate) status: StatusCode,
142 : }
143 :
144 16 : #[derive(measured::LabelGroup)]
145 : #[label(set = HttpRequestLatencyLabelGroupSet)]
146 : pub(crate) struct HttpRequestLatencyLabelGroup<'a> {
147 : #[label(dynamic_with = lasso::ThreadedRodeo, default)]
148 : pub(crate) path: &'a str,
149 : pub(crate) method: Method,
150 : }
151 :
152 64 : #[derive(measured::LabelGroup, Clone)]
153 : #[label(set = PageserverRequestLabelGroupSet)]
154 : pub(crate) struct PageserverRequestLabelGroup<'a> {
155 : #[label(dynamic_with = lasso::ThreadedRodeo, default)]
156 : pub(crate) pageserver_id: &'a str,
157 : #[label(dynamic_with = lasso::ThreadedRodeo, default)]
158 : pub(crate) path: &'a str,
159 : pub(crate) method: Method,
160 : }
161 :
162 32 : #[derive(measured::LabelGroup)]
163 : #[label(set = DatabaseQueryErrorLabelGroupSet)]
164 : pub(crate) struct DatabaseQueryErrorLabelGroup {
165 : pub(crate) error_type: DatabaseErrorLabel,
166 : pub(crate) operation: DatabaseOperation,
167 : }
168 :
169 24 : #[derive(measured::LabelGroup)]
170 : #[label(set = DatabaseQueryLatencyLabelGroupSet)]
171 : pub(crate) struct DatabaseQueryLatencyLabelGroup {
172 : pub(crate) operation: DatabaseOperation,
173 : }
174 :
175 24 : #[derive(measured::LabelGroup)]
176 : #[label(set = LeadershipStatusGroupSet)]
177 : pub(crate) struct LeadershipStatusGroup {
178 : pub(crate) status: LeadershipStatus,
179 : }
180 :
181 16 : #[derive(measured::LabelGroup, Clone)]
182 : #[label(set = ReconcileLongRunningLabelGroupSet)]
183 : pub(crate) struct ReconcileLongRunningLabelGroup<'a> {
184 : #[label(dynamic_with = lasso::ThreadedRodeo, default)]
185 : pub(crate) tenant_id: &'a str,
186 : #[label(dynamic_with = lasso::ThreadedRodeo, default)]
187 : pub(crate) shard_number: &'a str,
188 : #[label(dynamic_with = lasso::ThreadedRodeo, default)]
189 : pub(crate) sequence: &'a str,
190 : }
191 :
192 : #[derive(FixedCardinalityLabel, Clone, Copy)]
193 : pub(crate) enum ReconcileOutcome {
194 : #[label(rename = "ok")]
195 : Success,
196 : Error,
197 : Cancel,
198 : }
199 :
200 : #[derive(FixedCardinalityLabel, Copy, Clone)]
201 : pub(crate) enum Method {
202 : Get,
203 : Put,
204 : Post,
205 : Delete,
206 : Other,
207 : }
208 :
209 : impl From<hyper::Method> for Method {
210 0 : fn from(value: hyper::Method) -> Self {
211 0 : if value == hyper::Method::GET {
212 0 : Method::Get
213 0 : } else if value == hyper::Method::PUT {
214 0 : Method::Put
215 0 : } else if value == hyper::Method::POST {
216 0 : Method::Post
217 0 : } else if value == hyper::Method::DELETE {
218 0 : Method::Delete
219 : } else {
220 0 : Method::Other
221 : }
222 0 : }
223 : }
224 :
225 : #[derive(Clone, Copy)]
226 : pub(crate) struct StatusCode(pub(crate) hyper::http::StatusCode);
227 :
228 : impl LabelValue for StatusCode {
229 0 : fn visit<V: measured::label::LabelVisitor>(&self, v: V) -> V::Output {
230 0 : v.write_int(self.0.as_u16() as i64)
231 0 : }
232 : }
233 :
234 : impl FixedCardinalityLabel for StatusCode {
235 0 : fn cardinality() -> usize {
236 0 : (100..1000).len()
237 0 : }
238 :
239 0 : fn encode(&self) -> usize {
240 0 : self.0.as_u16() as usize
241 0 : }
242 :
243 0 : fn decode(value: usize) -> Self {
244 0 : Self(hyper::http::StatusCode::from_u16(u16::try_from(value).unwrap()).unwrap())
245 0 : }
246 : }
247 :
248 : #[derive(FixedCardinalityLabel, Clone, Copy)]
249 : pub(crate) enum DatabaseErrorLabel {
250 : Query,
251 : Connection,
252 : ConnectionPool,
253 : Logical,
254 : Migration,
255 : }
256 :
257 : impl DatabaseError {
258 0 : pub(crate) fn error_label(&self) -> DatabaseErrorLabel {
259 0 : match self {
260 0 : Self::Query(_) => DatabaseErrorLabel::Query,
261 0 : Self::Connection(_) => DatabaseErrorLabel::Connection,
262 0 : Self::ConnectionPool(_) => DatabaseErrorLabel::ConnectionPool,
263 0 : Self::Logical(_) => DatabaseErrorLabel::Logical,
264 0 : Self::Migration(_) => DatabaseErrorLabel::Migration,
265 : }
266 0 : }
267 : }
268 :
269 : /// Update the leadership status metric gauges to reflect the requested status
270 0 : pub(crate) fn update_leadership_status(status: LeadershipStatus) {
271 0 : let status_metric = &METRICS_REGISTRY
272 0 : .metrics_group
273 0 : .storage_controller_leadership_status;
274 :
275 0 : for s in LeadershipStatus::iter() {
276 0 : if s == status {
277 0 : status_metric.set(LeadershipStatusGroup { status: s }, 1);
278 0 : } else {
279 0 : status_metric.set(LeadershipStatusGroup { status: s }, 0);
280 0 : }
281 : }
282 0 : }
|