distributed_cache/
config.rs1use platform_core::AppConfigReader;
32use redis_connection::{duration_seconds, RedisConfig, BASE_PREFIX};
33
34pub const CACHE_ENABLED_KEY: &str = "redis.cache.enabled";
36pub const CACHE_INSTANCES_KEY: &str = "redis.cache.instances";
38pub const KEY_PREFIX_KEY: &str = "redis.cache.key.prefix";
40pub const DEFAULT_TTL_KEY: &str = "redis.cache.default.ttl";
42
43const DEFAULT_TTL: &str = "1h";
44const DEFAULT_TTL_SECONDS: u64 = 3600;
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct CacheConfig {
49 redis: RedisConfig,
50 key_prefix: String,
51 default_ttl_seconds: u64,
52}
53
54impl CacheConfig {
55 pub fn from_config() -> Self {
61 let config = AppConfigReader::get_instance();
62 let ttl_text = config.get_property_or(DEFAULT_TTL_KEY, DEFAULT_TTL);
63 CacheConfig {
64 redis: RedisConfig::from_prefix(BASE_PREFIX),
65 key_prefix: config.get_property(KEY_PREFIX_KEY).unwrap_or_default(),
66 default_ttl_seconds: duration_seconds(&ttl_text)
67 .filter(|seconds| *seconds > 0)
68 .unwrap_or(DEFAULT_TTL_SECONDS),
69 }
70 }
71
72 pub fn new(
74 redis: RedisConfig,
75 key_prefix: impl Into<String>,
76 default_ttl_seconds: u64,
77 ) -> Self {
78 CacheConfig {
79 redis,
80 key_prefix: key_prefix.into(),
81 default_ttl_seconds,
82 }
83 }
84
85 pub fn redis(&self) -> &RedisConfig {
87 &self.redis
88 }
89
90 pub fn key_prefix(&self) -> &str {
92 &self.key_prefix
93 }
94
95 pub fn default_ttl_seconds(&self) -> u64 {
97 self.default_ttl_seconds
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use platform_core::overrides;
105
106 fn serial() -> std::sync::MutexGuard<'static, ()> {
107 static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
108 LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
109 }
110
111 const KEYS: &[&str] = &[
112 "redis.cache.key.prefix",
113 "redis.cache.default.ttl",
114 "redis.host",
115 "redis.port",
116 "soa.redis.host",
117 ];
118
119 fn clear() {
120 for key in KEYS {
121 overrides::clear(key);
122 }
123 }
124
125 #[test]
127 fn defaults_when_unset() {
128 let _guard = serial();
129 clear();
130 let config = CacheConfig::from_config();
131 assert_eq!("", config.key_prefix());
132 assert_eq!(3600, config.default_ttl_seconds());
133 assert_eq!("127.0.0.1", config.redis().host());
134 assert_eq!(6379, config.redis().port());
135 }
136
137 #[test]
139 fn reads_cache_tunables_and_the_plain_redis_namespace() {
140 let _guard = serial();
141 clear();
142 overrides::set("redis.cache.key.prefix", "app1:");
143 overrides::set("redis.cache.default.ttl", "10m");
144 overrides::set("redis.host", "cache.example.com");
145 overrides::set("redis.port", "6380");
146 let config = CacheConfig::from_config();
147 assert_eq!("app1:", config.key_prefix());
148 assert_eq!(600, config.default_ttl_seconds());
149 assert_eq!("cache.example.com", config.redis().host());
150 assert_eq!(6380, config.redis().port());
151 overrides::set("redis.cache.default.ttl", "forever");
153 assert_eq!(3600, CacheConfig::from_config().default_ttl_seconds());
154 clear();
155 }
156
157 #[test]
160 fn ignores_the_soa_namespace() {
161 let _guard = serial();
162 clear();
163 overrides::set("soa.redis.host", "rendezvous.example.com");
164 overrides::set("redis.host", "cache.example.com");
165 assert_eq!(
166 "cache.example.com",
167 CacheConfig::from_config().redis().host()
168 );
169 overrides::clear("redis.host");
170 assert_eq!(
171 "127.0.0.1",
172 CacheConfig::from_config().redis().host(),
173 "without redis.host the cache falls to the default, not to soa.redis.host"
174 );
175 clear();
176 }
177}