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