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 eviction::{
32 fifo::{Fifo, FifoConfig},
33 lfu::{Lfu, LfuConfig},
34 lru::{Lru, LruConfig},
35 s3fifo::{S3Fifo, S3FifoConfig},
36 sieve::{Sieve, SieveConfig},
37 },
38 indexer::hash_table::HashTableIndexer,
39 inflight::{OptionalFetchBuilder, RequiredFetchBuilder},
40 pipe::ArcPipe,
41 raw::{Filter, RawCache, RawCacheConfig, RawCacheEntry, RawGetOrFetch, Weighter},
42 FetchTarget, Piece,
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 #[doc(hidden)]
914 pub fn with_pipe(self, pipe: ArcPipe<K, V, P>) -> Self {
915 match self {
916 Cache::Fifo(cache) => Cache::Fifo(cache.with_pipe(pipe)),
917 Cache::S3Fifo(cache) => Cache::S3Fifo(cache.with_pipe(pipe)),
918 Cache::Lru(cache) => Cache::Lru(cache.with_pipe(pipe)),
919 Cache::Lfu(cache) => Cache::Lfu(cache.with_pipe(pipe)),
920 Cache::Sieve(cache) => Cache::Sieve(cache.with_pipe(pipe)),
921 }
922 }
923}
924
925#[must_use]
927#[pin_project(project = GetOrFetchProj)]
928pub enum GetOrFetch<K, V, S = DefaultHasher, P = CacheProperties>
929where
930 K: Key,
931 V: Value,
932 S: HashBuilder,
933 P: Properties,
934{
935 Fifo(#[pin] FifoGetOrFetch<K, V, S, P>),
937 S3Fifo(#[pin] S3FifoGetOrFetch<K, V, S, P>),
939 Lru(#[pin] LruGetOrFetch<K, V, S, P>),
941 Lfu(#[pin] LfuGetOrFetch<K, V, S, P>),
943 Sieve(#[pin] SieveGetOrFetch<K, V, S, P>),
945}
946
947impl<K, V, S, P> Debug for GetOrFetch<K, V, S, P>
948where
949 K: Key,
950 V: Value,
951 S: HashBuilder,
952 P: Properties,
953{
954 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
955 match self {
956 Self::Fifo(raw) => f.debug_tuple("Fifo").field(raw).finish(),
957 Self::S3Fifo(raw) => f.debug_tuple("S3Fifo").field(raw).finish(),
958 Self::Lru(raw) => f.debug_tuple("Lru").field(raw).finish(),
959 Self::Lfu(raw) => f.debug_tuple("Lfu").field(raw).finish(),
960 Self::Sieve(raw) => f.debug_tuple("Sieve").field(raw).finish(),
961 }
962 }
963}
964
965impl<K, V, S, P> From<FifoGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
966where
967 K: Key,
968 V: Value,
969 S: HashBuilder,
970 P: Properties,
971{
972 fn from(entry: FifoGetOrFetch<K, V, S, P>) -> Self {
973 Self::Fifo(entry)
974 }
975}
976
977impl<K, V, S, P> From<S3FifoGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
978where
979 K: Key,
980 V: Value,
981 S: HashBuilder,
982 P: Properties,
983{
984 fn from(entry: S3FifoGetOrFetch<K, V, S, P>) -> Self {
985 Self::S3Fifo(entry)
986 }
987}
988
989impl<K, V, S, P> From<LruGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
990where
991 K: Key,
992 V: Value,
993 S: HashBuilder,
994 P: Properties,
995{
996 fn from(entry: LruGetOrFetch<K, V, S, P>) -> Self {
997 Self::Lru(entry)
998 }
999}
1000
1001impl<K, V, S, P> From<LfuGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
1002where
1003 K: Key,
1004 V: Value,
1005 S: HashBuilder,
1006 P: Properties,
1007{
1008 fn from(entry: LfuGetOrFetch<K, V, S, P>) -> Self {
1009 Self::Lfu(entry)
1010 }
1011}
1012
1013impl<K, V, S, P> From<SieveGetOrFetch<K, V, S, P>> for GetOrFetch<K, V, S, P>
1014where
1015 K: Key,
1016 V: Value,
1017 S: HashBuilder,
1018 P: Properties,
1019{
1020 fn from(entry: SieveGetOrFetch<K, V, S, P>) -> Self {
1021 Self::Sieve(entry)
1022 }
1023}
1024
1025impl<K, V, S, P> Future for GetOrFetch<K, V, S, P>
1026where
1027 K: Key + Clone,
1028 V: Value,
1029 S: HashBuilder,
1030 P: Properties,
1031{
1032 type Output = Result<CacheEntry<K, V, S, P>>;
1033
1034 fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
1035 self.poll_inner(cx)
1036 .map(|res| res.map(|opt| opt.expect("GetOrFetch future resolved to None")))
1037 }
1038}
1039
1040impl<K, V, S, P> GetOrFetch<K, V, S, P>
1041where
1042 K: Key + Clone,
1043 V: Value,
1044 S: HashBuilder,
1045 P: Properties,
1046{
1047 #[doc(hidden)]
1048 #[expect(clippy::type_complexity)]
1049 pub fn poll_inner(
1050 self: std::pin::Pin<&mut Self>,
1051 cx: &mut std::task::Context<'_>,
1052 ) -> std::task::Poll<Result<Option<CacheEntry<K, V, S, P>>>> {
1053 match self.project() {
1054 GetOrFetchProj::Fifo(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1055 GetOrFetchProj::S3Fifo(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1056 GetOrFetchProj::Lru(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1057 GetOrFetchProj::Lfu(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1058 GetOrFetchProj::Sieve(fut) => fut.poll(cx).map(|res| res.map(|opt| opt.map(CacheEntry::from))),
1059 }
1060 }
1061
1062 pub fn need_await(&self) -> bool {
1064 match self {
1065 GetOrFetch::Fifo(fut) => fut.need_await(),
1066 GetOrFetch::S3Fifo(fut) => fut.need_await(),
1067 GetOrFetch::Lru(fut) => fut.need_await(),
1068 GetOrFetch::Lfu(fut) => fut.need_await(),
1069 GetOrFetch::Sieve(fut) => fut.need_await(),
1070 }
1071 }
1072
1073 #[expect(clippy::allow_attributes)]
1076 #[allow(clippy::result_large_err)]
1077 pub fn try_unwrap(self) -> std::result::Result<CacheEntry<K, V, S, P>, Self> {
1078 match self {
1079 GetOrFetch::Fifo(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1080 GetOrFetch::S3Fifo(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1081 GetOrFetch::Lru(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1082 GetOrFetch::Lfu(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1083 GetOrFetch::Sieve(fut) => fut.try_unwrap().map(|opt| opt.unwrap().into()).map_err(Self::from),
1084 }
1085 }
1086}
1087
1088impl<K, V, S, P> Cache<K, V, S, P>
1089where
1090 K: Key,
1091 V: Value,
1092 S: HashBuilder,
1093 P: Properties,
1094{
1095 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::get_or_fetch"))]
1101 pub fn get_or_fetch<Q, F, FU, IT, ER>(&self, key: &Q, fetch: F) -> GetOrFetch<K, V, S, P>
1102 where
1103 Q: Hash + Equivalent<K> + ?Sized + ToOwned<Owned = K>,
1104 F: FnOnce() -> FU,
1105 FU: Future<Output = std::result::Result<IT, ER>> + Send + 'static,
1106 IT: Into<FetchTarget<K, V, P>>,
1107 ER: Into<anyhow::Error>,
1108 {
1109 match self {
1110 Cache::Fifo(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1111 Cache::Lru(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1112 Cache::Lfu(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1113 Cache::S3Fifo(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1114 Cache::Sieve(cache) => GetOrFetch::from(cache.get_or_fetch(key, fetch)),
1115 }
1116 }
1117
1118 #[doc(hidden)]
1119 #[cfg_attr(
1120 feature = "tracing",
1121 fastrace::trace(name = "foyer::memory::cache::get_or_fetch_inner")
1122 )]
1123 pub fn get_or_fetch_inner<Q, C, FO, FR>(
1124 &self,
1125 key: &Q,
1126 fo: FO,
1127 fr: FR,
1128 ctx: C,
1129 spawner: &Spawner,
1130 ) -> GetOrFetch<K, V, S, P>
1131 where
1132 Q: Hash + Equivalent<K> + ?Sized + ToOwned<Owned = K>,
1133 C: Any + Send + Sync + 'static,
1134 FO: FnOnce() -> Option<OptionalFetchBuilder<K, V, P, C>>,
1135 FR: FnOnce() -> Option<RequiredFetchBuilder<K, V, P, C>>,
1136 {
1137 match self {
1138 Cache::Fifo(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1139 Cache::Lru(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1140 Cache::Lfu(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1141 Cache::S3Fifo(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1142 Cache::Sieve(cache) => cache.get_or_fetch_inner(key, fo, fr, ctx, spawner).into(),
1143 }
1144 }
1145}
1146
1147#[cfg(test)]
1148mod tests {
1149 use std::{ops::Range, time::Duration};
1150
1151 use foyer_common::error::Error;
1152 use futures_util::future::join_all;
1153 use itertools::Itertools;
1154 use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng};
1155
1156 use super::*;
1157 use crate::eviction::{fifo::FifoConfig, lfu::LfuConfig, lru::LruConfig, s3fifo::S3FifoConfig};
1158
1159 const CAPACITY: usize = 100;
1160 const SHARDS: usize = 4;
1161 const RANGE: Range<u64> = 0..1000;
1162 const OPS: usize = 10000;
1163 const CONCURRENCY: usize = 8;
1164
1165 fn fifo() -> Cache<u64, u64> {
1166 CacheBuilder::new(CAPACITY)
1167 .with_shards(SHARDS)
1168 .with_eviction_config(FifoConfig {})
1169 .build()
1170 }
1171
1172 fn lru() -> Cache<u64, u64> {
1173 CacheBuilder::new(CAPACITY)
1174 .with_shards(SHARDS)
1175 .with_eviction_config(LruConfig {
1176 high_priority_pool_ratio: 0.1,
1177 })
1178 .build()
1179 }
1180
1181 fn lfu() -> Cache<u64, u64> {
1182 CacheBuilder::new(CAPACITY)
1183 .with_shards(SHARDS)
1184 .with_eviction_config(LfuConfig {
1185 window_capacity_ratio: 0.1,
1186 protected_capacity_ratio: 0.8,
1187 cmsketch_eps: 0.001,
1188 cmsketch_confidence: 0.9,
1189 })
1190 .build()
1191 }
1192
1193 fn s3fifo() -> Cache<u64, u64> {
1194 CacheBuilder::new(CAPACITY)
1195 .with_shards(SHARDS)
1196 .with_eviction_config(S3FifoConfig {
1197 small_queue_capacity_ratio: 0.1,
1198 ghost_queue_capacity_ratio: 10.0,
1199 small_to_main_freq_threshold: 2,
1200 })
1201 .build()
1202 }
1203
1204 fn sieve() -> Cache<u64, u64> {
1205 CacheBuilder::new(CAPACITY)
1206 .with_shards(SHARDS)
1207 .with_eviction_config(SieveConfig {})
1208 .build()
1209 }
1210
1211 fn init_cache(cache: &Cache<u64, u64>, rng: &mut StdRng) {
1212 let mut v = RANGE.collect_vec();
1213 v.shuffle(rng);
1214 for i in v {
1215 cache.insert(i, i);
1216 }
1217 }
1218
1219 async fn operate(cache: &Cache<u64, u64>, rng: &mut StdRng) {
1220 let i = rng.random_range(RANGE);
1221 match rng.random_range(0..=3) {
1222 0 => {
1223 let entry = cache.insert(i, i);
1224 assert_eq!(*entry.key(), i);
1225 assert_eq!(entry.key(), entry.value());
1226 }
1227 1 => {
1228 if let Some(entry) = cache.get(&i) {
1229 assert_eq!(*entry.key(), i);
1230 assert_eq!(entry.key(), entry.value());
1231 }
1232 }
1233 2 => {
1234 cache.remove(&i);
1235 }
1236 3 => {
1237 let entry = cache
1238 .get_or_fetch(&i, || async move {
1239 tokio::time::sleep(Duration::from_micros(10)).await;
1240 Ok::<_, Error>(i)
1241 })
1242 .await
1243 .unwrap();
1244 assert_eq!(*entry.key(), i);
1245 assert_eq!(entry.key(), entry.value());
1246 }
1247 _ => unreachable!(),
1248 }
1249 }
1250
1251 async fn case(cache: Cache<u64, u64>) {
1252 let mut rng = StdRng::seed_from_u64(42);
1253
1254 init_cache(&cache, &mut rng);
1255
1256 let cache = Arc::new(cache);
1257 let handles = (0..CONCURRENCY)
1258 .map(|_| {
1259 let cache = cache.clone();
1260 let mut rng = rng.clone();
1261 tokio::spawn(async move {
1262 for _ in 0..OPS {
1263 operate(&cache, &mut rng).await;
1264 }
1265 })
1266 })
1267 .collect_vec();
1268
1269 join_all(handles).await;
1270 }
1271
1272 #[tokio::test]
1273 async fn test_fifo_cache() {
1274 case(fifo()).await
1275 }
1276
1277 #[tokio::test]
1278 async fn test_lru_cache() {
1279 case(lru()).await
1280 }
1281
1282 #[tokio::test]
1283 async fn test_lfu_cache() {
1284 case(lfu()).await
1285 }
1286
1287 #[tokio::test]
1288 async fn test_s3fifo_cache() {
1289 case(s3fifo()).await
1290 }
1291
1292 #[tokio::test]
1293 async fn test_sieve_cache() {
1294 case(sieve()).await
1295 }
1296}