1use std::{any::Any, borrow::Cow, fmt::Debug, future::Future, hash::Hash, ops::Deref, sync::Arc};
16
17use equivalent::Equivalent;
18use foyer_common::{
19 code::{DefaultHasher, HashBuilder, Key, Value},
20 error::Result,
21 event::EventListener,
22 metrics::Metrics,
23 properties::{Age, Hint, Location, Properties, Source},
24 spawn::Spawner,
25};
26use mixtrics::{metrics::BoxedRegistry, registry::noop::NoopMetricsRegistry};
27use pin_project::pin_project;
28use serde::{Deserialize, Serialize};
29
30use crate::{
31 FetchTarget, Piece,
32 eviction::{
33 fifo::{Fifo, FifoConfig},
34 lfu::{Lfu, LfuConfig},
35 lru::{Lru, LruConfig},
36 s3fifo::{S3Fifo, S3FifoConfig},
37 sieve::{Sieve, SieveConfig},
38 },
39 indexer::hash_table::HashTableIndexer,
40 inflight::{OptionalFetchBuilder, RequiredFetchBuilder},
41 pipe::ArcPipe,
42 raw::{Filter, RawCache, RawCacheConfig, RawCacheEntry, RawGetOrFetch, Weighter},
43};
44
45#[derive(Debug, Clone, Default)]
47pub struct CacheProperties {
48 phantom: bool,
49 hint: Hint,
50}
51
52impl CacheProperties {
53 fn with_phantom(mut self, phantom: bool) -> Self {
55 self.phantom = phantom;
56 self
57 }
58
59 fn phantom(&self) -> bool {
60 self.phantom
61 }
62
63 pub fn with_hint(mut self, hint: Hint) -> Self {
65 self.hint = hint;
66 self
67 }
68
69 pub fn hint(&self) -> Hint {
71 self.hint
72 }
73}
74
75impl Properties for CacheProperties {
76 fn with_phantom(self, phantom: bool) -> Self {
77 self.with_phantom(phantom)
78 }
79
80 fn phantom(&self) -> Option<bool> {
81 Some(self.phantom())
82 }
83
84 fn with_hint(self, hint: Hint) -> Self {
85 self.with_hint(hint)
86 }
87
88 fn hint(&self) -> Option<Hint> {
89 Some(self.hint())
90 }
91
92 fn with_location(self, _: Location) -> Self {
93 self
94 }
95
96 fn location(&self) -> Option<Location> {
97 None
98 }
99
100 fn with_age(self, _: Age) -> Self {
101 self
102 }
103
104 fn age(&self) -> Option<Age> {
105 None
106 }
107}
108
109macro_rules! define_cache {
110 ($algo:ident) => {
111 paste::paste! {
112 pub type [<$algo Cache>]<K, V, S = DefaultHasher, P = CacheProperties> =
113 RawCache<$algo<K, V, P>, S, HashTableIndexer<$algo<K, V, P>>>;
114 pub type [<$algo CacheEntry>]<K, V, S = DefaultHasher, P = CacheProperties> =
115 RawCacheEntry<$algo<K, V, P>, S, HashTableIndexer<$algo<K, V, P>>>;
116 pub type [<$algo GetOrFetch>]<K, V, S = DefaultHasher, P = CacheProperties> =
117 RawGetOrFetch<$algo<K, V, P>, S, HashTableIndexer<$algo<K, V, P>>>;
118 }
119 };
120}
121
122macro_rules! define_caches {
123 ($($algo:ident),*) => {
124 $(
125 define_cache! { $algo }
126 )*
127 };
128}
129
130define_caches! { Fifo, S3Fifo, Lru, Lfu, Sieve }
134
135#[derive(Debug)]
137pub enum CacheEntry<K, V, S = DefaultHasher, P = CacheProperties>
138where
139 K: Key,
140 V: Value,
141 S: HashBuilder,
142 P: Properties,
143{
144 Fifo(FifoCacheEntry<K, V, S, P>),
146 S3Fifo(S3FifoCacheEntry<K, V, S, P>),
148 Lru(LruCacheEntry<K, V, S, P>),
150 Lfu(LfuCacheEntry<K, V, S, P>),
152 Sieve(SieveCacheEntry<K, V, S, P>),
154}
155
156impl<K, V, S, P> Clone for CacheEntry<K, V, S, P>
157where
158 K: Key,
159 V: Value,
160 S: HashBuilder,
161 P: Properties,
162{
163 fn clone(&self) -> Self {
164 match self {
165 Self::Fifo(entry) => Self::Fifo(entry.clone()),
166 Self::Lru(entry) => Self::Lru(entry.clone()),
167 Self::Lfu(entry) => Self::Lfu(entry.clone()),
168 Self::S3Fifo(entry) => Self::S3Fifo(entry.clone()),
169 Self::Sieve(entry) => Self::Sieve(entry.clone()),
170 }
171 }
172}
173
174impl<K, V, S, P> Deref for CacheEntry<K, V, S, P>
175where
176 K: Key,
177 V: Value,
178 S: HashBuilder,
179 P: Properties,
180{
181 type Target = V;
182
183 fn deref(&self) -> &Self::Target {
184 match self {
185 CacheEntry::Fifo(entry) => entry.deref(),
186 CacheEntry::Lru(entry) => entry.deref(),
187 CacheEntry::Lfu(entry) => entry.deref(),
188 CacheEntry::S3Fifo(entry) => entry.deref(),
189 CacheEntry::Sieve(entry) => entry.deref(),
190 }
191 }
192}
193
194impl<K, V, S, P> From<FifoCacheEntry<K, V, S, P>> for CacheEntry<K, V, S, P>
195where
196 K: Key,
197 V: Value,
198 S: HashBuilder,
199 P: Properties,
200{
201 fn from(entry: FifoCacheEntry<K, V, S, P>) -> Self {
202 Self::Fifo(entry)
203 }
204}
205
206impl<K, V, S, P> From<LruCacheEntry<K, V, S, P>> for CacheEntry<K, V, S, P>
207where
208 K: Key,
209 V: Value,
210 S: HashBuilder,
211 P: Properties,
212{
213 fn from(entry: LruCacheEntry<K, V, S, P>) -> Self {
214 Self::Lru(entry)
215 }
216}
217
218impl<K, V, S, P> From<LfuCacheEntry<K, V, S, P>> for CacheEntry<K, V, S, P>
219where
220 K: Key,
221 V: Value,
222 S: HashBuilder,
223 P: Properties,
224{
225 fn from(entry: LfuCacheEntry<K, V, S, P>) -> Self {
226 Self::Lfu(entry)
227 }
228}
229
230impl<K, V, S, P> From<S3FifoCacheEntry<K, V, S, P>> for CacheEntry<K, V, S, P>
231where
232 K: Key,
233 V: Value,
234 S: HashBuilder,
235 P: Properties,
236{
237 fn from(entry: S3FifoCacheEntry<K, V, S, P>) -> Self {
238 Self::S3Fifo(entry)
239 }
240}
241
242impl<K, V, S, P> From<SieveCacheEntry<K, V, S, P>> for CacheEntry<K, V, S, P>
243where
244 K: Key,
245 V: Value,
246 S: HashBuilder,
247 P: Properties,
248{
249 fn from(entry: SieveCacheEntry<K, V, S, P>) -> Self {
250 Self::Sieve(entry)
251 }
252}
253
254impl<K, V, S, P> CacheEntry<K, V, S, P>
255where
256 K: Key,
257 V: Value,
258 S: HashBuilder,
259 P: Properties,
260{
261 pub fn hash(&self) -> u64 {
263 match self {
264 CacheEntry::Fifo(entry) => entry.hash(),
265 CacheEntry::Lru(entry) => entry.hash(),
266 CacheEntry::Lfu(entry) => entry.hash(),
267 CacheEntry::S3Fifo(entry) => entry.hash(),
268 CacheEntry::Sieve(entry) => entry.hash(),
269 }
270 }
271
272 pub fn key(&self) -> &K {
274 match self {
275 CacheEntry::Fifo(entry) => entry.key(),
276 CacheEntry::Lru(entry) => entry.key(),
277 CacheEntry::Lfu(entry) => entry.key(),
278 CacheEntry::S3Fifo(entry) => entry.key(),
279 CacheEntry::Sieve(entry) => entry.key(),
280 }
281 }
282
283 pub fn value(&self) -> &V {
285 match self {
286 CacheEntry::Fifo(entry) => entry.value(),
287 CacheEntry::Lru(entry) => entry.value(),
288 CacheEntry::Lfu(entry) => entry.value(),
289 CacheEntry::S3Fifo(entry) => entry.value(),
290 CacheEntry::Sieve(entry) => entry.value(),
291 }
292 }
293
294 pub fn properties(&self) -> &P {
296 match self {
297 CacheEntry::Fifo(entry) => entry.properties(),
298 CacheEntry::Lru(entry) => entry.properties(),
299 CacheEntry::Lfu(entry) => entry.properties(),
300 CacheEntry::S3Fifo(entry) => entry.properties(),
301 CacheEntry::Sieve(entry) => entry.properties(),
302 }
303 }
304
305 pub fn weight(&self) -> usize {
307 match self {
308 CacheEntry::Fifo(entry) => entry.weight(),
309 CacheEntry::Lru(entry) => entry.weight(),
310 CacheEntry::Lfu(entry) => entry.weight(),
311 CacheEntry::S3Fifo(entry) => entry.weight(),
312 CacheEntry::Sieve(entry) => entry.weight(),
313 }
314 }
315
316 pub fn refs(&self) -> usize {
318 match self {
319 CacheEntry::Fifo(entry) => entry.refs(),
320 CacheEntry::Lru(entry) => entry.refs(),
321 CacheEntry::Lfu(entry) => entry.refs(),
322 CacheEntry::S3Fifo(entry) => entry.refs(),
323 CacheEntry::Sieve(entry) => entry.refs(),
324 }
325 }
326
327 pub fn is_outdated(&self) -> bool {
329 match self {
330 CacheEntry::Fifo(entry) => entry.is_outdated(),
331 CacheEntry::Lru(entry) => entry.is_outdated(),
332 CacheEntry::Lfu(entry) => entry.is_outdated(),
333 CacheEntry::S3Fifo(entry) => entry.is_outdated(),
334 CacheEntry::Sieve(entry) => entry.is_outdated(),
335 }
336 }
337
338 #[doc(hidden)]
340 pub fn piece(&self) -> Piece<K, V, P> {
341 match self {
342 CacheEntry::Fifo(entry) => entry.piece(),
343 CacheEntry::Lru(entry) => entry.piece(),
344 CacheEntry::Lfu(entry) => entry.piece(),
345 CacheEntry::S3Fifo(entry) => entry.piece(),
346 CacheEntry::Sieve(entry) => entry.piece(),
347 }
348 }
349
350 pub fn source(&self) -> Source {
352 match self {
353 CacheEntry::Fifo(entry) => entry.source(),
354 CacheEntry::Lru(entry) => entry.source(),
355 CacheEntry::Lfu(entry) => entry.source(),
356 CacheEntry::S3Fifo(entry) => entry.source(),
357 CacheEntry::Sieve(entry) => entry.source(),
358 }
359 }
360}
361
362#[derive(Debug, Clone, Serialize, Deserialize)]
364pub enum EvictionConfig {
365 Fifo(FifoConfig),
367 S3Fifo(S3FifoConfig),
369 Lru(LruConfig),
371 Lfu(LfuConfig),
373 Sieve(SieveConfig),
375}
376
377impl From<FifoConfig> for EvictionConfig {
378 fn from(value: FifoConfig) -> EvictionConfig {
379 EvictionConfig::Fifo(value)
380 }
381}
382
383impl From<S3FifoConfig> for EvictionConfig {
384 fn from(value: S3FifoConfig) -> EvictionConfig {
385 EvictionConfig::S3Fifo(value)
386 }
387}
388
389impl From<LruConfig> for EvictionConfig {
390 fn from(value: LruConfig) -> EvictionConfig {
391 EvictionConfig::Lru(value)
392 }
393}
394
395impl From<LfuConfig> for EvictionConfig {
396 fn from(value: LfuConfig) -> EvictionConfig {
397 EvictionConfig::Lfu(value)
398 }
399}
400
401impl From<SieveConfig> for EvictionConfig {
402 fn from(value: SieveConfig) -> EvictionConfig {
403 EvictionConfig::Sieve(value)
404 }
405}
406
407pub struct CacheBuilder<K, V, S>
409where
410 K: Key,
411 V: Value,
412 S: HashBuilder,
413{
414 name: Cow<'static, str>,
415
416 capacity: usize,
417 shards: usize,
418 eviction_config: EvictionConfig,
419
420 hash_builder: S,
421 weighter: Arc<dyn Weighter<K, V>>,
422 filter: Arc<dyn Filter<K, V>>,
423
424 event_listener: Option<Arc<dyn EventListener<Key = K, Value = V>>>,
425
426 registry: BoxedRegistry,
427 metrics: Option<Arc<Metrics>>,
428}
429
430impl<K, V> CacheBuilder<K, V, DefaultHasher>
431where
432 K: Key,
433 V: Value,
434{
435 pub fn new(capacity: usize) -> Self {
437 Self {
438 name: "foyer".into(),
439
440 capacity,
441 shards: 8,
442 eviction_config: LruConfig::default().into(),
443
444 hash_builder: Default::default(),
445 weighter: Arc::new(|_, _| 1),
446 filter: Arc::new(|_, _| true),
447 event_listener: None,
448
449 registry: Box::new(NoopMetricsRegistry),
450 metrics: None,
451 }
452 }
453}
454
455impl<K, V, S> CacheBuilder<K, V, S>
456where
457 K: Key,
458 V: Value,
459 S: HashBuilder,
460{
461 pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
467 self.name = name.into();
468 self
469 }
470
471 pub fn with_shards(mut self, shards: usize) -> Self {
474 self.shards = shards;
475 self
476 }
477
478 pub fn with_eviction_config(mut self, eviction_config: impl Into<EvictionConfig>) -> Self {
482 self.eviction_config = eviction_config.into();
483 self
484 }
485
486 pub fn with_hash_builder<OS>(self, hash_builder: OS) -> CacheBuilder<K, V, OS>
488 where
489 OS: HashBuilder,
490 {
491 CacheBuilder {
492 name: self.name,
493 capacity: self.capacity,
494 shards: self.shards,
495 eviction_config: self.eviction_config,
496 hash_builder,
497 weighter: self.weighter,
498 filter: self.filter,
499 event_listener: self.event_listener,
500 registry: self.registry,
501 metrics: self.metrics,
502 }
503 }
504
505 pub fn with_weighter(mut self, weighter: impl Weighter<K, V>) -> Self {
507 self.weighter = Arc::new(weighter);
508 self
509 }
510
511 pub fn with_filter(mut self, filter: impl Filter<K, V>) -> Self {
522 self.filter = Arc::new(filter);
523 self
524 }
525
526 pub fn with_event_listener(mut self, event_listener: Arc<dyn EventListener<Key = K, Value = V>>) -> Self {
528 self.event_listener = Some(event_listener);
529 self
530 }
531
532 pub fn with_metrics_registry(mut self, registry: BoxedRegistry) -> CacheBuilder<K, V, S> {
536 self.registry = registry;
537 self
538 }
539
540 #[doc(hidden)]
544 pub fn with_metrics(mut self, metrics: Arc<Metrics>) -> Self {
545 self.metrics = Some(metrics);
546 self
547 }
548
549 pub fn build<P>(self) -> Cache<K, V, S, P>
551 where
552 P: Properties,
553 {
554 if self.capacity < self.shards {
555 tracing::warn!(
556 "The in-memory cache capacity({}) < shards({}).",
557 self.capacity,
558 self.shards
559 );
560 }
561
562 let metrics = self
563 .metrics
564 .unwrap_or_else(|| Arc::new(Metrics::new(self.name, &self.registry)));
565
566 match self.eviction_config {
567 EvictionConfig::Fifo(eviction_config) => Cache::Fifo(RawCache::new(RawCacheConfig {
568 capacity: self.capacity,
569 shards: self.shards,
570 eviction_config,
571 hash_builder: self.hash_builder,
572 weighter: self.weighter,
573 filter: self.filter,
574 event_listener: self.event_listener,
575 metrics,
576 })),
577 EvictionConfig::S3Fifo(eviction_config) => Cache::S3Fifo(RawCache::new(RawCacheConfig {
578 capacity: self.capacity,
579 shards: self.shards,
580 eviction_config,
581 hash_builder: self.hash_builder,
582 weighter: self.weighter,
583 filter: self.filter,
584 event_listener: self.event_listener,
585 metrics,
586 })),
587 EvictionConfig::Lru(eviction_config) => Cache::Lru(RawCache::new(RawCacheConfig {
588 capacity: self.capacity,
589 shards: self.shards,
590 eviction_config,
591 hash_builder: self.hash_builder,
592 weighter: self.weighter,
593 filter: self.filter,
594 event_listener: self.event_listener,
595 metrics,
596 })),
597 EvictionConfig::Lfu(eviction_config) => Cache::Lfu(RawCache::new(RawCacheConfig {
598 capacity: self.capacity,
599 shards: self.shards,
600 eviction_config,
601 hash_builder: self.hash_builder,
602 weighter: self.weighter,
603 filter: self.filter,
604 event_listener: self.event_listener,
605 metrics,
606 })),
607 EvictionConfig::Sieve(eviction_config) => Cache::Sieve(RawCache::new(RawCacheConfig {
608 capacity: self.capacity,
609 shards: self.shards,
610 eviction_config,
611 hash_builder: self.hash_builder,
612 weighter: self.weighter,
613 filter: self.filter,
614 event_listener: self.event_listener,
615 metrics,
616 })),
617 }
618 }
619}
620
621pub enum Cache<K, V, S = DefaultHasher, P = CacheProperties>
623where
624 K: Key,
625 V: Value,
626 S: HashBuilder,
627 P: Properties,
628{
629 Fifo(FifoCache<K, V, S, P>),
631 Lru(LruCache<K, V, S, P>),
633 Lfu(LfuCache<K, V, S, P>),
635 S3Fifo(S3FifoCache<K, V, S, P>),
637 Sieve(SieveCache<K, V, S, P>),
639}
640
641impl<K, V, S, P> Clone for Cache<K, V, S, P>
642where
643 K: Key,
644 V: Value,
645 S: HashBuilder,
646 P: Properties,
647{
648 fn clone(&self) -> Self {
649 match self {
650 Cache::Fifo(cache) => Cache::Fifo(cache.clone()),
651 Cache::Lru(cache) => Cache::Lru(cache.clone()),
652 Cache::Lfu(cache) => Cache::Lfu(cache.clone()),
653 Cache::S3Fifo(cache) => Cache::S3Fifo(cache.clone()),
654 Cache::Sieve(cache) => Cache::Sieve(cache.clone()),
655 }
656 }
657}
658
659impl<K, V, S, P> Debug for Cache<K, V, S, P>
660where
661 K: Key,
662 V: Value,
663 S: HashBuilder,
664 P: Properties,
665{
666 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
667 match self {
668 Self::Fifo(_) => f.debug_tuple("Cache::FifoCache").finish(),
669 Self::S3Fifo(_) => f.debug_tuple("Cache::S3FifoCache").finish(),
670 Self::Lru(_) => f.debug_tuple("Cache::LruCache").finish(),
671 Self::Lfu(_) => f.debug_tuple("Cache::LfuCache").finish(),
672 Self::Sieve(_) => f.debug_tuple("Cache::SieveCache").finish(),
673 }
674 }
675}
676
677impl<K, V> Cache<K, V, DefaultHasher, CacheProperties>
678where
679 K: Key,
680 V: Value,
681{
682 pub fn builder(capacity: usize) -> CacheBuilder<K, V, DefaultHasher> {
684 CacheBuilder::new(capacity)
685 }
686}
687
688impl<K, V, S, P> Cache<K, V, S, P>
689where
690 K: Key,
691 V: Value,
692 S: HashBuilder,
693 P: Properties,
694{
695 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::resize"))]
697 pub fn resize(&self, capacity: usize) -> Result<()> {
698 match self {
699 Cache::Fifo(cache) => cache.resize(capacity),
700 Cache::S3Fifo(cache) => cache.resize(capacity),
701 Cache::Lru(cache) => cache.resize(capacity),
702 Cache::Lfu(cache) => cache.resize(capacity),
703 Cache::Sieve(cache) => cache.resize(capacity),
704 }
705 }
706
707 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::insert"))]
709 pub fn insert(&self, key: K, value: V) -> CacheEntry<K, V, S, P> {
710 match self {
711 Cache::Fifo(cache) => cache.insert(key, value).into(),
712 Cache::S3Fifo(cache) => cache.insert(key, value).into(),
713 Cache::Lru(cache) => cache.insert(key, value).into(),
714 Cache::Lfu(cache) => cache.insert(key, value).into(),
715 Cache::Sieve(cache) => cache.insert(key, value).into(),
716 }
717 }
718
719 #[cfg_attr(
721 feature = "tracing",
722 fastrace::trace(name = "foyer::memory::cache::insert_with_properties")
723 )]
724 pub fn insert_with_properties(&self, key: K, value: V, properties: P) -> CacheEntry<K, V, S, P> {
725 match self {
726 Cache::Fifo(cache) => cache.insert_with_properties(key, value, properties).into(),
727 Cache::S3Fifo(cache) => cache.insert_with_properties(key, value, properties).into(),
728 Cache::Lru(cache) => cache.insert_with_properties(key, value, properties).into(),
729 Cache::Lfu(cache) => cache.insert_with_properties(key, value, properties).into(),
730 Cache::Sieve(cache) => cache.insert_with_properties(key, value, properties).into(),
731 }
732 }
733
734 #[doc(hidden)]
735 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::insert_inner"))]
736 pub fn insert_piece(&self, piece: Piece<K, V, P>) -> CacheEntry<K, V, S, P> {
737 match self {
738 Cache::Fifo(cache) => cache.insert_piece(piece).into(),
739 Cache::S3Fifo(cache) => cache.insert_piece(piece).into(),
740 Cache::Lru(cache) => cache.insert_piece(piece).into(),
741 Cache::Lfu(cache) => cache.insert_piece(piece).into(),
742 Cache::Sieve(cache) => cache.insert_piece(piece).into(),
743 }
744 }
745
746 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::remove"))]
748 pub fn remove<Q>(&self, key: &Q) -> Option<CacheEntry<K, V, S, P>>
749 where
750 Q: Hash + Equivalent<K> + ?Sized,
751 {
752 match self {
753 Cache::Fifo(cache) => cache.remove(key).map(CacheEntry::from),
754 Cache::S3Fifo(cache) => cache.remove(key).map(CacheEntry::from),
755 Cache::Lru(cache) => cache.remove(key).map(CacheEntry::from),
756 Cache::Lfu(cache) => cache.remove(key).map(CacheEntry::from),
757 Cache::Sieve(cache) => cache.remove(key).map(CacheEntry::from),
758 }
759 }
760
761 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::get"))]
763 pub fn get<Q>(&self, key: &Q) -> Option<CacheEntry<K, V, S, P>>
764 where
765 Q: Hash + Equivalent<K> + ?Sized,
766 {
767 match self {
768 Cache::Fifo(cache) => cache.get(key).map(CacheEntry::from),
769 Cache::S3Fifo(cache) => cache.get(key).map(CacheEntry::from),
770 Cache::Lru(cache) => cache.get(key).map(CacheEntry::from),
771 Cache::Lfu(cache) => cache.get(key).map(CacheEntry::from),
772 Cache::Sieve(cache) => cache.get(key).map(CacheEntry::from),
773 }
774 }
775
776 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::contains"))]
778 pub fn contains<Q>(&self, key: &Q) -> bool
779 where
780 Q: Hash + Equivalent<K> + ?Sized,
781 {
782 match self {
783 Cache::Fifo(cache) => cache.contains(key),
784 Cache::S3Fifo(cache) => cache.contains(key),
785 Cache::Lru(cache) => cache.contains(key),
786 Cache::Lfu(cache) => cache.contains(key),
787 Cache::Sieve(cache) => cache.contains(key),
788 }
789 }
790
791 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::touch"))]
795 pub fn touch<Q>(&self, key: &Q) -> bool
796 where
797 Q: Hash + Equivalent<K> + ?Sized,
798 {
799 match self {
800 Cache::Fifo(cache) => cache.touch(key),
801 Cache::S3Fifo(cache) => cache.touch(key),
802 Cache::Lru(cache) => cache.touch(key),
803 Cache::Lfu(cache) => cache.touch(key),
804 Cache::Sieve(cache) => cache.touch(key),
805 }
806 }
807
808 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::clear"))]
810 pub fn clear(&self) {
811 match self {
812 Cache::Fifo(cache) => cache.clear(),
813 Cache::S3Fifo(cache) => cache.clear(),
814 Cache::Lru(cache) => cache.clear(),
815 Cache::Lfu(cache) => cache.clear(),
816 Cache::Sieve(cache) => cache.clear(),
817 }
818 }
819
820 pub fn capacity(&self) -> usize {
822 match self {
823 Cache::Fifo(cache) => cache.capacity(),
824 Cache::S3Fifo(cache) => cache.capacity(),
825 Cache::Lru(cache) => cache.capacity(),
826 Cache::Lfu(cache) => cache.capacity(),
827 Cache::Sieve(cache) => cache.capacity(),
828 }
829 }
830
831 pub fn usage(&self) -> usize {
833 match self {
834 Cache::Fifo(cache) => cache.usage(),
835 Cache::S3Fifo(cache) => cache.usage(),
836 Cache::Lru(cache) => cache.usage(),
837 Cache::Lfu(cache) => cache.usage(),
838 Cache::Sieve(cache) => cache.usage(),
839 }
840 }
841
842 pub fn entries(&self) -> usize {
844 match self {
845 Cache::Fifo(cache) => cache.entries(),
846 Cache::S3Fifo(cache) => cache.entries(),
847 Cache::Lru(cache) => cache.entries(),
848 Cache::Lfu(cache) => cache.entries(),
849 Cache::Sieve(cache) => cache.entries(),
850 }
851 }
852
853 pub fn hash<Q>(&self, key: &Q) -> u64
855 where
856 Q: Hash + ?Sized,
857 {
858 self.hash_builder().hash_one(key)
859 }
860
861 pub fn hash_builder(&self) -> &Arc<S> {
863 match self {
864 Cache::Fifo(cache) => cache.hash_builder(),
865 Cache::S3Fifo(cache) => cache.hash_builder(),
866 Cache::Lru(cache) => cache.hash_builder(),
867 Cache::Lfu(cache) => cache.hash_builder(),
868 Cache::Sieve(cache) => cache.hash_builder(),
869 }
870 }
871
872 pub fn shards(&self) -> usize {
874 match self {
875 Cache::Fifo(cache) => cache.shards(),
876 Cache::S3Fifo(cache) => cache.shards(),
877 Cache::Lru(cache) => cache.shards(),
878 Cache::Lfu(cache) => cache.shards(),
879 Cache::Sieve(cache) => cache.shards(),
880 }
881 }
882
883 pub fn evict_all(&self) {
888 match self {
889 Cache::Fifo(cache) => cache.evict_all(),
890 Cache::S3Fifo(cache) => cache.evict_all(),
891 Cache::Lru(cache) => cache.evict_all(),
892 Cache::Lfu(cache) => cache.evict_all(),
893 Cache::Sieve(cache) => cache.evict_all(),
894 }
895 }
896
897 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::raw::offload"))]
902 pub async fn flush(&self) {
903 match self {
904 Cache::Fifo(cache) => cache.flush().await,
905 Cache::S3Fifo(cache) => cache.flush().await,
906 Cache::Lru(cache) => cache.flush().await,
907 Cache::Lfu(cache) => cache.flush().await,
908 Cache::Sieve(cache) => cache.flush().await,
909 }
910 }
911
912 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::flush_if"))]
919 pub async fn flush_if<F>(&self, predicate: F)
920 where
921 F: FnMut(&K, &V) -> bool,
922 {
923 match self {
924 Cache::Fifo(cache) => cache.flush_if(predicate).await,
925 Cache::S3Fifo(cache) => cache.flush_if(predicate).await,
926 Cache::Lru(cache) => cache.flush_if(predicate).await,
927 Cache::Lfu(cache) => cache.flush_if(predicate).await,
928 Cache::Sieve(cache) => cache.flush_if(predicate).await,
929 }
930 }
931
932 #[doc(hidden)]
934 pub fn with_pipe(self, pipe: ArcPipe<K, V, P>) -> Self {
935 match self {
936 Cache::Fifo(cache) => Cache::Fifo(cache.with_pipe(pipe)),
937 Cache::S3Fifo(cache) => Cache::S3Fifo(cache.with_pipe(pipe)),
938 Cache::Lru(cache) => Cache::Lru(cache.with_pipe(pipe)),
939 Cache::Lfu(cache) => Cache::Lfu(cache.with_pipe(pipe)),
940 Cache::Sieve(cache) => Cache::Sieve(cache.with_pipe(pipe)),
941 }
942 }
943}
944
945#[must_use]
947#[pin_project(project = GetOrFetchProj)]
948pub enum GetOrFetch<K, V, S = DefaultHasher, P = CacheProperties>
949where
950 K: Key,
951 V: Value,
952 S: HashBuilder,
953 P: Properties,
954{
955 Fifo(#[pin] FifoGetOrFetch<K, V, S, P>),
957 S3Fifo(#[pin] S3FifoGetOrFetch<K, V, S, P>),
959 Lru(#[pin] LruGetOrFetch<K, V, S, P>),
961 Lfu(#[pin] LfuGetOrFetch<K, V, S, P>),
963 Sieve(#[pin] SieveGetOrFetch<K, V, S, P>),
965}
966
967impl<K, V, S, P> Debug for GetOrFetch<K, V, S, P>
968where
969 K: Key,
970 V: Value,
971 S: HashBuilder,
972 P: Properties,
973{
974 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
975 match self {
976 Self::Fifo(raw) => f.debug_tuple("Fifo").field(raw).finish(),
977 Self::S3Fifo(raw) => f.debug_tuple("S3Fifo").field(raw).finish(),
978 Self::Lru(raw) => f.debug_tuple("Lru").field(raw).finish(),
979 Self::Lfu(raw) => f.debug_tuple("Lfu").field(raw).finish(),
980 Self::Sieve(raw) => f.debug_tuple("Sieve").field(raw).finish(),
981 }
982 }
983}
984
985impl<K, V, S, P> From<FifoGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
986where
987 K: Key,
988 V: Value,
989 S: HashBuilder,
990 P: Properties,
991{
992 fn from(entry: FifoGetOrFetch<K, V, S, P>) -> Self {
993 Self::Fifo(entry)
994 }
995}
996
997impl<K, V, S, P> From<S3FifoGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
998where
999 K: Key,
1000 V: Value,
1001 S: HashBuilder,
1002 P: Properties,
1003{
1004 fn from(entry: S3FifoGetOrFetch<K, V, S, P>) -> Self {
1005 Self::S3Fifo(entry)
1006 }
1007}
1008
1009impl<K, V, S, P> From<LruGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
1010where
1011 K: Key,
1012 V: Value,
1013 S: HashBuilder,
1014 P: Properties,
1015{
1016 fn from(entry: LruGetOrFetch<K, V, S, P>) -> Self {
1017 Self::Lru(entry)
1018 }
1019}
1020
1021impl<K, V, S, P> From<LfuGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
1022where
1023 K: Key,
1024 V: Value,
1025 S: HashBuilder,
1026 P: Properties,
1027{
1028 fn from(entry: LfuGetOrFetch<K, V, S, P>) -> Self {
1029 Self::Lfu(entry)
1030 }
1031}
1032
1033impl<K, V, S, P> From<SieveGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
1034where
1035 K: Key,
1036 V: Value,
1037 S: HashBuilder,
1038 P: Properties,
1039{
1040 fn from(entry: SieveGetOrFetch<K, V, S, P>) -> Self {
1041 Self::Sieve(entry)
1042 }
1043}
1044
1045impl<K, V, S, P> Future for GetOrFetch<K, V, S, P>
1046where
1047 K: Key + Clone,
1048 V: Value,
1049 S: HashBuilder,
1050 P: Properties,
1051{
1052 type Output = Result<CacheEntry<K, V, S, P>>;
1053
1054 fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
1055 self.poll_inner(cx)
1056 .map(|res| res.map(|opt| opt.expect("GetOrFetch future resolved to None")))
1057 }
1058}
1059
1060impl<K, V, S, P> GetOrFetch<K, V, S, P>
1061where
1062 K: Key + Clone,
1063 V: Value,
1064 S: HashBuilder,
1065 P: Properties,
1066{
1067 #[doc(hidden)]
1068 #[expect(clippy::type_complexity)]
1069 pub fn poll_inner(
1070 self: std::pin::Pin<&mut Self>,
1071 cx: &mut std::task::Context<'_>,
1072 ) -> std::task::Poll<Result<Option<CacheEntry<K, V, S, P>>>> {
1073 match self.project() {
1074 GetOrFetchProj::Fifo(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1075 GetOrFetchProj::S3Fifo(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1076 GetOrFetchProj::Lru(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1077 GetOrFetchProj::Lfu(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1078 GetOrFetchProj::Sieve(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1079 }
1080 }
1081
1082 pub fn need_await(&self) -> bool {
1084 match self {
1085 GetOrFetch::Fifo(fut) => fut.need_await(),
1086 GetOrFetch::S3Fifo(fut) => fut.need_await(),
1087 GetOrFetch::Lru(fut) => fut.need_await(),
1088 GetOrFetch::Lfu(fut) => fut.need_await(),
1089 GetOrFetch::Sieve(fut) => fut.need_await(),
1090 }
1091 }
1092
1093 #[expect(clippy::allow_attributes)]
1096 #[allow(clippy::result_large_err)]
1097 pub fn try_unwrap(self) -> std::result::Result<CacheEntry<K, V, S, P>, Self> {
1098 match self {
1099 GetOrFetch::Fifo(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1100 GetOrFetch::S3Fifo(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1101 GetOrFetch::Lru(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1102 GetOrFetch::Lfu(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1103 GetOrFetch::Sieve(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1104 }
1105 }
1106}
1107
1108impl<K, V, S, P> Cache<K, V, S, P>
1109where
1110 K: Key,
1111 V: Value,
1112 S: HashBuilder,
1113 P: Properties,
1114{
1115 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::get_or_fetch"))]
1121 pub fn get_or_fetch<Q, F, FU, IT, ER>(&self, key: &Q, fetch: F) -> GetOrFetch<K, V, S, P>
1122 where
1123 Q: Hash + Equivalent<K> + ?Sized + ToOwned<Owned = K>,
1124 F: FnOnce() -> FU,
1125 FU: Future<Output = std::result::Result<IT, ER>> + Send + 'static,
1126 IT: Into<FetchTarget<K, V, P>>,
1127 ER: Into<anyhow::Error>,
1128 {
1129 match self {
1130 Cache::Fifo(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1131 Cache::Lru(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1132 Cache::Lfu(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1133 Cache::S3Fifo(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1134 Cache::Sieve(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1135 }
1136 }
1137
1138 #[doc(hidden)]
1139 #[cfg_attr(
1140 feature = "tracing",
1141 fastrace::trace(name = "foyer::memory::cache::get_or_fetch_inner")
1142 )]
1143 pub fn get_or_fetch_inner<Q, C, FO, FR>(
1144 &self,
1145 key: &Q,
1146 fo: FO,
1147 fr: FR,
1148 ctx: C,
1149 spawner: &Spawner,
1150 ) -> GetOrFetch<K, V, S, P>
1151 where
1152 Q: Hash + Equivalent<K> + ?Sized + ToOwned<Owned = K>,
1153 C: Any + Send + Sync + 'static,
1154 FO: FnOnce() -> Option<OptionalFetchBuilder<K, V, P, C>>,
1155 FR: FnOnce() -> Option<RequiredFetchBuilder<K, V, P, C>>,
1156 {
1157 match self {
1158 Cache::Fifo(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1159 Cache::Lru(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1160 Cache::Lfu(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1161 Cache::S3Fifo(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1162 Cache::Sieve(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1163 }
1164 }
1165}
1166
1167#[cfg(test)]
1168mod tests {
1169 use std::{ops::Range, time::Duration};
1170
1171 use foyer_common::error::Error;
1172 use futures_util::future::join_all;
1173 use itertools::Itertools;
1174 use rand::{RngExt, SeedableRng, rngs::StdRng, seq::SliceRandom};
1175
1176 use super::*;
1177 use crate::eviction::{fifo::FifoConfig, lfu::LfuConfig, lru::LruConfig, s3fifo::S3FifoConfig};
1178
1179 const CAPACITY: usize = 100;
1180 const SHARDS: usize = 4;
1181 const RANGE: Range<u64> = 0..1000;
1182 const OPS: usize = 10000;
1183 const CONCURRENCY: usize = 8;
1184
1185 fn fifo() -> Cache<u64, u64> {
1186 CacheBuilder::new(CAPACITY)
1187 .with_shards(SHARDS)
1188 .with_eviction_config(FifoConfig {})
1189 .build()
1190 }
1191
1192 fn lru() -> Cache<u64, u64> {
1193 CacheBuilder::new(CAPACITY)
1194 .with_shards(SHARDS)
1195 .with_eviction_config(LruConfig {
1196 high_priority_pool_ratio: 0.1,
1197 })
1198 .build()
1199 }
1200
1201 fn lfu() -> Cache<u64, u64> {
1202 CacheBuilder::new(CAPACITY)
1203 .with_shards(SHARDS)
1204 .with_eviction_config(LfuConfig {
1205 window_capacity_ratio: 0.1,
1206 protected_capacity_ratio: 0.8,
1207 cmsketch_eps: 0.001,
1208 cmsketch_confidence: 0.9,
1209 })
1210 .build()
1211 }
1212
1213 fn s3fifo() -> Cache<u64, u64> {
1214 CacheBuilder::new(CAPACITY)
1215 .with_shards(SHARDS)
1216 .with_eviction_config(S3FifoConfig {
1217 small_queue_capacity_ratio: 0.1,
1218 ghost_queue_capacity_ratio: 10.0,
1219 small_to_main_freq_threshold: 2,
1220 })
1221 .build()
1222 }
1223
1224 fn sieve() -> Cache<u64, u64> {
1225 CacheBuilder::new(CAPACITY)
1226 .with_shards(SHARDS)
1227 .with_eviction_config(SieveConfig {})
1228 .build()
1229 }
1230
1231 fn init_cache(cache: &Cache<u64, u64>, rng: &mut StdRng) {
1232 let mut v = RANGE.collect_vec();
1233 v.shuffle(rng);
1234 for i in v {
1235 cache.insert(i, i);
1236 }
1237 }
1238
1239 async fn operate(cache: &Cache<u64, u64>, rng: &mut StdRng) {
1240 let i = rng.random_range(RANGE);
1241 match rng.random_range(0..=3) {
1242 0 => {
1243 let entry = cache.insert(i, i);
1244 assert_eq!(*entry.key(), i);
1245 assert_eq!(entry.key(), entry.value());
1246 }
1247 1 => {
1248 if let Some(entry) = cache.get(&i) {
1249 assert_eq!(*entry.key(), i);
1250 assert_eq!(entry.key(), entry.value());
1251 }
1252 }
1253 2 => {
1254 cache.remove(&i);
1255 }
1256 3 => {
1257 let entry = cache
1258 .get_or_fetch(&i, || async move {
1259 tokio::time::sleep(Duration::from_micros(10)).await;
1260 Ok::<_, Error>(i)
1261 })
1262 .await
1263 .unwrap();
1264 assert_eq!(*entry.key(), i);
1265 assert_eq!(entry.key(), entry.value());
1266 }
1267 _ => unreachable!(),
1268 }
1269 }
1270
1271 async fn case(cache: Cache<u64, u64>) {
1272 let mut rng = StdRng::seed_from_u64(42);
1273
1274 init_cache(&cache, &mut rng);
1275
1276 let cache = Arc::new(cache);
1277 let handles = (0..CONCURRENCY)
1278 .map(|_| {
1279 let cache = cache.clone();
1280 let mut rng = StdRng::seed_from_u64(42);
1281 tokio::spawn(async move {
1282 for _ in 0..OPS {
1283 operate(&cache, &mut rng).await;
1284 }
1285 })
1286 })
1287 .collect_vec();
1288
1289 join_all(handles).await;
1290 }
1291
1292 #[tokio::test]
1293 async fn test_fifo_cache() {
1294 case(fifo()).await
1295 }
1296
1297 #[tokio::test]
1298 async fn test_lru_cache() {
1299 case(lru()).await
1300 }
1301
1302 #[tokio::test]
1303 async fn test_lfu_cache() {
1304 case(lfu()).await
1305 }
1306
1307 #[tokio::test]
1308 async fn test_s3fifo_cache() {
1309 case(s3fifo()).await
1310 }
1311
1312 #[tokio::test]
1313 async fn test_sieve_cache() {
1314 case(sieve()).await
1315 }
1316}