statsig_rust/
global_configs.rs

1use crate::{log_e, DynamicValue};
2use lazy_static::lazy_static;
3use parking_lot::RwLock;
4use std::{
5    collections::HashMap,
6    sync::{Arc, Weak},
7    time::Duration,
8};
9
10const TAG: &str = stringify!(GlobalConfigs);
11
12pub const MAX_SAMPLING_RATE: f64 = 10000.0;
13
14lazy_static! {
15    static ref GLOBAL_CONFIG_INSTANCES: RwLock<HashMap<String, Weak<GlobalConfigs>>> =
16        RwLock::new(HashMap::new());
17}
18
19struct Configs {
20    sdk_configs: HashMap<String, DynamicValue>,
21    diagnostics_sampling_rates: HashMap<String, f64>,
22}
23
24pub struct GlobalConfigs {
25    configs: RwLock<Configs>,
26}
27
28impl GlobalConfigs {
29    pub fn get_instance(sdk_key: &str) -> Arc<GlobalConfigs> {
30        match GLOBAL_CONFIG_INSTANCES.try_read_for(Duration::from_secs(5)) {
31            Some(read_guard) => {
32                if let Some(instance) = read_guard.get(sdk_key) {
33                    if let Some(instance) = instance.upgrade() {
34                        return instance.clone();
35                    }
36                }
37            }
38            None => {
39                log_e!(
40                    TAG,
41                    "Failed to get read guard: Failed to lock GLOBAL_CONFIG_INSTANCES"
42                );
43            }
44        }
45
46        let instance = Arc::new(GlobalConfigs {
47            configs: RwLock::new(Configs {
48                sdk_configs: HashMap::new(),
49                diagnostics_sampling_rates: HashMap::from([
50                    ("initialize".to_string(), 10000.0),
51                    ("config_sync".to_string(), 1000.0),
52                    ("dcs".to_string(), 1000.0),
53                    ("get_id_list".to_string(), 100.0), // default sampling rates
54                ]),
55            }),
56        });
57
58        match GLOBAL_CONFIG_INSTANCES.try_write_for(Duration::from_secs(5)) {
59            Some(mut write_guard) => {
60                write_guard.insert(sdk_key.into(), Arc::downgrade(&instance));
61            }
62            None => {
63                log_e!(
64                    TAG,
65                    "Failed to get write guard: Failed to lock GLOBAL_CONFIG_INSTANCES"
66                );
67            }
68        }
69
70        instance
71    }
72
73    pub fn set_sdk_configs(&self, new_configs: HashMap<String, DynamicValue>) {
74        match self.configs.try_write_for(Duration::from_secs(5)) {
75            Some(mut configs_guard) => {
76                for (key, value) in new_configs {
77                    configs_guard.sdk_configs.insert(key, value);
78                }
79            }
80            None => {
81                log_e!(TAG, "Failed to get write guard: Failed to lock configs");
82            }
83        }
84    }
85
86    pub fn set_diagnostics_sampling_rates(&self, new_sampling_rate: HashMap<String, f64>) {
87        match self.configs.try_write_for(Duration::from_secs(5)) {
88            Some(mut configs_guard) => {
89                for (key, rate) in new_sampling_rate {
90                    let clamped_rate = rate.clamp(0.0, MAX_SAMPLING_RATE);
91                    configs_guard
92                        .diagnostics_sampling_rates
93                        .insert(key, clamped_rate);
94                }
95            }
96            None => {
97                log_e!(TAG, "Failed to get write guard: Failed to lock configs");
98            }
99        }
100    }
101
102    pub fn use_sdk_config_value<T>(
103        &self,
104        key: &str,
105        f: impl FnOnce(Option<&DynamicValue>) -> T,
106    ) -> T {
107        match self.configs.try_read_for(Duration::from_secs(5)) {
108            Some(configs_guard) => f(configs_guard.sdk_configs.get(key)),
109            None => {
110                log_e!(TAG, "Failed to get read guard: Failed to lock configs");
111                f(None)
112            }
113        }
114    }
115
116    pub fn use_diagnostics_sampling_rate<T>(
117        &self,
118        key: &str,
119        f: impl FnOnce(Option<&f64>) -> T,
120    ) -> T {
121        match self.configs.try_read_for(Duration::from_secs(5)) {
122            Some(configs_guard) => f(configs_guard.diagnostics_sampling_rates.get(key)),
123            None => {
124                log_e!(TAG, "Failed to get read guard: Failed to lock configs");
125                f(None)
126            }
127        }
128    }
129}