Skip to main content

vfs/engine/
cache.rs

1use crate::engine::error::VfsResult;
2use crate::engine::metadata::MetadataStore;
3use crate::engine::types::{
4    BlockKey, ChunkEdit, ChunkRange, ChunkRef, CreateInodeAttrs, DentryStat, InodeMeta, InodePatch,
5    SnapshotId,
6};
7use async_trait::async_trait;
8use std::collections::{BTreeMap, VecDeque};
9use std::sync::Mutex;
10
11pub struct CachedMetadataStore<M> {
12    inner: M,
13    cache: Mutex<CacheState>,
14}
15
16#[derive(Debug)]
17struct CacheState {
18    capacity: usize,
19    generation: u64,
20    resolve: BTreeMap<String, Option<InodeMeta>>,
21    lstat: BTreeMap<String, Option<InodeMeta>>,
22    list_dir: BTreeMap<u64, Vec<DentryStat>>,
23    order: VecDeque<CacheKey>,
24}
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27enum CacheKey {
28    Resolve(String),
29    Lstat(String),
30    ListDir(u64),
31}
32
33impl<M> CachedMetadataStore<M> {
34    pub fn new(inner: M, capacity: usize) -> Self {
35        Self {
36            inner,
37            cache: Mutex::new(CacheState {
38                capacity,
39                generation: 0,
40                resolve: BTreeMap::new(),
41                lstat: BTreeMap::new(),
42                list_dir: BTreeMap::new(),
43                order: VecDeque::new(),
44            }),
45        }
46    }
47
48    pub fn into_inner(self) -> M {
49        self.inner
50    }
51
52    fn clear_after_mutation(&self) {
53        let mut cache = self.cache.lock().expect("cache mutex poisoned");
54        cache.generation = cache.generation.wrapping_add(1);
55        cache.resolve.clear();
56        cache.lstat.clear();
57        cache.list_dir.clear();
58        cache.order.clear();
59    }
60}
61
62impl CacheState {
63    fn remember(&mut self, key: CacheKey) {
64        if self.capacity == 0 {
65            self.resolve.clear();
66            self.lstat.clear();
67            self.list_dir.clear();
68            self.order.clear();
69            return;
70        }
71        self.order.push_back(key);
72        while self.order.len() > self.capacity {
73            if let Some(evicted) = self.order.pop_front() {
74                match evicted {
75                    CacheKey::Resolve(path) => {
76                        self.resolve.remove(&path);
77                    }
78                    CacheKey::Lstat(path) => {
79                        self.lstat.remove(&path);
80                    }
81                    CacheKey::ListDir(ino) => {
82                        self.list_dir.remove(&ino);
83                    }
84                }
85            }
86        }
87    }
88}
89
90#[async_trait]
91impl<M: MetadataStore> MetadataStore for CachedMetadataStore<M> {
92    async fn resolve(&self, path: &str) -> VfsResult<InodeMeta> {
93        let generation = {
94            let cache = self.cache.lock().expect("cache mutex poisoned");
95            if let Some(cached) = cache.resolve.get(path).cloned() {
96                return cached.ok_or_else(|| crate::engine::error::VfsError::enoent(path));
97            }
98            cache.generation
99        };
100        let result = self.inner.resolve(path).await;
101        let mut cache = self.cache.lock().expect("cache mutex poisoned");
102        if cache.generation == generation {
103            let cached = match &result {
104                Ok(meta) => Some(Some(meta.clone())),
105                Err(error) if error.code() == "ENOENT" => Some(None),
106                Err(_) => None,
107            };
108            if let Some(cached) = cached {
109                cache.resolve.insert(path.to_string(), cached);
110                cache.remember(CacheKey::Resolve(path.to_string()));
111            }
112        }
113        result
114    }
115
116    async fn resolve_parent(&self, path: &str) -> VfsResult<(InodeMeta, String)> {
117        self.inner.resolve_parent(path).await
118    }
119
120    async fn lstat(&self, path: &str) -> VfsResult<InodeMeta> {
121        let generation = {
122            let cache = self.cache.lock().expect("cache mutex poisoned");
123            if let Some(cached) = cache.lstat.get(path).cloned() {
124                return cached.ok_or_else(|| crate::engine::error::VfsError::enoent(path));
125            }
126            cache.generation
127        };
128        let result = self.inner.lstat(path).await;
129        let mut cache = self.cache.lock().expect("cache mutex poisoned");
130        if cache.generation == generation {
131            let cached = match &result {
132                Ok(meta) => Some(Some(meta.clone())),
133                Err(error) if error.code() == "ENOENT" => Some(None),
134                Err(_) => None,
135            };
136            if let Some(cached) = cached {
137                cache.lstat.insert(path.to_string(), cached);
138                cache.remember(CacheKey::Lstat(path.to_string()));
139            }
140        }
141        result
142    }
143
144    async fn list_dir(&self, ino: u64) -> VfsResult<Vec<DentryStat>> {
145        let generation = {
146            let cache = self.cache.lock().expect("cache mutex poisoned");
147            if let Some(cached) = cache.list_dir.get(&ino).cloned() {
148                return Ok(cached);
149            }
150            cache.generation
151        };
152        let entries = self.inner.list_dir(ino).await?;
153        let mut cache = self.cache.lock().expect("cache mutex poisoned");
154        if cache.generation == generation {
155            cache.list_dir.insert(ino, entries.clone());
156            cache.remember(CacheKey::ListDir(ino));
157        }
158        Ok(entries)
159    }
160
161    async fn create(
162        &self,
163        parent: u64,
164        name: &str,
165        attrs: CreateInodeAttrs,
166    ) -> VfsResult<InodeMeta> {
167        let result = self.inner.create(parent, name, attrs).await;
168        self.clear_after_mutation();
169        result
170    }
171
172    async fn link(&self, parent: u64, name: &str, target: u64) -> VfsResult<()> {
173        let result = self.inner.link(parent, name, target).await;
174        self.clear_after_mutation();
175        result
176    }
177
178    async fn remove(&self, parent: u64, name: &str) -> VfsResult<Vec<BlockKey>> {
179        let result = self.inner.remove(parent, name).await;
180        self.clear_after_mutation();
181        result
182    }
183
184    async fn rename(
185        &self,
186        src_parent: u64,
187        src: &str,
188        dst_parent: u64,
189        dst: &str,
190    ) -> VfsResult<Vec<BlockKey>> {
191        let result = self.inner.rename(src_parent, src, dst_parent, dst).await;
192        self.clear_after_mutation();
193        result
194    }
195
196    async fn set_attr(&self, ino: u64, patch: InodePatch) -> VfsResult<Vec<BlockKey>> {
197        let result = self.inner.set_attr(ino, patch).await;
198        self.clear_after_mutation();
199        result
200    }
201
202    async fn commit_write(
203        &self,
204        ino: u64,
205        edits: Vec<ChunkEdit>,
206        new_size: u64,
207        allocated_extents: Vec<(u64, u64)>,
208    ) -> VfsResult<Vec<BlockKey>> {
209        let result = self
210            .inner
211            .commit_write(ino, edits, new_size, allocated_extents)
212            .await;
213        self.clear_after_mutation();
214        result
215    }
216
217    async fn get_chunks(&self, ino: u64, range: ChunkRange) -> VfsResult<Vec<ChunkRef>> {
218        self.inner.get_chunks(ino, range).await
219    }
220
221    async fn snapshot(&self, root: u64) -> VfsResult<SnapshotId> {
222        self.inner.snapshot(root).await
223    }
224
225    async fn fork(&self, snap: SnapshotId) -> VfsResult<u64> {
226        self.inner.fork(snap).await
227    }
228
229    async fn gc(&self) -> VfsResult<Vec<BlockKey>> {
230        self.inner.gc().await
231    }
232
233    async fn flush(&self) -> VfsResult<()> {
234        self.inner.flush().await
235    }
236}