foyer_storage/engine/mod.rs
1// Copyright 2025 foyer Project Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{any::Any, fmt::Debug, sync::Arc};
16
17use foyer_common::{
18 code::{StorageKey, StorageValue},
19 metrics::Metrics,
20 properties::{Populated, Properties},
21};
22use foyer_memory::Piece;
23use futures_core::future::BoxFuture;
24
25use crate::{error::Result, filter::StorageFilterResult, io::engine::IoEngine, keeper::PieceRef, Device, Runtime};
26
27/// Load result.
28#[derive(Debug)]
29pub enum Load<K, V, P> {
30 /// Load entry success.
31 Entry {
32 /// The key of the entry.
33 key: K,
34 /// The value of the entry.
35 value: V,
36 /// The populated source context of the entry.
37 populated: Populated,
38 },
39 /// Load entry success from disk cache write queue.
40 Piece {
41 /// The piece of the entry.
42 piece: Piece<K, V, P>,
43 /// The populated source context of the entry.
44 populated: Populated,
45 },
46 /// The entry may be in the disk cache, the read io is throttled.
47 Throttled,
48 /// Disk cache miss.
49 Miss,
50}
51
52impl<K, V, P> Load<K, V, P> {
53 /// Return `Some` with the entry if load success, otherwise return `None`.
54 pub fn entry(self) -> Option<(K, V, Populated)> {
55 match self {
56 Load::Entry { key, value, populated } => Some((key, value, populated)),
57 _ => None,
58 }
59 }
60
61 /// Return `Some` with the entry if load success, otherwise return `None`.
62 ///
63 /// Only key and value will be returned.
64 pub fn kv(self) -> Option<(K, V)> {
65 match self {
66 Load::Entry { key, value, .. } => Some((key, value)),
67 _ => None,
68 }
69 }
70
71 /// Check if the load result is a cache miss.
72 pub fn is_miss(&self) -> bool {
73 matches!(self, Load::Miss)
74 }
75
76 /// Check if the load result is miss caused by io throttled.
77 pub fn is_throttled(&self) -> bool {
78 matches!(self, Load::Throttled)
79 }
80}
81
82/// The recover mode of the disk cache.
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
84#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
85#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
86pub enum RecoverMode {
87 /// Do not recover disk cache.
88 ///
89 /// For updatable cache, either [`RecoverMode::None`] or the tombstone log must be used to prevent from phantom
90 /// entry when reopen.
91 None,
92 /// Recover disk cache and skip errors.
93 #[default]
94 Quiet,
95 /// Recover disk cache and panic on errors.
96 Strict,
97}
98
99/// Context for building the disk cache engine.
100pub struct EngineBuildContext {
101 /// IO engine for the disk cache engine.
102 pub io_engine: Arc<dyn IoEngine>,
103 /// Shared metrics for all components.
104 pub metrics: Arc<Metrics>,
105 /// The runtime for the disk cache engine.
106 pub runtime: Runtime,
107 /// The recover mode of the disk cache engine.
108 pub recover_mode: RecoverMode,
109}
110
111/// Disk cache engine builder trait.
112#[expect(clippy::type_complexity)]
113pub trait EngineConfig<K, V, P>: Send + Sync + 'static + Debug
114where
115 K: StorageKey,
116 V: StorageValue,
117 P: Properties,
118{
119 /// Build the engine with the given configurations.
120 fn build(self: Box<Self>, ctx: EngineBuildContext) -> BoxFuture<'static, Result<Arc<dyn Engine<K, V, P>>>>;
121
122 /// Box the builder.
123 fn boxed(self) -> Box<Self>
124 where
125 Self: Sized,
126 {
127 Box::new(self)
128 }
129}
130
131/// Disk cache engine trait.
132pub trait Engine<K, V, P>: Send + Sync + 'static + Debug + Any
133where
134 K: StorageKey,
135 V: StorageValue,
136 P: Properties,
137{
138 /// Get the device used by this disk cache engine.
139 fn device(&self) -> &Arc<dyn Device>;
140
141 /// Return if the given key can be picked by the disk cache engine.
142 fn filter(&self, hash: u64, estimated_size: usize) -> StorageFilterResult;
143
144 /// Push a in-memory cache piece to the disk cache write queue.
145 fn enqueue(&self, piece: PieceRef<K, V, P>, estimated_size: usize);
146
147 /// Load a cache entry from the disk cache.
148 ///
149 /// `load` may return a false-positive result on entry key hash collision. It's the caller's responsibility to
150 /// check if the returned key matches the given key.
151 fn load(&self, hash: u64) -> BoxFuture<'static, Result<Load<K, V, P>>>;
152
153 /// Delete the cache entry with the given key from the disk cache.
154 fn delete(&self, hash: u64);
155
156 /// Check if the disk cache contains a cached entry with the given key.
157 ///
158 /// `contains` may return a false-positive result if there is a hash collision with the given key.
159 fn may_contains(&self, hash: u64) -> bool;
160
161 /// Delete all cached entries of the disk cache.
162 fn destroy(&self) -> BoxFuture<'static, Result<()>>;
163
164 /// Wait for the ongoing flush and reclaim tasks to finish.
165 fn wait(&self) -> BoxFuture<'static, ()>;
166
167 /// Close the disk cache gracefully.
168 ///
169 /// `close` will wait for all ongoing flush and reclaim tasks to finish.
170 fn close(&self) -> BoxFuture<'static, Result<()>>;
171}
172
173pub mod block;
174pub mod noop;