Skip to main content

dynomite/stats/
codec.rs

1//! Static metric descriptors for pool and server stats.
2//!
3//! The C reference uses `STATS_POOL_CODEC` and `STATS_SERVER_CODEC`
4//! X-macros to emit a struct-of-arrays of metric descriptors. We follow
5//! the same shape with a small `macro_rules!` so each metric gains a
6//! typed handle, an iterable list, and constant metadata.
7
8/// Kind of metric tracked by the stats subsystem.
9///
10/// # Examples
11///
12/// ```
13/// use dynomite::stats::{PoolField, StatsMetricType};
14/// assert_eq!(PoolField::ClientEof.kind(), StatsMetricType::Counter);
15/// ```
16#[derive(Copy, Clone, Eq, PartialEq, Debug)]
17pub enum StatsMetricType {
18    /// Monotonically increasing accumulator.
19    Counter,
20    /// Non-monotonic gauge that may go up or down.
21    Gauge,
22    /// Monotonic timestamp in seconds since epoch.
23    Timestamp,
24}
25
26/// Static descriptor for a metric: how it is interpreted, what its
27/// canonical lower-case name is, and a one-line human description.
28///
29/// # Examples
30///
31/// ```
32/// use dynomite::stats::{POOL_CODEC, MetricSpec};
33/// let first: &MetricSpec = &POOL_CODEC[0];
34/// assert!(!first.name.is_empty());
35/// ```
36#[derive(Copy, Clone, Eq, PartialEq, Debug)]
37pub struct MetricSpec {
38    /// Lower-case identifier as emitted in JSON.
39    pub name: &'static str,
40    /// Whether the metric is a counter, gauge, or timestamp.
41    pub kind: StatsMetricType,
42    /// Free-form description used by the `--describe-stats` CLI flag.
43    pub description: &'static str,
44}
45
46macro_rules! define_codec {
47    (
48        $enum_name:ident, $codec_const:ident,
49        $codec_doc:literal,
50        $variant_doc:literal,
51        { $( $variant:ident, $name:literal, $kind:ident, $desc:literal );* $(;)? }
52    ) => {
53        #[doc = $variant_doc]
54        ///
55        /// Each variant is a typed handle for a metric; its `as usize`
56        /// value is also the index into the metric vector held by
57        /// `PoolStats` / `ServerStats`.
58        #[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
59        #[repr(usize)]
60        pub enum $enum_name {
61            $(
62                #[doc = $desc]
63                $variant
64            ),*
65        }
66
67        impl $enum_name {
68            /// All variants of this metric set, in canonical order.
69            ///
70            /// # Examples
71            ///
72            #[doc = concat!(
73                "```\nuse dynomite::stats::", stringify!($enum_name),
74                ";\nassert!(!", stringify!($enum_name), "::ALL.is_empty());\n```"
75            )]
76            pub const ALL: &'static [$enum_name] = &[ $( Self::$variant ),* ];
77
78            /// Lower-case identifier as it appears in the JSON output.
79            ///
80            /// # Examples
81            ///
82            #[doc = concat!(
83                "```\nuse dynomite::stats::", stringify!($enum_name),
84                ";\nlet name = ", stringify!($enum_name),
85                "::ALL[0].name();\nassert!(!name.is_empty());\n```"
86            )]
87            pub fn name(self) -> &'static str {
88                match self { $( Self::$variant => $name ),* }
89            }
90
91            /// Whether this metric is a counter, gauge, or timestamp.
92            ///
93            /// # Examples
94            ///
95            #[doc = concat!(
96                "```\nuse dynomite::stats::{", stringify!($enum_name),
97                ", StatsMetricType};\nlet k = ", stringify!($enum_name),
98                "::ALL[0].kind();\nassert!(matches!(k, StatsMetricType::Counter | StatsMetricType::Gauge | StatsMetricType::Timestamp));\n```"
99            )]
100            pub fn kind(self) -> StatsMetricType {
101                match self { $( Self::$variant => StatsMetricType::$kind ),* }
102            }
103
104            /// Human-readable description used by `--describe-stats`.
105            ///
106            /// # Examples
107            ///
108            #[doc = concat!(
109                "```\nuse dynomite::stats::", stringify!($enum_name),
110                ";\nlet d = ", stringify!($enum_name),
111                "::ALL[0].description();\nassert!(!d.is_empty());\n```"
112            )]
113            pub fn description(self) -> &'static str {
114                match self { $( Self::$variant => $desc ),* }
115            }
116
117            /// Index of this metric in the corresponding stats vector.
118            ///
119            /// # Examples
120            ///
121            #[doc = concat!(
122                "```\nuse dynomite::stats::", stringify!($enum_name),
123                ";\nassert_eq!(", stringify!($enum_name),
124                "::ALL[0].index(), 0);\n```"
125            )]
126            pub fn index(self) -> usize {
127                self as usize
128            }
129        }
130
131        #[doc = $codec_doc]
132        ///
133        /// # Examples
134        ///
135        #[doc = concat!(
136            "```\nuse dynomite::stats::", stringify!($codec_const),
137            ";\nassert!(!", stringify!($codec_const), ".is_empty());\n```"
138        )]
139        pub const $codec_const: &[MetricSpec] = &[
140            $(
141                MetricSpec {
142                    name: $name,
143                    kind: StatsMetricType::$kind,
144                    description: $desc,
145                }
146            ),*
147        ];
148    };
149}
150
151define_codec!(
152    PoolField, POOL_CODEC,
153    "Const slice of every pool metric descriptor in declaration order.",
154    "Typed handle for a pool metric.",
155    {
156    ClientEof,                "client_eof",                Counter, "# eof on client connections";
157    ClientErr,                "client_err",                Counter, "# errors on client connections";
158    ClientConnections,        "client_connections",        Gauge,   "# active client connections";
159    ClientReadRequests,       "client_read_requests",      Counter, "# client read requests";
160    ClientWriteRequests,      "client_write_requests",     Counter, "# client write responses";
161    ClientDroppedRequests,    "client_dropped_requests",   Counter, "# client dropped requests";
162    ClientNonQuorumWResponses, "client_non_quorum_w_responses", Counter, "# client non quorum write responses";
163    ClientNonQuorumRResponses, "client_non_quorum_r_responses", Counter, "# client non quorum read responses";
164    ServerEjects,             "server_ejects",             Counter, "# times backend server was ejected";
165    DnodeClientEof,           "dnode_client_eof",          Counter, "# eof on dnode client connections";
166    DnodeClientErr,           "dnode_client_err",          Counter, "# errors on dnode client connections";
167    DnodeClientConnections,   "dnode_client_connections",  Gauge,   "# active dnode client connections";
168    DnodeClientInQueue,       "dnode_client_in_queue",     Gauge,   "# dnode client requests in incoming queue";
169    DnodeClientInQueueBytes,  "dnode_client_in_queue_bytes", Gauge, "current dnode client request bytes in incoming queue";
170    DnodeClientOutQueue,      "dnode_client_out_queue",    Gauge,   "# dnode client requests in outgoing queue";
171    DnodeClientOutQueueBytes, "dnode_client_out_queue_bytes", Gauge, "current dnode client request bytes in outgoing queue";
172    PeerDroppedRequests,      "peer_dropped_requests",     Counter, "# local dc peer dropped requests";
173    PeerTimedoutRequests,     "peer_timedout_requests",    Counter, "# local dc peer timedout requests";
174    RemotePeerDroppedRequests,"remote_peer_dropped_requests", Counter, "# remote dc peer dropped requests";
175    RemotePeerTimedoutRequests,"remote_peer_timedout_requests", Counter, "# remote dc peer timedout requests";
176    RemotePeerFailoverRequests,"remote_peer_failover_requests", Counter, "# remote dc peer failover requests";
177    PeerEof,                  "peer_eof",                  Counter, "# eof on peer connections";
178    PeerErr,                  "peer_err",                  Counter, "# errors on peer connections";
179    PeerTimedout,             "peer_timedout",             Counter, "# timeouts on local dc peer connections";
180    RemotePeerTimedout,       "remote_peer_timedout",      Counter, "# timeouts on remote dc peer connections";
181    PeerConnections,          "peer_connections",          Gauge,   "# active peer connections";
182    PeerForwardError,         "peer_forward_error",        Gauge,   "# times we encountered a peer forwarding error";
183    PeerRequests,             "peer_requests",             Counter, "# peer requests";
184    PeerRequestBytes,         "peer_request_bytes",        Counter, "total peer request bytes";
185    PeerResponses,            "peer_responses",            Counter, "# peer respones";
186    PeerResponseBytes,        "peer_response_bytes",       Counter, "total peer response bytes";
187    PeerEjectedAt,            "peer_ejected_at",           Timestamp, "timestamp when peer was ejected";
188    PeerEjects,               "peer_ejects",               Counter, "# times a peer was ejected";
189    PeerInQueue,              "peer_in_queue",             Gauge,   "# local dc peer requests in incoming queue";
190    RemotePeerInQueue,        "remote_peer_in_queue",      Gauge,   "# remote dc peer requests in incoming queue";
191    PeerInQueueBytes,         "peer_in_queue_bytes",       Gauge,   "current peer request bytes in incoming queue";
192    RemotePeerInQueueBytes,   "remote_peer_in_queue_bytes", Gauge,  "current peer request bytes in incoming queue to remote DC";
193    PeerOutQueue,             "peer_out_queue",            Gauge,   "# local dc peer requests in outgoing queue";
194    RemotePeerOutQueue,       "remote_peer_out_queue",     Gauge,   "# remote dc peer requests in outgoing queue";
195    PeerOutQueueBytes,        "peer_out_queue_bytes",      Gauge,   "current peer request bytes in outgoing queue";
196    RemotePeerOutQueueBytes,  "remote_peer_out_queue_bytes", Gauge, "current peer request bytes in outgoing queue to remote DC";
197    PeerMismatchRequests,     "peer_mismatch_requests",    Counter, "current dnode peer mismatched messages";
198    ForwardError,             "forward_error",             Counter, "# times we encountered a forwarding error";
199    Fragments,                "fragments",                 Counter, "# fragments created from a multi-vector request";
200    StatsCount,               "stats_count",               Counter, "# stats request";
201});
202
203define_codec!(
204    ServerField, SERVER_CODEC,
205    "Const slice of every server metric descriptor in declaration order.",
206    "Typed handle for a server metric.",
207    {
208    ServerEof,                "server_eof",                Counter, "# eof on server connections";
209    ServerErr,                "server_err",                Counter, "# errors on server connections";
210    ServerTimedout,           "server_timedout",           Counter, "# timeouts on server connections";
211    ServerEjectedAt,          "server_ejected_at",         Timestamp, "timestamp when server was ejected in usec since epoch";
212    ServerDroppedRequests,    "server_dropped_requests",   Counter, "# server dropped requests";
213    ServerTimedoutRequests,   "server_timedout_requests",  Counter, "# server timedout requests";
214    ReadRequests,             "read_requests",             Counter, "# read requests";
215    ReadRequestBytes,         "read_request_bytes",        Counter, "total read request bytes";
216    WriteRequests,            "write_requests",            Counter, "# write requests";
217    WriteRequestBytes,        "write_request_bytes",       Counter, "total write request bytes";
218    ReadResponses,            "read_responses",            Counter, "# read respones";
219    ReadResponseBytes,        "read_response_bytes",       Counter, "total read response bytes";
220    WriteResponses,           "write_responses",           Counter, "# write respones";
221    WriteResponseBytes,       "write_response_bytes",      Counter, "total write response bytes";
222    InQueue,                  "in_queue",                  Gauge,   "# requests in incoming queue";
223    InQueueBytes,             "in_queue_bytes",            Gauge,   "current request bytes in incoming queue";
224    OutQueue,                 "out_queue",                 Gauge,   "# requests in outgoing queue";
225    OutQueueBytes,            "out_queue_bytes",           Gauge,   "current request bytes in outgoing queue";
226    RedisReqGet,              "redis_req_get",             Counter, "# Redis get";
227    RedisReqSet,              "redis_req_set",             Counter, "# Redis set";
228    RedisReqDel,              "redis_req_del",             Counter, "# Redis del";
229    RedisReqIncrDecr,         "redis_req_incr_decr",       Counter, "# Redis incr or decr";
230    RedisReqKeys,             "redis_req_keys",            Counter, "# Redis keys";
231    RedisReqMget,             "redis_req_mget",            Counter, "# Redis mget";
232    RedisReqScan,             "redis_req_scan",            Counter, "# Redis scan";
233    RedisReqSort,             "redis_req_sort",            Counter, "# Redis sort";
234    RedisReqLreqm,            "redis_req_lreqm",           Counter, "# Redis lreqm";
235    RedisReqSunion,           "redis_req_sunion",          Counter, "# Redis sunion";
236    RedisReqPing,             "redis_req_ping",            Counter, "# Redis ping";
237    RedisReqLists,            "redis_req_lists",           Counter, "# Redis lists";
238    RedisReqSets,             "redis_req_sets",            Counter, "# Redis sets";
239    RedisReqHashes,           "redis_req_hashes",          Counter, "# Redis hashes";
240    RedisReqSortedsets,       "redis_req_sortedsets",      Counter, "# Redis sortedsets";
241    RedisReqOther,            "redis_req_other",           Counter, "# Redis other";
242});
243
244#[cfg(test)]
245mod tests {
246    use super::*;
247
248    #[test]
249    fn pool_codec_indexes_align_with_variants() {
250        for (i, variant) in PoolField::ALL.iter().copied().enumerate() {
251            assert_eq!(variant.index(), i);
252            assert_eq!(variant.name(), POOL_CODEC[i].name);
253            assert_eq!(variant.kind(), POOL_CODEC[i].kind);
254            assert_eq!(variant.description(), POOL_CODEC[i].description);
255        }
256    }
257
258    #[test]
259    fn server_codec_indexes_align_with_variants() {
260        for (i, variant) in ServerField::ALL.iter().copied().enumerate() {
261            assert_eq!(variant.index(), i);
262            assert_eq!(variant.name(), SERVER_CODEC[i].name);
263            assert_eq!(variant.kind(), SERVER_CODEC[i].kind);
264            assert_eq!(variant.description(), SERVER_CODEC[i].description);
265        }
266    }
267
268    #[test]
269    fn pool_kinds_match_c_codec() {
270        assert_eq!(PoolField::ClientConnections.kind(), StatsMetricType::Gauge);
271        assert_eq!(PoolField::ClientEof.kind(), StatsMetricType::Counter);
272        assert_eq!(PoolField::PeerEjectedAt.kind(), StatsMetricType::Timestamp);
273    }
274
275    #[test]
276    fn pool_codec_has_expected_count() {
277        assert_eq!(POOL_CODEC.len(), PoolField::ALL.len());
278    }
279
280    #[test]
281    fn server_codec_has_expected_count() {
282        assert_eq!(SERVER_CODEC.len(), ServerField::ALL.len());
283    }
284}