|
| 1 | +use std::sync::atomic::AtomicUsize; |
| 2 | + |
| 3 | +use serde::Serialize; |
| 4 | + |
| 5 | +#[derive(Default, Debug, Serialize)] |
| 6 | +#[non_exhaustive] |
| 7 | +pub struct Metrics { |
| 8 | + /// Guage of active connections to the database server, this includes both connections that have belong |
| 9 | + /// to the pool, and connections currently owned by the application. |
| 10 | + pub connection_count: AtomicUsize, |
| 11 | + /// Guage of active connections that currently belong to the pool. |
| 12 | + pub connections_in_pool: AtomicUsize, |
| 13 | + /// Guage of GetConn requests that are currently active. |
| 14 | + pub active_wait_requests: AtomicUsize, |
| 15 | + /// Counter of connections that failed to be created. |
| 16 | + pub create_failed: AtomicUsize, |
| 17 | + /// Counter of connections discarded due to pool constraints. |
| 18 | + pub discarded_superfluous_connection: AtomicUsize, |
| 19 | + /// Counter of connections discarded due to being closed upon return to the pool. |
| 20 | + pub discarded_unestablished_connection: AtomicUsize, |
| 21 | + /// Counter of connections that have been returned to the pool dirty that needed to be cleaned |
| 22 | + /// (ie. open transactions, pending queries, etc). |
| 23 | + pub dirty_connection_return: AtomicUsize, |
| 24 | + /// Counter of connections that have been discarded as they were expired by the pool constraints. |
| 25 | + pub discarded_expired_connection: AtomicUsize, |
| 26 | + /// Counter of connections that have been reset. |
| 27 | + pub resetting_connection: AtomicUsize, |
| 28 | + /// Counter of connections that have been discarded as they returned an error during cleanup. |
| 29 | + pub discarded_error_during_cleanup: AtomicUsize, |
| 30 | + /// Counter of connections that have been returned to the pool. |
| 31 | + pub connection_returned_to_pool: AtomicUsize, |
| 32 | + /// Histogram of times connections have spent outside of the pool. |
| 33 | + #[cfg(feature = "hdrhistogram")] |
| 34 | + pub connection_active_duration: MetricsHistogram, |
| 35 | + /// Histogram of times connections have spent inside of the pool. |
| 36 | + #[cfg(feature = "hdrhistogram")] |
| 37 | + pub connection_idle_duration: MetricsHistogram, |
| 38 | + /// Histogram of times connections have spent being checked for health. |
| 39 | + #[cfg(feature = "hdrhistogram")] |
| 40 | + pub check_duration: MetricsHistogram, |
| 41 | + /// Histogram of time spent waiting to connect to the server. |
| 42 | + #[cfg(feature = "hdrhistogram")] |
| 43 | + pub connect_duration: MetricsHistogram, |
| 44 | +} |
| 45 | + |
| 46 | +impl Metrics { |
| 47 | + /// Resets all histograms to allow for histograms to be bound to a period of time (ie. between metric scrapes) |
| 48 | + pub fn clear_histograms(&self) { |
| 49 | + self.connection_active_duration.reset(); |
| 50 | + self.connection_idle_duration.reset(); |
| 51 | + self.check_duration.reset(); |
| 52 | + self.connect_duration.reset(); |
| 53 | + } |
| 54 | +} |
| 55 | + |
| 56 | +#[cfg(feature = "hdrhistogram")] |
| 57 | +#[derive(Debug)] |
| 58 | +pub struct MetricsHistogram(std::sync::Mutex<hdrhistogram::Histogram<u64>>); |
| 59 | + |
| 60 | +impl MetricsHistogram { |
| 61 | + pub fn reset(&self) { |
| 62 | + self.lock().unwrap().reset(); |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +#[cfg(feature = "hdrhistogram")] |
| 67 | +impl Default for MetricsHistogram { |
| 68 | + fn default() -> Self { |
| 69 | + let hdr = hdrhistogram::Histogram::new_with_bounds(1, 30 * 1_000_000, 2).unwrap(); |
| 70 | + Self(std::sync::Mutex::new(hdr)) |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +#[cfg(feature = "hdrhistogram")] |
| 75 | +impl Serialize for MetricsHistogram { |
| 76 | + fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> |
| 77 | + where |
| 78 | + S: serde::Serializer, |
| 79 | + { |
| 80 | + let hdr = self.0.lock().unwrap(); |
| 81 | + |
| 82 | + /// A percentile of this histogram - for supporting serializers this |
| 83 | + /// will ignore the key (such as `90%ile`) and instead add a |
| 84 | + /// dimension to the metrics (such as `quantile=0.9`). |
| 85 | + macro_rules! ile { |
| 86 | + ($e:expr) => { |
| 87 | + &MetricAlias(concat!("!|quantile=", $e), hdr.value_at_quantile($e)) |
| 88 | + }; |
| 89 | + } |
| 90 | + |
| 91 | + /// A 'qualified' metric name - for supporting serializers such as |
| 92 | + /// serde_prometheus, this will prepend the metric name to this key, |
| 93 | + /// outputting `response_time_count`, for example rather than just |
| 94 | + /// `count`. |
| 95 | + macro_rules! qual { |
| 96 | + ($e:expr) => { |
| 97 | + &MetricAlias("<|", $e) |
| 98 | + }; |
| 99 | + } |
| 100 | + |
| 101 | + use serde::ser::SerializeMap; |
| 102 | + |
| 103 | + let mut tup = serializer.serialize_map(Some(10))?; |
| 104 | + tup.serialize_entry("samples", qual!(hdr.len()))?; |
| 105 | + tup.serialize_entry("min", qual!(hdr.min()))?; |
| 106 | + tup.serialize_entry("max", qual!(hdr.max()))?; |
| 107 | + tup.serialize_entry("mean", qual!(hdr.mean()))?; |
| 108 | + tup.serialize_entry("stdev", qual!(hdr.stdev()))?; |
| 109 | + tup.serialize_entry("90%ile", ile!(0.9))?; |
| 110 | + tup.serialize_entry("95%ile", ile!(0.95))?; |
| 111 | + tup.serialize_entry("99%ile", ile!(0.99))?; |
| 112 | + tup.serialize_entry("99.9%ile", ile!(0.999))?; |
| 113 | + tup.serialize_entry("99.99%ile", ile!(0.9999))?; |
| 114 | + tup.end() |
| 115 | + } |
| 116 | +} |
| 117 | + |
| 118 | +/// This is a mocked 'newtype' (eg. `A(u64)`) that instead allows us to |
| 119 | +/// define our own type name that doesn't have to abide by Rust's constraints |
| 120 | +/// on type names. This allows us to do some manipulation of our metrics, |
| 121 | +/// allowing us to add dimensionality to our metrics via key=value pairs, or |
| 122 | +/// key manipulation on serializers that support it. |
| 123 | +#[cfg(feature = "hdrhistogram")] |
| 124 | +struct MetricAlias<T: Serialize>(&'static str, T); |
| 125 | + |
| 126 | +#[cfg(feature = "hdrhistogram")] |
| 127 | +impl<T: Serialize> Serialize for MetricAlias<T> { |
| 128 | + fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> |
| 129 | + where |
| 130 | + S: serde::Serializer, |
| 131 | + { |
| 132 | + serializer.serialize_newtype_struct(self.0, &self.1) |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +#[cfg(feature = "hdrhistogram")] |
| 137 | +impl std::ops::Deref for MetricsHistogram { |
| 138 | + type Target = std::sync::Mutex<hdrhistogram::Histogram<u64>>; |
| 139 | + |
| 140 | + fn deref(&self) -> &Self::Target { |
| 141 | + &self.0 |
| 142 | + } |
| 143 | +} |
0 commit comments