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}