1use prometheus::{
5 core::{Collector, Desc},
6 proto::{Counter, Gauge, LabelPair, Metric, MetricFamily, MetricType, Summary},
7};
8use sui_pg_db::Db;
9
10pub struct DbConnectionStatsCollector {
12 db: Db,
13 desc: Vec<(MetricType, Desc)>,
14}
15
16impl DbConnectionStatsCollector {
17 pub fn new(prefix: Option<&str>, db: Db) -> Self {
18 let prefix = prefix.unwrap_or("db");
19 let name = |n| format!("{prefix}_{n}");
20
21 let desc = vec![
22 (
23 MetricType::GAUGE,
24 desc(
25 name("connections"),
26 "Number of connections currently being managed by the pool",
27 ),
28 ),
29 (
30 MetricType::GAUGE,
31 desc(
32 name("idle_connections"),
33 "Number of idle connections in the pool",
34 ),
35 ),
36 (
37 MetricType::COUNTER,
38 desc(
39 name("connect_direct"),
40 "Connections that did not have to wait",
41 ),
42 ),
43 (
44 MetricType::SUMMARY,
45 desc(name("connect_waited"), "Connections that had to wait"),
46 ),
47 (
48 MetricType::COUNTER,
49 desc(
50 name("connect_timed_out"),
51 "Connections that timed out waiting for a connection",
52 ),
53 ),
54 (
55 MetricType::COUNTER,
56 desc(
57 name("connections_created"),
58 "Connections that have been created in the pool",
59 ),
60 ),
61 (
62 MetricType::COUNTER,
63 desc_with_labels(
64 name("connections_closed"),
65 "Total connections that were closed",
66 &["reason"],
67 ),
68 ),
69 ];
70
71 Self { db, desc }
72 }
73}
74
75impl Collector for DbConnectionStatsCollector {
76 fn desc(&self) -> Vec<&Desc> {
77 self.desc.iter().map(|d| &d.1).collect()
78 }
79
80 fn collect(&self) -> Vec<MetricFamily> {
81 let state = self.db.state();
82 let stats = state.statistics;
83
84 vec![
85 gauge(&self.desc[0].1, state.connections as f64),
86 gauge(&self.desc[1].1, state.idle_connections as f64),
87 counter(&self.desc[2].1, stats.get_direct as f64),
88 summary(
89 &self.desc[3].1,
90 stats.get_wait_time.as_millis() as f64,
91 stats.get_waited + stats.get_timed_out,
92 ),
93 counter(&self.desc[4].1, stats.get_timed_out as f64),
94 counter(&self.desc[5].1, stats.connections_created as f64),
95 counter_with_labels(
96 &self.desc[6].1,
97 &[
98 ("reason", "broken", stats.connections_closed_broken as f64),
99 ("reason", "invalid", stats.connections_closed_invalid as f64),
100 (
101 "reason",
102 "max_lifetime",
103 stats.connections_closed_max_lifetime as f64,
104 ),
105 (
106 "reason",
107 "idle_timeout",
108 stats.connections_closed_idle_timeout as f64,
109 ),
110 ],
111 ),
112 ]
113 }
114}
115
116fn desc(name: String, help: &str) -> Desc {
117 desc_with_labels(name, help, &[])
118}
119
120fn desc_with_labels(name: String, help: &str, labels: &[&str]) -> Desc {
121 Desc::new(
122 name,
123 help.to_string(),
124 labels.iter().map(|s| s.to_string()).collect(),
125 Default::default(),
126 )
127 .expect("Bad metric description")
128}
129
130fn gauge(desc: &Desc, value: f64) -> MetricFamily {
131 let mut g = Gauge::default();
132 let mut m = Metric::default();
133 let mut mf = MetricFamily::new();
134
135 g.set_value(value);
136 m.set_gauge(g);
137
138 mf.mut_metric().push(m);
139 mf.set_name(desc.fq_name.clone());
140 mf.set_help(desc.help.clone());
141 mf.set_field_type(MetricType::GAUGE);
142 mf
143}
144
145fn counter(desc: &Desc, value: f64) -> MetricFamily {
146 let mut c = Counter::default();
147 let mut m = Metric::default();
148 let mut mf = MetricFamily::new();
149
150 c.set_value(value);
151 m.set_counter(c);
152
153 mf.mut_metric().push(m);
154 mf.set_name(desc.fq_name.clone());
155 mf.set_help(desc.help.clone());
156 mf.set_field_type(MetricType::COUNTER);
157 mf
158}
159
160fn counter_with_labels(desc: &Desc, values: &[(&str, &str, f64)]) -> MetricFamily {
161 let mut mf = MetricFamily::new();
162
163 for (name, label, value) in values {
164 let mut c = Counter::default();
165 let mut l = LabelPair::default();
166 let mut m = Metric::default();
167
168 c.set_value(*value);
169 l.set_name(name.to_string());
170 l.set_value(label.to_string());
171
172 m.set_counter(c);
173 m.mut_label().push(l);
174 mf.mut_metric().push(m);
175 }
176
177 mf.set_name(desc.fq_name.clone());
178 mf.set_help(desc.help.clone());
179 mf.set_field_type(MetricType::COUNTER);
180 mf
181}
182
183fn summary(desc: &Desc, sum: f64, count: u64) -> MetricFamily {
184 let mut s = Summary::default();
185 let mut m = Metric::default();
186 let mut mf = MetricFamily::new();
187
188 s.set_sample_sum(sum);
189 s.set_sample_count(count);
190 m.set_summary(s);
191
192 mf.mut_metric().push(m);
193 mf.set_name(desc.fq_name.clone());
194 mf.set_help(desc.help.clone());
195 mf.set_field_type(MetricType::SUMMARY);
196 mf
197}