use std::time::{Duration, SystemTime};
use log::{error, warn};
use sieve_cache::SyncSieveCache;
use crate::dns_key::DNSKey;
#[derive(Clone)]
pub struct CachedResponse {
pub data: Vec<u8>,
pub expires_at: SystemTime,
}
impl CachedResponse {
pub fn new(data: Vec<u8>, ttl: Duration) -> Self {
let expires_at = SystemTime::now()
.checked_add(ttl)
.unwrap_or_else(|| SystemTime::now() + Duration::from_secs(u64::MAX));
Self { data, expires_at }
}
pub fn is_expired(&self) -> bool {
SystemTime::now().duration_since(self.expires_at).is_ok()
}
}
pub type SyncDnsCache = SyncSieveCache<DNSKey, CachedResponse>;
pub fn create_dns_cache(capacity: usize) -> SyncDnsCache {
if let Ok(cache) = SyncSieveCache::<DNSKey, CachedResponse>::new(capacity) {
return cache;
}
error!("Failed to create DNS cache with requested capacity of {capacity} entries");
let smaller_capacity = std::cmp::max(100, capacity / 2);
warn!("Falling back to smaller DNS cache capacity: {smaller_capacity} entries");
if let Ok(cache) = SyncSieveCache::<DNSKey, CachedResponse>::new(smaller_capacity) {
return cache;
}
error!("Failed to create DNS cache with reduced capacity of {smaller_capacity} entries");
let minimal_capacity = 10; warn!("Falling back to minimal DNS cache capacity: {minimal_capacity} entries");
match SyncSieveCache::<DNSKey, CachedResponse>::new(minimal_capacity) {
Ok(cache) => cache,
Err(e) => {
error!(
"Critical error: Failed to allocate even minimal DNS cache with {minimal_capacity} entries: {e}"
);
warn!("Running with a non-functional cache - performance will be degraded");
SyncSieveCache::<DNSKey, CachedResponse>::new(1)
.unwrap_or_else(|e2| {
panic!("Fatal error: Cannot allocate even a trivial cache with 1 entry. System is likely out of memory: {e2}")
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, SystemTime};
#[test]
fn test_large_ttl_conversion() {
let data = vec![1, 2, 3];
let ttl = Duration::from_secs(365 * 24 * 60 * 60);
let cached = CachedResponse::new(data, ttl);
assert!(cached.expires_at > SystemTime::now());
}
#[test]
fn test_cache_expiry_check() {
let data = vec![1, 2, 3];
let cached = CachedResponse::new(data, Duration::from_secs(0));
std::thread::sleep(Duration::from_millis(1));
assert!(cached.is_expired());
}
}