pinakes-core: update remaining modules and tests
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I9e0ff5ea33a5cf697473423e88f167ce6a6a6964
This commit is contained in:
parent
c8425a4c34
commit
3d9f8933d2
44 changed files with 1207 additions and 578 deletions
|
|
@ -30,13 +30,18 @@ pub struct CacheStats {
|
|||
}
|
||||
|
||||
impl CacheStats {
|
||||
#[must_use]
|
||||
pub fn hit_rate(&self) -> f64 {
|
||||
let total = self.hits + self.misses;
|
||||
if total == 0 {
|
||||
0.0
|
||||
} else {
|
||||
self.hits as f64 / total as f64
|
||||
}
|
||||
// Compute ratio using integer arithmetic: hits * 10000 / total gives basis
|
||||
// points (0..=10000), then scale back to [0.0, 1.0]. Returns 0.0 if total
|
||||
// is zero.
|
||||
let basis_points = self
|
||||
.hits
|
||||
.saturating_mul(10_000)
|
||||
.checked_div(total)
|
||||
.unwrap_or(0);
|
||||
f64::from(u32::try_from(basis_points).unwrap_or(u32::MAX)) / 10_000.0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +93,7 @@ where
|
|||
V: Clone + Send + Sync + 'static,
|
||||
{
|
||||
/// Create a new cache with the specified TTL and maximum capacity.
|
||||
#[must_use]
|
||||
pub fn new(ttl: Duration, max_capacity: u64) -> Self {
|
||||
let inner = MokaCache::builder()
|
||||
.time_to_live(ttl)
|
||||
|
|
@ -101,6 +107,7 @@ where
|
|||
}
|
||||
|
||||
/// Create a new cache with TTL, max capacity, and time-to-idle.
|
||||
#[must_use]
|
||||
pub fn new_with_idle(
|
||||
ttl: Duration,
|
||||
tti: Duration,
|
||||
|
|
@ -120,16 +127,16 @@ where
|
|||
|
||||
/// Get a value from the cache.
|
||||
pub async fn get(&self, key: &K) -> Option<V> {
|
||||
match self.inner.get(key).await {
|
||||
Some(value) => {
|
||||
self.metrics.record_hit();
|
||||
Some(value)
|
||||
},
|
||||
None => {
|
||||
self.inner.get(key).await.map_or_else(
|
||||
|| {
|
||||
self.metrics.record_miss();
|
||||
None
|
||||
},
|
||||
}
|
||||
|value| {
|
||||
self.metrics.record_hit();
|
||||
Some(value)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Insert a value into the cache.
|
||||
|
|
@ -150,11 +157,13 @@ where
|
|||
}
|
||||
|
||||
/// Get the current number of entries in the cache.
|
||||
#[must_use]
|
||||
pub fn entry_count(&self) -> u64 {
|
||||
self.inner.entry_count()
|
||||
}
|
||||
|
||||
/// Get cache statistics.
|
||||
#[must_use]
|
||||
pub fn stats(&self) -> CacheStats {
|
||||
let (hits, misses) = self.metrics.stats();
|
||||
CacheStats {
|
||||
|
|
@ -168,11 +177,12 @@ where
|
|||
|
||||
/// Specialized cache for search query results.
|
||||
pub struct QueryCache {
|
||||
/// Cache keyed by (query_hash, offset, limit)
|
||||
/// Cache keyed by (`query_hash`, offset, limit)
|
||||
inner: Cache<String, String>,
|
||||
}
|
||||
|
||||
impl QueryCache {
|
||||
#[must_use]
|
||||
pub fn new(ttl: Duration, max_capacity: u64) -> Self {
|
||||
Self {
|
||||
inner: Cache::new(ttl, max_capacity),
|
||||
|
|
@ -224,6 +234,7 @@ impl QueryCache {
|
|||
self.inner.invalidate_all().await;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn stats(&self) -> CacheStats {
|
||||
self.inner.stats()
|
||||
}
|
||||
|
|
@ -236,6 +247,7 @@ pub struct MetadataCache {
|
|||
}
|
||||
|
||||
impl MetadataCache {
|
||||
#[must_use]
|
||||
pub fn new(ttl: Duration, max_capacity: u64) -> Self {
|
||||
Self {
|
||||
inner: Cache::new(ttl, max_capacity),
|
||||
|
|
@ -257,6 +269,7 @@ impl MetadataCache {
|
|||
self.inner.invalidate(&content_hash.to_string()).await;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn stats(&self) -> CacheStats {
|
||||
self.inner.stats()
|
||||
}
|
||||
|
|
@ -268,6 +281,7 @@ pub struct MediaCache {
|
|||
}
|
||||
|
||||
impl MediaCache {
|
||||
#[must_use]
|
||||
pub fn new(ttl: Duration, max_capacity: u64) -> Self {
|
||||
Self {
|
||||
inner: Cache::new(ttl, max_capacity),
|
||||
|
|
@ -290,6 +304,7 @@ impl MediaCache {
|
|||
self.inner.invalidate_all().await;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn stats(&self) -> CacheStats {
|
||||
self.inner.stats()
|
||||
}
|
||||
|
|
@ -348,6 +363,7 @@ pub struct CacheLayer {
|
|||
impl CacheLayer {
|
||||
/// Create a new cache layer with the specified TTL (using defaults for other
|
||||
/// settings).
|
||||
#[must_use]
|
||||
pub fn new(ttl_secs: u64) -> Self {
|
||||
let config = CacheConfig {
|
||||
response_ttl_secs: ttl_secs,
|
||||
|
|
@ -357,6 +373,7 @@ impl CacheLayer {
|
|||
}
|
||||
|
||||
/// Create a new cache layer with full configuration.
|
||||
#[must_use]
|
||||
pub fn with_config(config: CacheConfig) -> Self {
|
||||
Self {
|
||||
responses: Cache::new(
|
||||
|
|
@ -401,6 +418,7 @@ impl CacheLayer {
|
|||
}
|
||||
|
||||
/// Get aggregated statistics for all caches.
|
||||
#[must_use]
|
||||
pub fn stats(&self) -> CacheLayerStats {
|
||||
CacheLayerStats {
|
||||
responses: self.responses.stats(),
|
||||
|
|
@ -411,7 +429,8 @@ impl CacheLayer {
|
|||
}
|
||||
|
||||
/// Get the current configuration.
|
||||
pub fn config(&self) -> &CacheConfig {
|
||||
#[must_use]
|
||||
pub const fn config(&self) -> &CacheConfig {
|
||||
&self.config
|
||||
}
|
||||
}
|
||||
|
|
@ -427,6 +446,7 @@ pub struct CacheLayerStats {
|
|||
|
||||
impl CacheLayerStats {
|
||||
/// Get the overall hit rate across all caches.
|
||||
#[must_use]
|
||||
pub fn overall_hit_rate(&self) -> f64 {
|
||||
let total_hits = self.responses.hits
|
||||
+ self.queries.hits
|
||||
|
|
@ -438,15 +458,16 @@ impl CacheLayerStats {
|
|||
+ self.metadata.misses
|
||||
+ self.media.misses;
|
||||
|
||||
if total_requests == 0 {
|
||||
0.0
|
||||
} else {
|
||||
total_hits as f64 / total_requests as f64
|
||||
}
|
||||
let basis_points = total_hits
|
||||
.saturating_mul(10_000)
|
||||
.checked_div(total_requests)
|
||||
.unwrap_or(0);
|
||||
f64::from(u32::try_from(basis_points).unwrap_or(u32::MAX)) / 10_000.0
|
||||
}
|
||||
|
||||
/// Get the total number of entries across all caches.
|
||||
pub fn total_entries(&self) -> u64 {
|
||||
#[must_use]
|
||||
pub const fn total_entries(&self) -> u64 {
|
||||
self.responses.size
|
||||
+ self.queries.size
|
||||
+ self.metadata.size
|
||||
|
|
@ -460,7 +481,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_cache_basic_operations() {
|
||||
let cache: Cache<String, String> = Cache::new(Duration::from_secs(60), 100);
|
||||
let cache: Cache<String, String> = Cache::new(Duration::from_mins(1), 100);
|
||||
|
||||
// Insert and get
|
||||
cache.insert("key1".to_string(), "value1".to_string()).await;
|
||||
|
|
@ -479,7 +500,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_cache_stats() {
|
||||
let cache: Cache<String, String> = Cache::new(Duration::from_secs(60), 100);
|
||||
let cache: Cache<String, String> = Cache::new(Duration::from_mins(1), 100);
|
||||
|
||||
cache.insert("key1".to_string(), "value1".to_string()).await;
|
||||
let _ = cache.get(&"key1".to_string()).await; // hit
|
||||
|
|
@ -493,7 +514,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn test_query_cache() {
|
||||
let cache = QueryCache::new(Duration::from_secs(60), 100);
|
||||
let cache = QueryCache::new(Duration::from_mins(1), 100);
|
||||
|
||||
cache
|
||||
.insert("test query", 0, 10, Some("name"), "results".to_string())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue