Skip to main content

common_frontend/
metrics.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Cache lookup counters shared by frontend, pipeline, and protocol handlers.
16//!
17//! The `type` label identifies `otlp_trace_aux`,
18//! `pipeline_table`, `pipeline`, `pipeline_original`, `pipeline_failover`, or
19//! `mysql_prepared_stmt`. Counters aggregate across catalogs and connections.
20//! Trace lookups count distinct service/operation keys once per request, excluding
21//! rechecks. Pipeline loaders count successful coalesced waiters as hits and
22//! failed loads as misses.
23//! Failover counts one outcome after resolving all candidate schemas; an ambiguous
24//! result is a miss. Table and prepared-statement caches count each lookup.
25//! Both outcomes for all known caches are exported from the first metric update.
26
27use lazy_static::lazy_static;
28use prometheus::{IntCounterVec, register_int_counter_vec};
29
30const CACHE_TYPES: [&str; 6] = [
31    "otlp_trace_aux",
32    "pipeline_table",
33    "pipeline",
34    "pipeline_original",
35    "pipeline_failover",
36    "mysql_prepared_stmt",
37];
38
39lazy_static! {
40    static ref CACHE_COUNTERS: (IntCounterVec, IntCounterVec) = {
41        let hits = register_int_counter_vec!(
42            "greptime_frontend_cache_hit",
43            "frontend cache hit",
44            &["type"]
45        )
46        .unwrap();
47        let misses = register_int_counter_vec!(
48            "greptime_frontend_cache_miss",
49            "frontend cache miss",
50            &["type"]
51        )
52        .unwrap();
53        // Register both outcomes together so an all-hit or all-miss cache has both series.
54        for cache_type in CACHE_TYPES {
55            hits.with_label_values(&[cache_type]);
56            misses.with_label_values(&[cache_type]);
57        }
58        (hits, misses)
59    };
60
61    /// Frontend cache hits by cache type.
62    pub static ref CACHE_HIT: IntCounterVec = CACHE_COUNTERS.0.clone();
63    /// Frontend cache misses by cache type.
64    pub static ref CACHE_MISS: IntCounterVec = CACHE_COUNTERS.1.clone();
65}
66
67/// Records one completed cache lookup.
68pub fn record_cache_lookup(cache_type: &str, hit: bool) {
69    if hit {
70        CACHE_HIT.with_label_values(&[cache_type]).inc();
71    } else {
72        CACHE_MISS.with_label_values(&[cache_type]).inc();
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_cache_lookup_counters() {
82        record_cache_lookup("pipeline", false);
83        // Scrape before accessing CACHE_HIT, which would initialize it and hide a missing series.
84        let families = prometheus::gather();
85        for (name, expected) in [
86            ("greptime_frontend_cache_hit", 0.0),
87            ("greptime_frontend_cache_miss", 1.0),
88        ] {
89            let family = families
90                .iter()
91                .find(|family| family.name() == name)
92                .unwrap();
93            assert_eq!(family.get_metric().len(), CACHE_TYPES.len());
94            for cache_type in CACHE_TYPES {
95                let metric = family
96                    .get_metric()
97                    .iter()
98                    .find(|metric| metric.get_label()[0].value() == cache_type)
99                    .unwrap();
100                assert_eq!(metric.get_label()[0].name(), "type");
101                assert_eq!(
102                    metric.get_counter().value(),
103                    if cache_type == "pipeline" {
104                        expected
105                    } else {
106                        0.0
107                    }
108                );
109            }
110        }
111        record_cache_lookup("pipeline", true);
112        record_cache_lookup("pipeline", false);
113        assert_eq!(CACHE_HIT.with_label_values(&["pipeline"]).get(), 1);
114        assert_eq!(CACHE_MISS.with_label_values(&["pipeline"]).get(), 2);
115    }
116}