1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::{collections::HashMap, time::Duration};

use itertools::Itertools;
use metrics::{Counter, Gauge, Histogram};
use reth_metrics::Metrics;
use reth_primitives::StaticFileSegment;
use strum::{EnumIter, IntoEnumIterator};

/// Metrics for the static file provider.
#[derive(Debug)]
pub struct StaticFileProviderMetrics {
    segments: HashMap<StaticFileSegment, StaticFileSegmentMetrics>,
    segment_operations: HashMap<
        (StaticFileSegment, StaticFileProviderOperation),
        StaticFileProviderOperationMetrics,
    >,
}

impl Default for StaticFileProviderMetrics {
    fn default() -> Self {
        Self {
            segments: StaticFileSegment::iter()
                .map(|segment| {
                    (
                        segment,
                        StaticFileSegmentMetrics::new_with_labels(&[("segment", segment.as_str())]),
                    )
                })
                .collect(),
            segment_operations: StaticFileSegment::iter()
                .cartesian_product(StaticFileProviderOperation::iter())
                .map(|(segment, operation)| {
                    (
                        (segment, operation),
                        StaticFileProviderOperationMetrics::new_with_labels(&[
                            ("segment", segment.as_str()),
                            ("operation", operation.as_str()),
                        ]),
                    )
                })
                .collect(),
        }
    }
}

impl StaticFileProviderMetrics {
    pub(crate) fn record_segment(
        &self,
        segment: StaticFileSegment,
        size: u64,
        files: usize,
        entries: usize,
    ) {
        self.segments.get(&segment).expect("segment metrics should exist").size.set(size as f64);
        self.segments.get(&segment).expect("segment metrics should exist").files.set(files as f64);
        self.segments
            .get(&segment)
            .expect("segment metrics should exist")
            .entries
            .set(entries as f64);
    }

    pub(crate) fn record_segment_operation(
        &self,
        segment: StaticFileSegment,
        operation: StaticFileProviderOperation,
        duration: Option<Duration>,
    ) {
        self.segment_operations
            .get(&(segment, operation))
            .expect("segment operation metrics should exist")
            .calls_total
            .increment(1);

        if let Some(duration) = duration {
            self.segment_operations
                .get(&(segment, operation))
                .expect("segment operation metrics should exist")
                .write_duration_seconds
                .record(duration.as_secs_f64());
        }
    }

    pub(crate) fn record_segment_operations(
        &self,
        segment: StaticFileSegment,
        operation: StaticFileProviderOperation,
        count: u64,
        duration: Option<Duration>,
    ) {
        self.segment_operations
            .get(&(segment, operation))
            .expect("segment operation metrics should exist")
            .calls_total
            .increment(count);

        if let Some(duration) = duration {
            self.segment_operations
                .get(&(segment, operation))
                .expect("segment operation metrics should exist")
                .write_duration_seconds
                .record(duration.as_secs_f64() / count as f64);
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
pub(crate) enum StaticFileProviderOperation {
    InitCursor,
    OpenWriter,
    Append,
    Prune,
    IncrementBlock,
    CommitWriter,
}

impl StaticFileProviderOperation {
    const fn as_str(&self) -> &'static str {
        match self {
            Self::InitCursor => "init-cursor",
            Self::OpenWriter => "open-writer",
            Self::Append => "append",
            Self::Prune => "prune",
            Self::IncrementBlock => "increment-block",
            Self::CommitWriter => "commit-writer",
        }
    }
}

/// Metrics for a specific static file segment.
#[derive(Metrics)]
#[metrics(scope = "static_files.segment")]
pub(crate) struct StaticFileSegmentMetrics {
    /// The size of a static file segment
    size: Gauge,
    /// The number of files for a static file segment
    files: Gauge,
    /// The number of entries for a static file segment
    entries: Gauge,
}

#[derive(Metrics)]
#[metrics(scope = "static_files.jar_provider")]
pub(crate) struct StaticFileProviderOperationMetrics {
    /// Total number of static file jar provider operations made.
    calls_total: Counter,
    /// The time it took to execute the static file jar provider operation that writes data.
    write_duration_seconds: Histogram,
}