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, RawCache, RawCacheConfig, RawCacheEntry, RawFetch, Weighter},
40 Piece, Pipe, Result,
41};
42
43#[derive(Debug, Clone, Default)]
45pub struct CacheProperties {
46 ephemeral: bool,
47 hint: Hint,
48}
49
50impl CacheProperties {
51 pub fn with_ephemeral(mut self, ephemeral: bool) -> Self {
56 self.ephemeral = ephemeral;
57 self
58 }
59
60 pub fn ephemeral(&self) -> bool {
62 self.ephemeral
63 }
64
65 pub fn with_hint(mut self, hint: Hint) -> Self {
67 self.hint = hint;
68 self
69 }
70
71 pub fn hint(&self) -> Hint {
73 self.hint
74 }
75}
76
77impl Properties for CacheProperties {
78 fn with_ephemeral(self, ephemeral: bool) -> Self {
79 self.with_ephemeral(ephemeral)
80 }
81
82 fn ephemeral(&self) -> Option<bool> {
83 Some(self.ephemeral())
84 }
85
86 fn with_hint(self, hint: Hint) -> Self {
87 self.with_hint(hint)
88 }
89
90 fn hint(&self) -> Option<Hint> {
91 Some(self.hint())
92 }
93
94 fn with_location(self, _: Location) -> Self {
95 self
96 }
97
98 fn location(&self) -> Option<Location> {
99 None
100 }
101
102 fn with_source(self, _: Source) -> Self {
103 self
104 }
105
106 fn source(&self) -> Option<Source> {
107 None
108 }
109}
110
111pub type FifoCache<K, V, S = DefaultHasher, P = CacheProperties> = RawCache<Fifo<K, V, P>, S>;
112pub type FifoCacheEntry<K, V, S = DefaultHasher, P = CacheProperties> = RawCacheEntry<Fifo<K, V, P>, S>;
113pub type FifoFetch<K, V, ER, S = DefaultHasher, P = CacheProperties> = RawFetch<Fifo<K, V, P>, ER, S>;
114
115pub type S3FifoCache<K, V, S = DefaultHasher, P = CacheProperties> = RawCache<S3Fifo<K, V, P>, S>;
116pub type S3FifoCacheEntry<K, V, S = DefaultHasher, P = CacheProperties> = RawCacheEntry<S3Fifo<K, V, P>, S>;
117pub type S3FifoFetch<K, V, ER, S = DefaultHasher, P = CacheProperties> = RawFetch<S3Fifo<K, V, P>, ER, S>;
118
119pub type LruCache<K, V, S = DefaultHasher, P = CacheProperties> = RawCache<Lru<K, V, P>, S>;
120pub type LruCacheEntry<K, V, S = DefaultHasher, P = CacheProperties> = RawCacheEntry<Lru<K, V, P>, S>;
121pub type LruFetch<K, V, ER, S = DefaultHasher, P = CacheProperties> = RawFetch<Lru<K, V, P>, ER, S>;
122
123pub type LfuCache<K, V, S = DefaultHasher, P = CacheProperties> = RawCache<Lfu<K, V, P>, S>;
124pub type LfuCacheEntry<K, V, S = DefaultHasher, P = CacheProperties> = RawCacheEntry<Lfu<K, V, P>, S>;
125pub type LfuFetch<K, V, ER, S = DefaultHasher, P = CacheProperties> = RawFetch<Lfu<K, V, P>, ER, S>;
126
127pub type SieveCache<K, V, S = DefaultHasher, P = CacheProperties> = RawCache<Sieve<K, V, P>, S>;
128pub type SieveCacheEntry<K, V, S = DefaultHasher, P = CacheProperties> = RawCacheEntry<Sieve<K, V, P>, S>;
129pub type SieveFetch<K, V, ER, S = DefaultHasher, P = CacheProperties> = RawFetch<Sieve<K, V, P>, ER, S>;
130
131#[derive(Debug)]
133pub enum CacheEntry<K, V, S = DefaultHasher, P = CacheProperties>
134where
135 K: Key,
136 V: Value,
137 S: HashBuilder,
138 P: Properties,
139{
140 Fifo(FifoCacheEntry<K, V, S, P>),
142 S3Fifo(S3FifoCacheEntry<K, V, S, P>),
144 Lru(LruCacheEntry<K, V, S, P>),
146 Lfu(LfuCacheEntry<K, V, S, P>),
148 Sieve(SieveCacheEntry<K, V, S, P>),
150}
151
152impl<K, V, S, P> Clone for CacheEntry<K, V, S, P>
153where
154 K: Key,
155 V: Value,
156 S: HashBuilder,
157 P: Properties,
158{
159 fn clone(&self) -> Self {
160 match self {
161 Self::Fifo(entry) => Self::Fifo(entry.clone()),
162 Self::Lru(entry) => Self::Lru(entry.clone()),
163 Self::Lfu(entry) => Self::Lfu(entry.clone()),
164 Self::S3Fifo(entry) => Self::S3Fifo(entry.clone()),
165 Self::Sieve(entry) => Self::Sieve(entry.clone()),
166 }
167 }
168}
169
170impl<K, V, S, P> Deref for CacheEntry<K, V, S, P>
171where
172 K: Key,
173 V: Value,
174 S: HashBuilder,
175 P: Properties,
176{
177 type Target = V;
178
179 fn deref(&self) -> &Self::Target {
180 match self {
181 CacheEntry::Fifo(entry) => entry.deref(),
182 CacheEntry::Lru(entry) => entry.deref(),
183 CacheEntry::Lfu(entry) => entry.deref(),
184 CacheEntry::S3Fifo(entry) => entry.deref(),
185 CacheEntry::Sieve(entry) => entry.deref(),
186 }
187 }
188}
189
190impl<K, V, S, P> From<FifoCacheEntry<K, V, S, P>> for CacheEntry<K, V, S, P>
191where
192 K: Key,
193 V: Value,
194 S: HashBuilder,
195 P: Properties,
196{
197 fn from(entry: FifoCacheEntry<K, V, S, P>) -> Self {
198 Self::Fifo(entry)
199 }
200}
201
202impl<K, V, S, P> From<LruCacheEntry<K, V, S, P>> for CacheEntry<K, V, S, P>
203where
204 K: Key,
205 V: Value,
206 S: HashBuilder,
207 P: Properties,
208{
209 fn from(entry: LruCacheEntry<K, V, S, P>) -> Self {
210 Self::Lru(entry)
211 }
212}
213
214impl<K, V, S, P> From<LfuCacheEntry<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: LfuCacheEntry<K, V, S, P>) -> Self {
222 Self::Lfu(entry)
223 }
224}
225
226impl<K, V, S, P> From<S3FifoCacheEntry<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: S3FifoCacheEntry<K, V, S, P>) -> Self {
234 Self::S3Fifo(entry)
235 }
236}
237
238impl<K, V, S, P> From<SieveCacheEntry<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: SieveCacheEntry<K, V, S, P>) -> Self {
246 Self::Sieve(entry)
247 }
248}
249
250impl<K, V, S, P> CacheEntry<K, V, S, P>
251where
252 K: Key,
253 V: Value,
254 S: HashBuilder,
255 P: Properties,
256{
257 pub fn hash(&self) -> u64 {
259 match self {
260 CacheEntry::Fifo(entry) => entry.hash(),
261 CacheEntry::Lru(entry) => entry.hash(),
262 CacheEntry::Lfu(entry) => entry.hash(),
263 CacheEntry::S3Fifo(entry) => entry.hash(),
264 CacheEntry::Sieve(entry) => entry.hash(),
265 }
266 }
267
268 pub fn key(&self) -> &K {
270 match self {
271 CacheEntry::Fifo(entry) => entry.key(),
272 CacheEntry::Lru(entry) => entry.key(),
273 CacheEntry::Lfu(entry) => entry.key(),
274 CacheEntry::S3Fifo(entry) => entry.key(),
275 CacheEntry::Sieve(entry) => entry.key(),
276 }
277 }
278
279 pub fn value(&self) -> &V {
281 match self {
282 CacheEntry::Fifo(entry) => entry.value(),
283 CacheEntry::Lru(entry) => entry.value(),
284 CacheEntry::Lfu(entry) => entry.value(),
285 CacheEntry::S3Fifo(entry) => entry.value(),
286 CacheEntry::Sieve(entry) => entry.value(),
287 }
288 }
289
290 pub fn properties(&self) -> &P {
292 match self {
293 CacheEntry::Fifo(entry) => entry.properties(),
294 CacheEntry::Lru(entry) => entry.properties(),
295 CacheEntry::Lfu(entry) => entry.properties(),
296 CacheEntry::S3Fifo(entry) => entry.properties(),
297 CacheEntry::Sieve(entry) => entry.properties(),
298 }
299 }
300
301 pub fn weight(&self) -> usize {
303 match self {
304 CacheEntry::Fifo(entry) => entry.weight(),
305 CacheEntry::Lru(entry) => entry.weight(),
306 CacheEntry::Lfu(entry) => entry.weight(),
307 CacheEntry::S3Fifo(entry) => entry.weight(),
308 CacheEntry::Sieve(entry) => entry.weight(),
309 }
310 }
311
312 pub fn refs(&self) -> usize {
314 match self {
315 CacheEntry::Fifo(entry) => entry.refs(),
316 CacheEntry::Lru(entry) => entry.refs(),
317 CacheEntry::Lfu(entry) => entry.refs(),
318 CacheEntry::S3Fifo(entry) => entry.refs(),
319 CacheEntry::Sieve(entry) => entry.refs(),
320 }
321 }
322
323 pub fn is_outdated(&self) -> bool {
325 match self {
326 CacheEntry::Fifo(entry) => entry.is_outdated(),
327 CacheEntry::Lru(entry) => entry.is_outdated(),
328 CacheEntry::Lfu(entry) => entry.is_outdated(),
329 CacheEntry::S3Fifo(entry) => entry.is_outdated(),
330 CacheEntry::Sieve(entry) => entry.is_outdated(),
331 }
332 }
333
334 pub fn piece(&self) -> Piece<K, V, P> {
336 match self {
337 CacheEntry::Fifo(entry) => entry.piece(),
338 CacheEntry::Lru(entry) => entry.piece(),
339 CacheEntry::Lfu(entry) => entry.piece(),
340 CacheEntry::S3Fifo(entry) => entry.piece(),
341 CacheEntry::Sieve(entry) => entry.piece(),
342 }
343 }
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
348pub enum EvictionConfig {
349 Fifo(FifoConfig),
351 S3Fifo(S3FifoConfig),
353 Lru(LruConfig),
355 Lfu(LfuConfig),
357 Sieve(SieveConfig),
359}
360
361impl From<FifoConfig> for EvictionConfig {
362 fn from(value: FifoConfig) -> EvictionConfig {
363 EvictionConfig::Fifo(value)
364 }
365}
366
367impl From<S3FifoConfig> for EvictionConfig {
368 fn from(value: S3FifoConfig) -> EvictionConfig {
369 EvictionConfig::S3Fifo(value)
370 }
371}
372
373impl From<LruConfig> for EvictionConfig {
374 fn from(value: LruConfig) -> EvictionConfig {
375 EvictionConfig::Lru(value)
376 }
377}
378
379impl From<LfuConfig> for EvictionConfig {
380 fn from(value: LfuConfig) -> EvictionConfig {
381 EvictionConfig::Lfu(value)
382 }
383}
384
385impl From<SieveConfig> for EvictionConfig {
386 fn from(value: SieveConfig) -> EvictionConfig {
387 EvictionConfig::Sieve(value)
388 }
389}
390
391pub struct CacheBuilder<K, V, S>
393where
394 K: Key,
395 V: Value,
396 S: HashBuilder,
397{
398 name: Cow<'static, str>,
399
400 capacity: usize,
401 shards: usize,
402 eviction_config: EvictionConfig,
403
404 hash_builder: S,
405 weighter: Arc<dyn Weighter<K, V>>,
406
407 event_listener: Option<Arc<dyn EventListener<Key = K, Value = V>>>,
408
409 registry: BoxedRegistry,
410 metrics: Option<Arc<Metrics>>,
411}
412
413impl<K, V> CacheBuilder<K, V, DefaultHasher>
414where
415 K: Key,
416 V: Value,
417{
418 pub fn new(capacity: usize) -> Self {
420 Self {
421 name: "foyer".into(),
422
423 capacity,
424 shards: 8,
425 eviction_config: LruConfig::default().into(),
426
427 hash_builder: Default::default(),
428 weighter: Arc::new(|_, _| 1),
429 event_listener: None,
430
431 registry: Box::new(NoopMetricsRegistry),
432 metrics: None,
433 }
434 }
435}
436
437impl<K, V, S> CacheBuilder<K, V, S>
438where
439 K: Key,
440 V: Value,
441 S: HashBuilder,
442{
443 pub fn with_name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
449 self.name = name.into();
450 self
451 }
452
453 pub fn with_shards(mut self, shards: usize) -> Self {
456 self.shards = shards;
457 self
458 }
459
460 pub fn with_eviction_config(mut self, eviction_config: impl Into<EvictionConfig>) -> Self {
464 self.eviction_config = eviction_config.into();
465 self
466 }
467
468 pub fn with_hash_builder<OS>(self, hash_builder: OS) -> CacheBuilder<K, V, OS>
470 where
471 OS: HashBuilder,
472 {
473 CacheBuilder {
474 name: self.name,
475 capacity: self.capacity,
476 shards: self.shards,
477 eviction_config: self.eviction_config,
478 hash_builder,
479 weighter: self.weighter,
480 event_listener: self.event_listener,
481 registry: self.registry,
482 metrics: self.metrics,
483 }
484 }
485
486 pub fn with_weighter(mut self, weighter: impl Weighter<K, V>) -> Self {
488 self.weighter = Arc::new(weighter);
489 self
490 }
491
492 pub fn with_event_listener(mut self, event_listener: Arc<dyn EventListener<Key = K, Value = V>>) -> Self {
494 self.event_listener = Some(event_listener);
495 self
496 }
497
498 pub fn with_metrics_registry(mut self, registry: BoxedRegistry) -> CacheBuilder<K, V, S> {
502 self.registry = registry;
503 self
504 }
505
506 #[doc(hidden)]
510 pub fn with_metrics(mut self, metrics: Arc<Metrics>) -> Self {
511 self.metrics = Some(metrics);
512 self
513 }
514
515 pub fn build<P>(self) -> Cache<K, V, S, P>
517 where
518 P: Properties,
519 {
520 if self.capacity < self.shards {
521 tracing::warn!(
522 "The in-memory cache capacity({}) < shards({}).",
523 self.capacity,
524 self.shards
525 );
526 }
527
528 let metrics = self
529 .metrics
530 .unwrap_or_else(|| Arc::new(Metrics::new(self.name, &self.registry)));
531
532 match self.eviction_config {
533 EvictionConfig::Fifo(eviction_config) => Cache::Fifo(Arc::new(RawCache::new(RawCacheConfig {
534 capacity: self.capacity,
535 shards: self.shards,
536 eviction_config,
537 hash_builder: self.hash_builder,
538 weighter: self.weighter,
539 event_listener: self.event_listener,
540 metrics,
541 }))),
542 EvictionConfig::S3Fifo(eviction_config) => Cache::S3Fifo(Arc::new(RawCache::new(RawCacheConfig {
543 capacity: self.capacity,
544 shards: self.shards,
545 eviction_config,
546 hash_builder: self.hash_builder,
547 weighter: self.weighter,
548 event_listener: self.event_listener,
549 metrics,
550 }))),
551 EvictionConfig::Lru(eviction_config) => Cache::Lru(Arc::new(RawCache::new(RawCacheConfig {
552 capacity: self.capacity,
553 shards: self.shards,
554 eviction_config,
555 hash_builder: self.hash_builder,
556 weighter: self.weighter,
557 event_listener: self.event_listener,
558 metrics,
559 }))),
560 EvictionConfig::Lfu(eviction_config) => Cache::Lfu(Arc::new(RawCache::new(RawCacheConfig {
561 capacity: self.capacity,
562 shards: self.shards,
563 eviction_config,
564 hash_builder: self.hash_builder,
565 weighter: self.weighter,
566 event_listener: self.event_listener,
567 metrics,
568 }))),
569 EvictionConfig::Sieve(eviction_config) => Cache::Sieve(Arc::new(RawCache::new(RawCacheConfig {
570 capacity: self.capacity,
571 shards: self.shards,
572 eviction_config,
573 hash_builder: self.hash_builder,
574 weighter: self.weighter,
575 event_listener: self.event_listener,
576 metrics,
577 }))),
578 }
579 }
580}
581
582pub enum Cache<K, V, S = DefaultHasher, P = CacheProperties>
584where
585 K: Key,
586 V: Value,
587 S: HashBuilder,
588 P: Properties,
589{
590 Fifo(Arc<FifoCache<K, V, S, P>>),
592 Lru(Arc<LruCache<K, V, S, P>>),
594 Lfu(Arc<LfuCache<K, V, S, P>>),
596 S3Fifo(Arc<S3FifoCache<K, V, S, P>>),
598 Sieve(Arc<SieveCache<K, V, S, P>>),
600}
601
602impl<K, V, S, P> Debug for Cache<K, V, S, P>
603where
604 K: Key,
605 V: Value,
606 S: HashBuilder,
607 P: Properties,
608{
609 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
610 match self {
611 Self::Fifo(_) => f.debug_tuple("Cache::FifoCache").finish(),
612 Self::S3Fifo(_) => f.debug_tuple("Cache::S3FifoCache").finish(),
613 Self::Lru(_) => f.debug_tuple("Cache::LruCache").finish(),
614 Self::Lfu(_) => f.debug_tuple("Cache::LfuCache").finish(),
615 Self::Sieve(_) => f.debug_tuple("Cache::SieveCache").finish(),
616 }
617 }
618}
619
620impl<K, V, S, P> Clone for Cache<K, V, S, P>
621where
622 K: Key,
623 V: Value,
624 S: HashBuilder,
625 P: Properties,
626{
627 fn clone(&self) -> Self {
628 match self {
629 Self::Fifo(cache) => Self::Fifo(cache.clone()),
630 Self::S3Fifo(cache) => Self::S3Fifo(cache.clone()),
631 Self::Lru(cache) => Self::Lru(cache.clone()),
632 Self::Lfu(cache) => Self::Lfu(cache.clone()),
633 Self::Sieve(cache) => Self::Sieve(cache.clone()),
634 }
635 }
636}
637
638impl<K, V> Cache<K, V, DefaultHasher, CacheProperties>
639where
640 K: Key,
641 V: Value,
642{
643 pub fn builder(capacity: usize) -> CacheBuilder<K, V, DefaultHasher> {
645 CacheBuilder::new(capacity)
646 }
647}
648
649impl<K, V, S, P> Cache<K, V, S, P>
650where
651 K: Key,
652 V: Value,
653 S: HashBuilder,
654 P: Properties,
655{
656 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::resize"))]
658 pub fn resize(&self, capacity: usize) -> Result<()> {
659 match self {
660 Cache::Fifo(cache) => cache.resize(capacity),
661 Cache::S3Fifo(cache) => cache.resize(capacity),
662 Cache::Lru(cache) => cache.resize(capacity),
663 Cache::Lfu(cache) => cache.resize(capacity),
664 Cache::Sieve(cache) => cache.resize(capacity),
665 }
666 }
667
668 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::insert"))]
670 pub fn insert(&self, key: K, value: V) -> CacheEntry<K, V, S, P> {
671 match self {
672 Cache::Fifo(cache) => cache.insert(key, value).into(),
673 Cache::S3Fifo(cache) => cache.insert(key, value).into(),
674 Cache::Lru(cache) => cache.insert(key, value).into(),
675 Cache::Lfu(cache) => cache.insert(key, value).into(),
676 Cache::Sieve(cache) => cache.insert(key, value).into(),
677 }
678 }
679
680 #[cfg_attr(
682 feature = "tracing",
683 fastrace::trace(name = "foyer::memory::cache::insert_with_properties")
684 )]
685 pub fn insert_with_properties(&self, key: K, value: V, properties: P) -> CacheEntry<K, V, S, P> {
686 match self {
687 Cache::Fifo(cache) => cache.insert_with_properties(key, value, properties).into(),
688 Cache::S3Fifo(cache) => cache.insert_with_properties(key, value, properties).into(),
689 Cache::Lru(cache) => cache.insert_with_properties(key, value, properties).into(),
690 Cache::Lfu(cache) => cache.insert_with_properties(key, value, properties).into(),
691 Cache::Sieve(cache) => cache.insert_with_properties(key, value, properties).into(),
692 }
693 }
694
695 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::remove"))]
697 pub fn remove<Q>(&self, key: &Q) -> Option<CacheEntry<K, V, S, P>>
698 where
699 Q: Hash + Equivalent<K> + ?Sized,
700 {
701 match self {
702 Cache::Fifo(cache) => cache.remove(key).map(CacheEntry::from),
703 Cache::S3Fifo(cache) => cache.remove(key).map(CacheEntry::from),
704 Cache::Lru(cache) => cache.remove(key).map(CacheEntry::from),
705 Cache::Lfu(cache) => cache.remove(key).map(CacheEntry::from),
706 Cache::Sieve(cache) => cache.remove(key).map(CacheEntry::from),
707 }
708 }
709
710 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::get"))]
712 pub fn get<Q>(&self, key: &Q) -> Option<CacheEntry<K, V, S, P>>
713 where
714 Q: Hash + Equivalent<K> + ?Sized,
715 {
716 match self {
717 Cache::Fifo(cache) => cache.get(key).map(CacheEntry::from),
718 Cache::S3Fifo(cache) => cache.get(key).map(CacheEntry::from),
719 Cache::Lru(cache) => cache.get(key).map(CacheEntry::from),
720 Cache::Lfu(cache) => cache.get(key).map(CacheEntry::from),
721 Cache::Sieve(cache) => cache.get(key).map(CacheEntry::from),
722 }
723 }
724
725 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::contains"))]
727 pub fn contains<Q>(&self, key: &Q) -> bool
728 where
729 Q: Hash + Equivalent<K> + ?Sized,
730 {
731 match self {
732 Cache::Fifo(cache) => cache.contains(key),
733 Cache::S3Fifo(cache) => cache.contains(key),
734 Cache::Lru(cache) => cache.contains(key),
735 Cache::Lfu(cache) => cache.contains(key),
736 Cache::Sieve(cache) => cache.contains(key),
737 }
738 }
739
740 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::touch"))]
744 pub fn touch<Q>(&self, key: &Q) -> bool
745 where
746 Q: Hash + Equivalent<K> + ?Sized,
747 {
748 match self {
749 Cache::Fifo(cache) => cache.touch(key),
750 Cache::S3Fifo(cache) => cache.touch(key),
751 Cache::Lru(cache) => cache.touch(key),
752 Cache::Lfu(cache) => cache.touch(key),
753 Cache::Sieve(cache) => cache.touch(key),
754 }
755 }
756
757 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::clear"))]
759 pub fn clear(&self) {
760 match self {
761 Cache::Fifo(cache) => cache.clear(),
762 Cache::S3Fifo(cache) => cache.clear(),
763 Cache::Lru(cache) => cache.clear(),
764 Cache::Lfu(cache) => cache.clear(),
765 Cache::Sieve(cache) => cache.clear(),
766 }
767 }
768
769 pub fn capacity(&self) -> usize {
771 match self {
772 Cache::Fifo(cache) => cache.capacity(),
773 Cache::S3Fifo(cache) => cache.capacity(),
774 Cache::Lru(cache) => cache.capacity(),
775 Cache::Lfu(cache) => cache.capacity(),
776 Cache::Sieve(cache) => cache.capacity(),
777 }
778 }
779
780 pub fn usage(&self) -> usize {
782 match self {
783 Cache::Fifo(cache) => cache.usage(),
784 Cache::S3Fifo(cache) => cache.usage(),
785 Cache::Lru(cache) => cache.usage(),
786 Cache::Lfu(cache) => cache.usage(),
787 Cache::Sieve(cache) => cache.usage(),
788 }
789 }
790
791 pub fn hash<Q>(&self, key: &Q) -> u64
793 where
794 Q: Hash + ?Sized,
795 {
796 self.hash_builder().hash_one(key)
797 }
798
799 pub fn hash_builder(&self) -> &Arc<S> {
801 match self {
802 Cache::Fifo(cache) => cache.hash_builder(),
803 Cache::S3Fifo(cache) => cache.hash_builder(),
804 Cache::Lru(cache) => cache.hash_builder(),
805 Cache::Lfu(cache) => cache.hash_builder(),
806 Cache::Sieve(cache) => cache.hash_builder(),
807 }
808 }
809
810 pub fn shards(&self) -> usize {
812 match self {
813 Cache::Fifo(cache) => cache.shards(),
814 Cache::S3Fifo(cache) => cache.shards(),
815 Cache::Lru(cache) => cache.shards(),
816 Cache::Lfu(cache) => cache.shards(),
817 Cache::Sieve(cache) => cache.shards(),
818 }
819 }
820
821 #[doc(hidden)]
823 pub fn set_pipe(&self, pipe: Box<dyn Pipe<Key = K, Value = V, Properties = P>>) {
824 match self {
825 Cache::Fifo(cache) => cache.set_pipe(pipe),
826 Cache::S3Fifo(cache) => cache.set_pipe(pipe),
827 Cache::Lru(cache) => cache.set_pipe(pipe),
828 Cache::Lfu(cache) => cache.set_pipe(pipe),
829 Cache::Sieve(cache) => cache.set_pipe(pipe),
830 }
831 }
832
833 pub fn evict_all(&self) {
838 match self {
839 Cache::Fifo(cache) => cache.evict_all(),
840 Cache::S3Fifo(cache) => cache.evict_all(),
841 Cache::Lru(cache) => cache.evict_all(),
842 Cache::Lfu(cache) => cache.evict_all(),
843 Cache::Sieve(cache) => cache.evict_all(),
844 }
845 }
846
847 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::raw::offload"))]
852 pub async fn flush(&self) {
853 match self {
854 Cache::Fifo(cache) => cache.flush().await,
855 Cache::S3Fifo(cache) => cache.flush().await,
856 Cache::Lru(cache) => cache.flush().await,
857 Cache::Lfu(cache) => cache.flush().await,
858 Cache::Sieve(cache) => cache.flush().await,
859 }
860 }
861}
862
863#[pin_project(project = FetchProj)]
865pub enum Fetch<K, V, ER, S = DefaultHasher, P = CacheProperties>
866where
867 K: Key,
868 V: Value,
869 S: HashBuilder,
870 P: Properties,
871{
872 Fifo(#[pin] FifoFetch<K, V, ER, S, P>),
874 S3Fifo(#[pin] S3FifoFetch<K, V, ER, S, P>),
876 Lru(#[pin] LruFetch<K, V, ER, S, P>),
878 Lfu(#[pin] LfuFetch<K, V, ER, S, P>),
880 Sieve(#[pin] SieveFetch<K, V, ER, S, P>),
882}
883
884impl<K, V, ER, S, P> From<FifoFetch<K, V, ER, S, P>> for Fetch<K, V, ER, S, P>
885where
886 K: Key,
887 V: Value,
888 S: HashBuilder,
889 P: Properties,
890{
891 fn from(entry: FifoFetch<K, V, ER, S, P>) -> Self {
892 Self::Fifo(entry)
893 }
894}
895
896impl<K, V, ER, S, P> From<S3FifoFetch<K, V, ER, S, P>> for Fetch<K, V, ER, S, P>
897where
898 K: Key,
899 V: Value,
900 S: HashBuilder,
901 P: Properties,
902{
903 fn from(entry: S3FifoFetch<K, V, ER, S, P>) -> Self {
904 Self::S3Fifo(entry)
905 }
906}
907
908impl<K, V, ER, S, P> From<LruFetch<K, V, ER, S, P>> for Fetch<K, V, ER, S, P>
909where
910 K: Key,
911 V: Value,
912 S: HashBuilder,
913 P: Properties,
914{
915 fn from(entry: LruFetch<K, V, ER, S, P>) -> Self {
916 Self::Lru(entry)
917 }
918}
919
920impl<K, V, ER, S, P> From<LfuFetch<K, V, ER, S, P>> for Fetch<K, V, ER, S, P>
921where
922 K: Key,
923 V: Value,
924 S: HashBuilder,
925 P: Properties,
926{
927 fn from(entry: LfuFetch<K, V, ER, S, P>) -> Self {
928 Self::Lfu(entry)
929 }
930}
931
932impl<K, V, ER, S, P> From<SieveFetch<K, V, ER, S, P>> for Fetch<K, V, ER, S, P>
933where
934 K: Key,
935 V: Value,
936 S: HashBuilder,
937 P: Properties,
938{
939 fn from(entry: SieveFetch<K, V, ER, S, P>) -> Self {
940 Self::Sieve(entry)
941 }
942}
943
944impl<K, V, ER, S, P> Future for Fetch<K, V, ER, S, P>
945where
946 K: Key,
947 V: Value,
948 ER: From<Error>,
949 S: HashBuilder,
950 P: Properties,
951{
952 type Output = std::result::Result<CacheEntry<K, V, S, P>, ER>;
953
954 fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
955 match self.project() {
956 FetchProj::Fifo(entry) => entry.poll(cx).map(|res| res.map(CacheEntry::from)),
957 FetchProj::S3Fifo(entry) => entry.poll(cx).map(|res| res.map(CacheEntry::from)),
958 FetchProj::Lru(entry) => entry.poll(cx).map(|res| res.map(CacheEntry::from)),
959 FetchProj::Lfu(entry) => entry.poll(cx).map(|res| res.map(CacheEntry::from)),
960 FetchProj::Sieve(entry) => entry.poll(cx).map(|res| res.map(CacheEntry::from)),
961 }
962 }
963}
964
965impl<K, V, ER, S, P> Fetch<K, V, ER, S, P>
966where
967 K: Key,
968 V: Value,
969 S: HashBuilder,
970 P: Properties,
971{
972 pub fn state(&self) -> FetchState {
974 match self {
975 Fetch::Fifo(fetch) => fetch.state(),
976 Fetch::S3Fifo(fetch) => fetch.state(),
977 Fetch::Lru(fetch) => fetch.state(),
978 Fetch::Lfu(fetch) => fetch.state(),
979 Fetch::Sieve(fetch) => fetch.state(),
980 }
981 }
982
983 #[doc(hidden)]
985 pub fn store(&self) -> &Option<FetchContext> {
986 match self {
987 Fetch::Fifo(fetch) => fetch.store(),
988 Fetch::S3Fifo(fetch) => fetch.store(),
989 Fetch::Lru(fetch) => fetch.store(),
990 Fetch::Lfu(fetch) => fetch.store(),
991 Fetch::Sieve(fetch) => fetch.store(),
992 }
993 }
994}
995
996impl<K, V, S, P> Cache<K, V, S, P>
997where
998 K: Key + Clone,
999 V: Value,
1000 S: HashBuilder,
1001 P: Properties,
1002{
1003 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::fetch"))]
1009 pub fn fetch<F, FU, ER>(&self, key: K, fetch: F) -> Fetch<K, V, ER, S, P>
1010 where
1011 F: FnOnce() -> FU,
1012 FU: Future<Output = std::result::Result<V, ER>> + Send + 'static,
1013 ER: Send + 'static + Debug,
1014 {
1015 match self {
1016 Cache::Fifo(cache) => Fetch::from(cache.fetch(key, fetch)),
1017 Cache::S3Fifo(cache) => Fetch::from(cache.fetch(key, fetch)),
1018 Cache::Lru(cache) => Fetch::from(cache.fetch(key, fetch)),
1019 Cache::Lfu(cache) => Fetch::from(cache.fetch(key, fetch)),
1020 Cache::Sieve(cache) => Fetch::from(cache.fetch(key, fetch)),
1021 }
1022 }
1023
1024 #[cfg_attr(feature = "tracing", fastrace::trace(name = "foyer::memory::cache::fetch"))]
1030 pub fn fetch_with_properties<F, FU, ER>(&self, key: K, properties: P, fetch: F) -> Fetch<K, V, ER, S, P>
1031 where
1032 F: FnOnce() -> FU,
1033 FU: Future<Output = std::result::Result<V, ER>> + Send + 'static,
1034 ER: Send + 'static + Debug,
1035 {
1036 match self {
1037 Cache::Fifo(cache) => Fetch::from(cache.fetch_with_properties(key, properties, fetch)),
1038 Cache::S3Fifo(cache) => Fetch::from(cache.fetch_with_properties(key, properties, fetch)),
1039 Cache::Lru(cache) => Fetch::from(cache.fetch_with_properties(key, properties, fetch)),
1040 Cache::Lfu(cache) => Fetch::from(cache.fetch_with_properties(key, properties, fetch)),
1041 Cache::Sieve(cache) => Fetch::from(cache.fetch_with_properties(key, properties, fetch)),
1042 }
1043 }
1044
1045 #[doc(hidden)]
1053 pub fn fetch_inner<F, FU, ER, ID>(
1054 &self,
1055 key: K,
1056 properties: P,
1057 fetch: F,
1058 runtime: &SingletonHandle,
1059 ) -> Fetch<K, V, ER, S, P>
1060 where
1061 F: FnOnce() -> FU,
1062 FU: Future<Output = ID> + Send + 'static,
1063 ER: Send + 'static + Debug,
1064 ID: Into<Diversion<std::result::Result<V, ER>, FetchContext>>,
1065 {
1066 match self {
1067 Cache::Fifo(cache) => Fetch::from(cache.fetch_inner(key, properties, fetch, runtime)),
1068 Cache::Lru(cache) => Fetch::from(cache.fetch_inner(key, properties, fetch, runtime)),
1069 Cache::Lfu(cache) => Fetch::from(cache.fetch_inner(key, properties, fetch, runtime)),
1070 Cache::S3Fifo(cache) => Fetch::from(cache.fetch_inner(key, properties, fetch, runtime)),
1071 Cache::Sieve(cache) => Fetch::from(cache.fetch_inner(key, properties, fetch, runtime)),
1072 }
1073 }
1074}
1075
1076#[cfg(test)]
1077mod tests {
1078 use std::{ops::Range, time::Duration};
1079
1080 use futures_util::future::join_all;
1081 use itertools::Itertools;
1082 use rand::{rngs::StdRng, seq::SliceRandom, Rng, SeedableRng};
1083
1084 use super::*;
1085 use crate::eviction::{fifo::FifoConfig, lfu::LfuConfig, lru::LruConfig, s3fifo::S3FifoConfig};
1086
1087 const CAPACITY: usize = 100;
1088 const SHARDS: usize = 4;
1089 const RANGE: Range<u64> = 0..1000;
1090 const OPS: usize = 10000;
1091 const CONCURRENCY: usize = 8;
1092
1093 fn fifo() -> Cache<u64, u64> {
1094 CacheBuilder::new(CAPACITY)
1095 .with_shards(SHARDS)
1096 .with_eviction_config(FifoConfig {})
1097 .build()
1098 }
1099
1100 fn lru() -> Cache<u64, u64> {
1101 CacheBuilder::new(CAPACITY)
1102 .with_shards(SHARDS)
1103 .with_eviction_config(LruConfig {
1104 high_priority_pool_ratio: 0.1,
1105 })
1106 .build()
1107 }
1108
1109 fn lfu() -> Cache<u64, u64> {
1110 CacheBuilder::new(CAPACITY)
1111 .with_shards(SHARDS)
1112 .with_eviction_config(LfuConfig {
1113 window_capacity_ratio: 0.1,
1114 protected_capacity_ratio: 0.8,
1115 cmsketch_eps: 0.001,
1116 cmsketch_confidence: 0.9,
1117 })
1118 .build()
1119 }
1120
1121 fn s3fifo() -> Cache<u64, u64> {
1122 CacheBuilder::new(CAPACITY)
1123 .with_shards(SHARDS)
1124 .with_eviction_config(S3FifoConfig {
1125 small_queue_capacity_ratio: 0.1,
1126 ghost_queue_capacity_ratio: 10.0,
1127 small_to_main_freq_threshold: 2,
1128 })
1129 .build()
1130 }
1131
1132 fn sieve() -> Cache<u64, u64> {
1133 CacheBuilder::new(CAPACITY)
1134 .with_shards(SHARDS)
1135 .with_eviction_config(SieveConfig {})
1136 .build()
1137 }
1138
1139 fn init_cache(cache: &Cache<u64, u64>, rng: &mut StdRng) {
1140 let mut v = RANGE.collect_vec();
1141 v.shuffle(rng);
1142 for i in v {
1143 cache.insert(i, i);
1144 }
1145 }
1146
1147 async fn operate(cache: &Cache<u64, u64>, rng: &mut StdRng) {
1148 let i = rng.random_range(RANGE);
1149 match rng.random_range(0..=3) {
1150 0 => {
1151 let entry = cache.insert(i, i);
1152 assert_eq!(*entry.key(), i);
1153 assert_eq!(entry.key(), entry.value());
1154 }
1155 1 => {
1156 if let Some(entry) = cache.get(&i) {
1157 assert_eq!(*entry.key(), i);
1158 assert_eq!(entry.key(), entry.value());
1159 }
1160 }
1161 2 => {
1162 cache.remove(&i);
1163 }
1164 3 => {
1165 let entry = cache
1166 .fetch(i, || async move {
1167 tokio::time::sleep(Duration::from_micros(10)).await;
1168 Ok::<_, Error>(i)
1169 })
1170 .await
1171 .unwrap();
1172 assert_eq!(*entry.key(), i);
1173 assert_eq!(entry.key(), entry.value());
1174 }
1175 _ => unreachable!(),
1176 }
1177 }
1178
1179 async fn case(cache: Cache<u64, u64>) {
1180 let mut rng = StdRng::seed_from_u64(42);
1181
1182 init_cache(&cache, &mut rng);
1183
1184 let handles = (0..CONCURRENCY)
1185 .map(|_| {
1186 let cache = cache.clone();
1187 let mut rng = rng.clone();
1188 tokio::spawn(async move {
1189 for _ in 0..OPS {
1190 operate(&cache, &mut rng).await;
1191 }
1192 })
1193 })
1194 .collect_vec();
1195
1196 join_all(handles).await;
1197 }
1198
1199 #[tokio::test]
1200 async fn test_fifo_cache() {
1201 case(fifo()).await
1202 }
1203
1204 #[tokio::test]
1205 async fn test_lru_cache() {
1206 case(lru()).await
1207 }
1208
1209 #[tokio::test]
1210 async fn test_lfu_cache() {
1211 case(lfu()).await
1212 }
1213
1214 #[tokio::test]
1215 async fn test_s3fifo_cache() {
1216 case(s3fifo()).await
1217 }
1218
1219 #[tokio::test]
1220 async fn test_sieve_cache() {
1221 case(sieve()).await
1222 }
1223}