use platform_core::AppConfigReader;
use redis_connection::{duration_seconds, RedisConfig, BASE_PREFIX};
pub const CACHE_ENABLED_KEY: &str = "redis.cache.enabled";
pub const CACHE_INSTANCES_KEY: &str = "redis.cache.instances";
pub const KEY_PREFIX_KEY: &str = "redis.cache.key.prefix";
pub const DEFAULT_TTL_KEY: &str = "redis.cache.default.ttl";
const DEFAULT_TTL: &str = "1h";
const DEFAULT_TTL_SECONDS: u64 = 3600;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CacheConfig {
redis: RedisConfig,
key_prefix: String,
default_ttl_seconds: u64,
}
impl CacheConfig {
pub fn from_config() -> Self {
let config = AppConfigReader::get_instance();
let ttl_text = config.get_property_or(DEFAULT_TTL_KEY, DEFAULT_TTL);
CacheConfig {
redis: RedisConfig::from_prefix(BASE_PREFIX),
key_prefix: config.get_property(KEY_PREFIX_KEY).unwrap_or_default(),
default_ttl_seconds: duration_seconds(&ttl_text)
.filter(|seconds| *seconds > 0)
.unwrap_or(DEFAULT_TTL_SECONDS),
}
}
pub fn new(
redis: RedisConfig,
key_prefix: impl Into<String>,
default_ttl_seconds: u64,
) -> Self {
CacheConfig {
redis,
key_prefix: key_prefix.into(),
default_ttl_seconds,
}
}
pub fn redis(&self) -> &RedisConfig {
&self.redis
}
pub fn key_prefix(&self) -> &str {
&self.key_prefix
}
pub fn default_ttl_seconds(&self) -> u64 {
self.default_ttl_seconds
}
}
#[cfg(test)]
mod tests {
use super::*;
use platform_core::overrides;
fn serial() -> std::sync::MutexGuard<'static, ()> {
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
}
const KEYS: &[&str] = &[
"redis.cache.key.prefix",
"redis.cache.default.ttl",
"redis.host",
"redis.port",
"soa.redis.host",
];
fn clear() {
for key in KEYS {
overrides::clear(key);
}
}
#[test]
fn defaults_when_unset() {
let _guard = serial();
clear();
let config = CacheConfig::from_config();
assert_eq!("", config.key_prefix());
assert_eq!(3600, config.default_ttl_seconds());
assert_eq!("127.0.0.1", config.redis().host());
assert_eq!(6379, config.redis().port());
}
#[test]
fn reads_cache_tunables_and_the_plain_redis_namespace() {
let _guard = serial();
clear();
overrides::set("redis.cache.key.prefix", "app1:");
overrides::set("redis.cache.default.ttl", "10m");
overrides::set("redis.host", "cache.example.com");
overrides::set("redis.port", "6380");
let config = CacheConfig::from_config();
assert_eq!("app1:", config.key_prefix());
assert_eq!(600, config.default_ttl_seconds());
assert_eq!("cache.example.com", config.redis().host());
assert_eq!(6380, config.redis().port());
overrides::set("redis.cache.default.ttl", "forever");
assert_eq!(3600, CacheConfig::from_config().default_ttl_seconds());
clear();
}
#[test]
fn ignores_the_soa_namespace() {
let _guard = serial();
clear();
overrides::set("soa.redis.host", "rendezvous.example.com");
overrides::set("redis.host", "cache.example.com");
assert_eq!(
"cache.example.com",
CacheConfig::from_config().redis().host()
);
overrides::clear("redis.host");
assert_eq!(
"127.0.0.1",
CacheConfig::from_config().redis().host(),
"without redis.host the cache falls to the default, not to soa.redis.host"
);
clear();
}
}