@@ -27,7 +27,7 @@ use object_store::{
27
27
use observability_deps:: tracing:: { error, info, warn} ;
28
28
use tokio:: sync:: {
29
29
mpsc:: { channel, Receiver , Sender } ,
30
- oneshot,
30
+ oneshot, watch ,
31
31
} ;
32
32
33
33
/// Shared future type for cache values that are being fetched
@@ -58,6 +58,9 @@ impl CacheRequest {
58
58
pub trait ParquetCacheOracle : Send + Sync + Debug {
59
59
/// Register a cache request with the oracle
60
60
fn register ( & self , cache_request : CacheRequest ) ;
61
+
62
+ // Get a receiver that is notified when a prune takes place and how much memory was freed
63
+ fn prune_notifier ( & self ) -> watch:: Receiver < usize > ;
61
64
}
62
65
63
66
/// Concrete implementation of the [`ParquetCacheOracle`]
@@ -66,6 +69,7 @@ pub trait ParquetCacheOracle: Send + Sync + Debug {
66
69
#[ derive( Debug , Clone ) ]
67
70
pub struct MemCacheOracle {
68
71
cache_request_tx : Sender < CacheRequest > ,
72
+ prune_notifier_tx : watch:: Sender < usize > ,
69
73
}
70
74
71
75
// TODO(trevor): make this configurable with reasonable default
@@ -80,8 +84,12 @@ impl MemCacheOracle {
80
84
fn new ( mem_cached_store : Arc < MemCachedObjectStore > , prune_interval : Duration ) -> Self {
81
85
let ( cache_request_tx, cache_request_rx) = channel ( CACHE_REQUEST_BUFFER_SIZE ) ;
82
86
background_cache_request_handler ( Arc :: clone ( & mem_cached_store) , cache_request_rx) ;
83
- background_cache_pruner ( mem_cached_store, prune_interval) ;
84
- Self { cache_request_tx }
87
+ let ( prune_notifier_tx, _prune_notifier_rx) = watch:: channel ( 0 ) ;
88
+ background_cache_pruner ( mem_cached_store, prune_notifier_tx. clone ( ) , prune_interval) ;
89
+ Self {
90
+ cache_request_tx,
91
+ prune_notifier_tx,
92
+ }
85
93
}
86
94
}
87
95
@@ -94,6 +102,10 @@ impl ParquetCacheOracle for MemCacheOracle {
94
102
} ;
95
103
} ) ;
96
104
}
105
+
106
+ fn prune_notifier ( & self ) -> watch:: Receiver < usize > {
107
+ self . prune_notifier_tx . subscribe ( )
108
+ }
97
109
}
98
110
99
111
/// Helper function for creation of a [`MemCachedObjectStore`] and [`MemCacheOracle`]
@@ -324,10 +336,10 @@ impl Cache {
324
336
/// Prune least recently hit entries from the cache
325
337
///
326
338
/// This is a no-op if the `used` amount on the cache is not >= its `capacity`
327
- fn prune ( & self ) {
339
+ fn prune ( & self ) -> Option < usize > {
328
340
let used = self . used . load ( Ordering :: SeqCst ) ;
329
341
if used < self . capacity {
330
- return ;
342
+ return None ;
331
343
}
332
344
let n_to_prune = ( self . map . len ( ) as f64 * self . prune_percent ) . floor ( ) as usize ;
333
345
// use a BinaryHeap to determine the cut-off time, at which, entries that were
@@ -367,6 +379,8 @@ impl Cache {
367
379
}
368
380
// update used mem size with freed amount:
369
381
self . used . fetch_sub ( freed, Ordering :: SeqCst ) ;
382
+
383
+ Some ( freed)
370
384
}
371
385
}
372
386
@@ -663,14 +677,17 @@ fn background_cache_request_handler(
663
677
/// A background task for pruning un-needed entries in the cache
664
678
fn background_cache_pruner (
665
679
mem_store : Arc < MemCachedObjectStore > ,
680
+ prune_notifier_tx : watch:: Sender < usize > ,
666
681
interval_duration : Duration ,
667
682
) -> tokio:: task:: JoinHandle < ( ) > {
668
683
tokio:: spawn ( async move {
669
684
let mut interval = tokio:: time:: interval ( interval_duration) ;
670
685
interval. set_missed_tick_behavior ( tokio:: time:: MissedTickBehavior :: Skip ) ;
671
686
loop {
672
687
interval. tick ( ) . await ;
673
- mem_store. cache . prune ( ) ;
688
+ if let Some ( freed) = mem_store. cache . prune ( ) {
689
+ let _ = prune_notifier_tx. send ( freed) ;
690
+ }
674
691
}
675
692
} )
676
693
}
@@ -764,6 +781,7 @@ pub(crate) mod tests {
764
781
cache_prune_percent,
765
782
cache_prune_interval,
766
783
) ;
784
+ let mut prune_notifier = oracle. prune_notifier ( ) ;
767
785
// PUT an entry into the store:
768
786
let path_1 = Path :: from ( "0.parquet" ) ;
769
787
let payload_1 = b"Janeway" ;
@@ -856,8 +874,8 @@ pub(crate) mod tests {
856
874
assert_eq ! ( 1 , inner_store. total_read_request_count( & path_2) ) ;
857
875
assert_eq ! ( 1 , inner_store. total_read_request_count( & path_3) ) ;
858
876
859
- // allow some time for pruning:
860
- tokio :: time :: sleep ( Duration :: from_millis ( 500 ) ) . await ;
877
+ prune_notifier . changed ( ) . await . unwrap ( ) ;
878
+ assert_eq ! ( 23 , * prune_notifier . borrow_and_update ( ) ) ;
861
879
862
880
// GET paris from the cached store, this will not be served by the cache, because paris was
863
881
// evicted by neelix:
0 commit comments