relay_cardinality/
config.rs

1use relay_base_schema::metrics::MetricNamespace;
2use serde::{Deserialize, Serialize};
3
4use crate::SlidingWindow;
5
6/// A cardinality limit.
7#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
8#[serde(rename_all = "camelCase")]
9pub struct CardinalityLimit {
10    /// Unique identifier of the cardinality limit.
11    pub id: String,
12
13    /// Whether this is a passive limit.
14    ///
15    /// Passive limits are tracked separately to normal limits
16    /// and are not enforced, but still evaluated.
17    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
18    pub passive: bool,
19    /// If `true` additional reporting of cardinality is enabled.
20    ///
21    /// The cardinality limiter will keep track of every tracked limit
22    /// and record the current cardinality. The reported data is not per limit
23    /// but per scope. For example if [`Self::scope`] is set to [`CardinalityScope::Name`],
24    /// the current cardinality for each metric name is reported.
25    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
26    pub report: bool,
27
28    /// The sliding window to enforce the cardinality limits in.
29    pub window: SlidingWindow,
30    /// The cardinality limit.
31    pub limit: u32,
32
33    /// Scope which the limit applies to.
34    pub scope: CardinalityScope,
35    /// Metric namespace the limit applies to.
36    ///
37    /// No namespace means this specific limit is enforced across all namespaces.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub namespace: Option<MetricNamespace>,
40}
41
42/// A scope to restrict the [`CardinalityLimit`] to.
43#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
44#[serde(rename_all = "lowercase")]
45pub enum CardinalityScope {
46    /// An organization level cardinality limit.
47    ///
48    /// The limit will be enforced across the entire org.
49    Organization,
50
51    /// A project level cardinality limit.
52    ///
53    /// The limit will be enforced for a specific project.
54    ///
55    /// Hierarchy: `Organization > Project`.
56    Project,
57
58    /// A per metric type cardinality limit.
59    ///
60    /// This scope is very similar to [`Self::Name`], it operates on a per metric
61    /// basis which includes organization and project id.
62    ///
63    /// A metric type cardinality limit is mostly useful for cardinality reports.
64    ///
65    /// Hierarchy: `Organization > Project > Type`.
66    Type,
67
68    /// A per metric name cardinality limit.
69    ///
70    /// The name scope is a sub-scope of project and organization.
71    ///
72    /// Hierarchy: `Organization > Project > Name`.
73    Name,
74
75    /// Any other scope that is not known by this Relay.
76    #[serde(other)]
77    Unknown,
78}
79
80impl CardinalityScope {
81    /// Returns the string representation of this scope.
82    pub fn as_str(self) -> &'static str {
83        match self {
84            CardinalityScope::Organization => "organization",
85            CardinalityScope::Project => "project",
86            CardinalityScope::Type => "type",
87            CardinalityScope::Name => "name",
88            CardinalityScope::Unknown => "unknown",
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_cardinality_limit_json() {
99        let limit = CardinalityLimit {
100            id: "some_id".to_string(),
101            passive: false,
102            report: false,
103            window: SlidingWindow {
104                window_seconds: 3600,
105                granularity_seconds: 200,
106            },
107            limit: 1337,
108            scope: CardinalityScope::Organization,
109            namespace: Some(MetricNamespace::Custom),
110        };
111
112        let j = serde_json::to_string(&limit).unwrap();
113        assert_eq!(serde_json::from_str::<CardinalityLimit>(&j).unwrap(), limit);
114
115        let j = r#"{
116            "id":"some_id",
117            "window":{"windowSeconds":3600,"granularitySeconds":200},
118            "limit":1337,
119            "scope":"organization",
120            "namespace":"custom"
121        }"#;
122        assert_eq!(serde_json::from_str::<CardinalityLimit>(j).unwrap(), limit);
123    }
124}