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