fix(cache): wire trusted-proxy cache and remove stale cache traces (#2581)

This commit is contained in:
houseme
2026-04-18 00:57:21 +08:00
committed by GitHub
parent 38eb0781cf
commit ffcf18f5f3
13 changed files with 231 additions and 51 deletions
+67 -15
View File
@@ -14,62 +14,114 @@
//! High-performance cache implementation for proxy validation results using Moka.
use moka::future::Cache;
use moka::sync::Cache;
use std::net::IpAddr;
use std::time::Duration;
use crate::ProxyMetrics;
/// Cache for storing IP validation results.
#[derive(Debug, Clone)]
pub struct IpValidationCache {
/// The underlying Moka cache.
cache: Cache<IpAddr, bool>,
/// Configured capacity.
capacity: usize,
/// Whether the cache is enabled.
enabled: bool,
/// Optional metrics collector for cache activity.
metrics: Option<ProxyMetrics>,
}
impl IpValidationCache {
/// Creates a new `IpValidationCache` using Moka.
pub fn new(capacity: usize, ttl: Duration, enabled: bool) -> Self {
pub fn new(capacity: usize, ttl: Duration, enabled: bool, metrics: Option<ProxyMetrics>) -> Self {
let cache = Cache::builder().max_capacity(capacity as u64).time_to_live(ttl).build();
Self { cache, enabled }
let this = Self {
cache,
capacity,
enabled,
metrics,
};
this.update_cache_size_metric();
this
}
/// Checks if an IP is trusted, using the cache if available.
pub async fn is_trusted(&self, ip: &IpAddr, validator: impl FnOnce(&IpAddr) -> bool) -> bool {
pub fn is_trusted(&self, ip: &IpAddr, validator: impl FnOnce(&IpAddr) -> bool) -> bool {
if !self.enabled {
return validator(ip);
}
// Attempt to get the result from cache.
if let Some(is_trusted) = self.cache.get(ip).await {
metrics::counter!("rustfs_trusted_proxy_cache_hits").increment(1);
if let Some(is_trusted) = self.cache.get(ip) {
self.record_cache_hit();
return is_trusted;
}
// Cache miss: perform validation and update cache.
metrics::counter!("rustfs_trusted_proxy_cache_misses").increment(1);
// Cache miss: perform validation. Only positive trust decisions are cached
// to avoid polluting the cache with one-off untrusted client IPs.
self.record_cache_miss();
let is_trusted = validator(ip);
self.cache.insert(*ip, is_trusted).await;
if is_trusted {
self.cache.insert(*ip, is_trusted);
self.update_cache_size_metric();
}
is_trusted
}
/// Clears all entries from the cache.
pub async fn clear(&self) {
pub fn clear(&self) {
self.cache.invalidate_all();
metrics::gauge!("rustfs_trusted_proxy_cache_size").set(0.0);
self.cache.run_pending_tasks();
self.update_cache_size_metric();
}
/// Runs pending cache maintenance tasks and refreshes size metrics.
pub fn run_maintenance(&self) {
if !self.enabled {
return;
}
self.cache.run_pending_tasks();
self.update_cache_size_metric();
}
/// Returns statistics about the current state of the cache.
pub fn stats(&self) -> CacheStats {
if self.enabled {
self.cache.run_pending_tasks();
}
let entry_count = self.cache.entry_count();
CacheStats {
size: entry_count as usize,
// Moka doesn't expose max_capacity directly in a simple way after build,
// but we can track it if needed.
capacity: 0,
capacity: self.capacity,
}
}
/// Returns whether the cache is enabled.
pub fn is_enabled(&self) -> bool {
self.enabled
}
fn record_cache_hit(&self) {
if let Some(metrics) = &self.metrics {
metrics.record_cache_hit();
}
}
fn record_cache_miss(&self) {
if let Some(metrics) = &self.metrics {
metrics.record_cache_miss();
}
}
fn update_cache_size_metric(&self) {
if let Some(metrics) = &self.metrics {
metrics.set_cache_size(self.cache.entry_count() as usize);
}
}
}