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}