Skip to main content

dynomite/stats/
codec.rs

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