@@ -60,6 +60,19 @@ use crate::{
6060pub trait Weighter < K , V > : Fn ( & K , & V ) -> usize + Send + Sync + ' static { }
6161impl < K , V , T > Weighter < K , V > for T where T : Fn ( & K , & V ) -> usize + Send + Sync + ' static { }
6262
63+ /// The filter for the in-memory cache.
64+ ///
65+ /// The filter is used to decide whether to admit or reject an entry based on its key and value.
66+ ///
67+ /// If the filter returns true, the key value can be inserted into the in-memory cache;
68+ /// otherwise, the key value cannot be inserted.
69+ ///
70+ /// To ensure API consistency, the in-memory cache will still return a cache entry,
71+ /// but it will not count towards the in-memory cache usage,
72+ /// and it will be immediately reclaimed when the cache entry is dropped.
73+ pub trait Filter < K , V > : Fn ( & K , & V ) -> bool + Send + Sync + ' static { }
74+ impl < K , V , T > Filter < K , V > for T where T : Fn ( & K , & V ) -> bool + Send + Sync + ' static { }
75+
6376pub struct RawCacheConfig < E , S >
6477where
6578 E : Eviction ,
7083 pub eviction_config : E :: Config ,
7184 pub hash_builder : S ,
7285 pub weighter : Arc < dyn Weighter < E :: Key , E :: Value > > ,
86+ pub filter : Arc < dyn Filter < E :: Key , E :: Value > > ,
7387 pub event_listener : Option < Arc < dyn EventListener < Key = E :: Key , Value = E :: Value > > > ,
7488 pub metrics : Arc < Metrics > ,
7589}
@@ -398,6 +412,7 @@ where
398412
399413 hash_builder : Arc < S > ,
400414 weighter : Arc < dyn Weighter < E :: Key , E :: Value > > ,
415+ filter : Arc < dyn Filter < E :: Key , E :: Value > > ,
401416
402417 metrics : Arc < Metrics > ,
403418 event_listener : Option < Arc < dyn EventListener < Key = E :: Key , Value = E :: Value > > > ,
@@ -491,6 +506,7 @@ where
491506 capacity : config. capacity ,
492507 hash_builder : Arc :: new ( config. hash_builder ) ,
493508 weighter : config. weighter ,
509+ filter : config. filter ,
494510 metrics : config. metrics ,
495511 event_listener : config. event_listener ,
496512 pipe : ArcSwap :: new ( Arc :: new ( pipe) ) ,
@@ -559,10 +575,13 @@ where
559575 & self ,
560576 key : E :: Key ,
561577 value : E :: Value ,
562- properties : E :: Properties ,
578+ mut properties : E :: Properties ,
563579 ) -> RawCacheEntry < E , S , I > {
564580 let hash = self . inner . hash_builder . hash_one ( & key) ;
565581 let weight = ( self . inner . weighter ) ( & key, & value) ;
582+ if !( self . inner . filter ) ( & key, & value) {
583+ properties = properties. with_disposable ( true ) ;
584+ }
566585 let record = Arc :: new ( Record :: new ( Data {
567586 key,
568587 value,
@@ -582,6 +601,17 @@ where
582601 #[ cfg_attr( feature = "tracing" , fastrace:: trace( name = "foyer::memory::raw::insert_inner" ) ) ]
583602 fn insert_inner ( & self , record : Arc < Record < E > > ) -> RawCacheEntry < E , S , I > {
584603 if record. properties ( ) . disposable ( ) . unwrap_or_default ( ) {
604+ // Remove the stale record if it exists.
605+ self . inner . shards [ self . shard ( record. hash ( ) ) ]
606+ . write ( )
607+ . remove ( record. hash ( ) , record. key ( ) )
608+ . inspect ( |r| {
609+ // Deallocate data out of the lock critical section.
610+ if let Some ( listener) = self . inner . event_listener . as_ref ( ) {
611+ listener. on_leave ( Event :: Replace , r. key ( ) , r. value ( ) ) ;
612+ }
613+ } ) ;
614+
585615 // If the record is disposable, we do not insert it into the cache.
586616 // Instead, we just return it and let it be dropped immediately after the last reference drops.
587617 record. inc_refs ( 1 ) ;
@@ -1212,6 +1242,7 @@ mod tests {
12121242 eviction_config : FifoConfig :: default ( ) ,
12131243 hash_builder : Default :: default ( ) ,
12141244 weighter : Arc :: new ( |_, _| 1 ) ,
1245+ filter : Arc :: new ( |_, _| true ) ,
12151246 event_listener : None ,
12161247 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
12171248 } )
@@ -1226,6 +1257,7 @@ mod tests {
12261257 eviction_config : S3FifoConfig :: default ( ) ,
12271258 hash_builder : Default :: default ( ) ,
12281259 weighter : Arc :: new ( |_, _| 1 ) ,
1260+ filter : Arc :: new ( |_, _| true ) ,
12291261 event_listener : None ,
12301262 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
12311263 } )
@@ -1240,6 +1272,7 @@ mod tests {
12401272 eviction_config : LruConfig :: default ( ) ,
12411273 hash_builder : Default :: default ( ) ,
12421274 weighter : Arc :: new ( |_, _| 1 ) ,
1275+ filter : Arc :: new ( |_, _| true ) ,
12431276 event_listener : None ,
12441277 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
12451278 } )
@@ -1254,6 +1287,7 @@ mod tests {
12541287 eviction_config : LfuConfig :: default ( ) ,
12551288 hash_builder : Default :: default ( ) ,
12561289 weighter : Arc :: new ( |_, _| 1 ) ,
1290+ filter : Arc :: new ( |_, _| true ) ,
12571291 event_listener : None ,
12581292 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
12591293 } )
@@ -1268,6 +1302,7 @@ mod tests {
12681302 eviction_config : SieveConfig { } ,
12691303 hash_builder : Default :: default ( ) ,
12701304 weighter : Arc :: new ( |_, _| 1 ) ,
1305+ filter : Arc :: new ( |_, _| true ) ,
12711306 event_listener : None ,
12721307 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
12731308 } )
@@ -1306,6 +1341,43 @@ mod tests {
13061341 assert_eq ! ( fifo. usage( ) , 0 ) ;
13071342 drop ( e2a) ;
13081343 assert_eq ! ( fifo. usage( ) , 0 ) ;
1344+
1345+ let fifo = fifo_cache_for_test ( ) ;
1346+ fifo. insert ( 1 , 1 ) ;
1347+ assert_eq ! ( fifo. usage( ) , 1 ) ;
1348+ assert_eq ! ( fifo. get( & 1 ) . unwrap( ) . value( ) , & 1 ) ;
1349+ let e2 = fifo. insert_with_properties ( 1 , 100 , TestProperties :: default ( ) . with_disposable ( true ) ) ;
1350+ assert_eq ! ( fifo. usage( ) , 0 ) ;
1351+ drop ( e2) ;
1352+ assert_eq ! ( fifo. usage( ) , 0 ) ;
1353+ assert ! ( fifo. get( & 1 ) . is_none( ) ) ;
1354+ }
1355+
1356+ #[ expect( clippy:: type_complexity) ]
1357+ #[ test_log:: test]
1358+ fn test_insert_filter ( ) {
1359+ let fifo: RawCache <
1360+ Fifo < u64 , u64 , TestProperties > ,
1361+ ModHasher ,
1362+ HashTableIndexer < Fifo < u64 , u64 , TestProperties > > ,
1363+ > = RawCache :: new ( RawCacheConfig {
1364+ capacity : 256 ,
1365+ shards : 4 ,
1366+ eviction_config : FifoConfig :: default ( ) ,
1367+ hash_builder : Default :: default ( ) ,
1368+ weighter : Arc :: new ( |_, _| 1 ) ,
1369+ filter : Arc :: new ( |k, _| !matches ! ( * k, 42 ) ) ,
1370+ event_listener : None ,
1371+ metrics : Arc :: new ( Metrics :: noop ( ) ) ,
1372+ } ) ;
1373+
1374+ fifo. insert ( 1 , 1 ) ;
1375+ fifo. insert ( 2 , 2 ) ;
1376+ fifo. insert ( 42 , 42 ) ;
1377+ assert_eq ! ( fifo. usage( ) , 2 ) ;
1378+ assert_eq ! ( fifo. get( & 1 ) . unwrap( ) . value( ) , & 1 ) ;
1379+ assert_eq ! ( fifo. get( & 2 ) . unwrap( ) . value( ) , & 2 ) ;
1380+ assert ! ( fifo. get( & 42 ) . is_none( ) ) ;
13091381 }
13101382
13111383 #[ test]
@@ -1338,6 +1410,7 @@ mod tests {
13381410 eviction_config : FifoConfig :: default ( ) ,
13391411 hash_builder : Default :: default ( ) ,
13401412 weighter : Arc :: new ( |k, v| k. len ( ) + v. len ( ) ) ,
1413+ filter : Arc :: new ( |_, _| true ) ,
13411414 event_listener : None ,
13421415 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
13431416 } ) ;
@@ -1440,6 +1513,7 @@ mod tests {
14401513 eviction_config : FifoConfig :: default ( ) ,
14411514 hash_builder : Default :: default ( ) ,
14421515 weighter : Arc :: new ( |_, _| 1 ) ,
1516+ filter : Arc :: new ( |_, _| true ) ,
14431517 event_listener : None ,
14441518 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
14451519 } ) ;
@@ -1455,6 +1529,7 @@ mod tests {
14551529 eviction_config : S3FifoConfig :: default ( ) ,
14561530 hash_builder : Default :: default ( ) ,
14571531 weighter : Arc :: new ( |_, _| 1 ) ,
1532+ filter : Arc :: new ( |_, _| true ) ,
14581533 event_listener : None ,
14591534 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
14601535 } ) ;
@@ -1470,6 +1545,7 @@ mod tests {
14701545 eviction_config : LruConfig :: default ( ) ,
14711546 hash_builder : Default :: default ( ) ,
14721547 weighter : Arc :: new ( |_, _| 1 ) ,
1548+ filter : Arc :: new ( |_, _| true ) ,
14731549 event_listener : None ,
14741550 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
14751551 } ) ;
@@ -1485,6 +1561,7 @@ mod tests {
14851561 eviction_config : LfuConfig :: default ( ) ,
14861562 hash_builder : Default :: default ( ) ,
14871563 weighter : Arc :: new ( |_, _| 1 ) ,
1564+ filter : Arc :: new ( |_, _| true ) ,
14881565 event_listener : None ,
14891566 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
14901567 } ) ;
@@ -1500,6 +1577,7 @@ mod tests {
15001577 eviction_config : SieveConfig { } ,
15011578 hash_builder : Default :: default ( ) ,
15021579 weighter : Arc :: new ( |_, _| 1 ) ,
1580+ filter : Arc :: new ( |_, _| true ) ,
15031581 event_listener : None ,
15041582 metrics : Arc :: new ( Metrics :: noop ( ) ) ,
15051583 } ) ;
0 commit comments