1use std::collections::BTreeMap;
4use std::sync::{Arc, Mutex, MutexGuard};
5
6use sva_formula::{FOURIER_DUAL_RULES_VERSION, Hash};
7use sva_samples::{Buffer, Extent};
8
9use super::codec::{self, STORE_FORMAT};
10use super::index::{Index, key_of, name_of};
11use super::joined;
12use super::stored::{Header, Samples};
13
14pub const DEFAULT_STORE_BYTES: u64 = 2 << 30;
15
16fn version() -> String {
17 format!("sva store format {STORE_FORMAT} table {FOURIER_DUAL_RULES_VERSION}")
18}
19
20pub const INDEX_NAME: &str = "index";
21
22const META: &str = "meta";
23
24pub trait Backend: Sized {
26 type Lock;
27 fn lock(&self) -> impl Future<Output = Result<Self::Lock, String>>;
29 fn get(&self, name: &str) -> impl Future<Output = Result<Option<Vec<u8>>, String>>;
30 fn get_range(
32 &self,
33 name: &str,
34 from: u64,
35 len: u64,
36 ) -> impl Future<Output = Result<Option<Vec<u8>>, String>>;
37 fn put(&self, name: &str, bytes: &[u8]) -> impl Future<Output = Result<(), String>>;
38 fn delete(&self, name: &str) -> impl Future<Output = Result<bool, String>>;
40 fn list(&self) -> impl Future<Output = Result<Vec<(String, u64)>, String>>;
42 fn staging(&self) -> impl Future<Output = Result<Self, String>>;
44 fn rename(&self, name: &str, to: &Self) -> impl Future<Output = Result<bool, String>>;
47}
48
49#[derive(Clone, Default)]
50struct Staged {
51 chunks: Vec<(String, Extent)>,
52 meta: bool,
53}
54
55pub struct Store<B> {
59 backend: B,
60 staging: B,
61 max_bytes: u64,
62 index: Mutex<Index>,
63 staged: Mutex<BTreeMap<Hash, Staged>>,
64 written: Mutex<u64>,
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum Opening {
69 Locking,
70 ReadingIndex,
71 IndexRead {
73 index_bytes: u64,
74 entries: usize,
75 bytes: u64,
76 },
77}
78
79enum Prepared {
80 Ready { bytes: u64 },
81 Dropped,
82 Refused,
83}
84
85#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
86pub struct Persisted {
87 pub written: usize,
88 pub evicted: usize,
89 pub refused: usize,
91}
92
93fn locked<T>(held: &Mutex<T>) -> MutexGuard<'_, T> {
94 held.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
95}
96
97impl<B: Backend> Store<B> {
98 pub async fn open(backend: B, max_bytes: u64) -> Result<Store<B>, String> {
101 Store::open_told(backend, max_bytes, &|_| ()).await
102 }
103
104 pub async fn open_told(
105 backend: B,
106 max_bytes: u64,
107 told: &dyn Fn(Opening),
108 ) -> Result<Store<B>, String> {
109 let version = version();
110 told(Opening::Locking);
111 let held = backend.lock().await?;
112 told(Opening::ReadingIndex);
113 let found = backend.get(INDEX_NAME).await?;
114 let index_bytes = found.as_ref().map_or(0, |text| text.len() as u64);
115 let index = match found.and_then(|text| Index::read(&text, &version)) {
116 Some(index) => index,
117 None => {
118 let empty = Index::default();
119 backend
120 .put(INDEX_NAME, empty.text(&version).as_bytes())
121 .await?;
122 empty
123 }
124 };
125 told(Opening::IndexRead {
126 index_bytes,
127 entries: index.held.len(),
128 bytes: index.bytes,
129 });
130 let staging = backend.staging().await?;
131 drop(held);
132 Ok(Store {
133 backend,
134 staging,
135 max_bytes,
136 index: Mutex::new(index),
137 staged: Mutex::new(BTreeMap::new()),
138 written: Mutex::new(0),
139 })
140 }
141
142 pub fn max_bytes(&self) -> u64 {
143 self.max_bytes
144 }
145
146 pub fn bytes(&self) -> u64 {
147 locked(&self.index).bytes
148 }
149
150 pub fn holds(&self, key: Hash) -> bool {
151 locked(&self.index).held.contains_key(&key)
152 }
153
154 pub(crate) fn used(&self, key: Hash) {
155 locked(&self.index).used(key);
156 }
157
158 pub(crate) async fn lookup(&self, key: Hash) -> Option<Header> {
160 let found = self.written(key).await?;
161 let &Samples::Of { key: of, by } = found.samples() else {
162 return Some(found);
163 };
164 let samples = match self.written(of).await?.into_parts().1 {
165 Samples::Entry { file, runs, .. } => Samples::Entry {
166 file,
167 runs,
168 shift: -by,
169 },
170 Samples::Staged { file, chunks, .. } => Samples::Staged {
171 file,
172 chunks,
173 shift: -by,
174 },
175 _ => return None,
176 };
177 Some(Header::new(found.into_parts().0, samples))
178 }
179
180 async fn written(&self, key: Hash) -> Option<Header> {
182 if let Some(staged) = self.staged_value(key).await {
183 return Some(staged);
184 }
185 let name = name_of(key);
186 let Some(mut bytes) = self.backend.get_range(&name, 0, 8).await.ok()? else {
187 locked(&self.index).forget(key);
188 return None;
189 };
190 let rest = codec::head_len(&bytes)? - 8;
191 bytes.extend(self.backend.get_range(&name, 8, rest as u64).await.ok()??);
192 let (found, len) =
193 codec::read_head(&bytes, key).filter(|(found, _)| found.stored().key == key)?;
194 let mut index = locked(&self.index);
195 match index.held.contains_key(&key) {
196 true => index.used(key),
197 false => index.touch(key, len),
198 }
199 Some(found)
200 }
201
202 async fn staged_value(&self, key: Hash) -> Option<Header> {
203 let chunks = {
204 let staged = locked(&self.staged);
205 let held = staged.get(&key).filter(|staged| staged.meta)?;
206 held.chunks.clone()
207 };
208 let meta = self.staging.get(&staged_name(key, META)).await.ok()??;
209 let (found, _) = codec::read_head(&meta, key)?;
210 Some(match found.refers() {
211 true => found,
212 false => {
213 let samples = Samples::Staged {
214 file: key,
215 chunks,
216 shift: 0,
217 };
218 Header::new(found.into_parts().0, samples)
219 }
220 })
221 }
222
223 pub(crate) async fn read(&self, head: &Header, over: Extent) -> Option<Vec<Buffer>> {
225 let mut out = Vec::new();
226 match head.samples() {
227 Samples::None | Samples::Of { .. } => {}
228 Samples::Entry { file, runs, shift } => {
229 for run in runs {
230 let met = run.extent().intersect(over.shifted(-shift));
231 if met.is_empty() {
232 continue;
233 }
234 let (from, to) = codec::chunks_over(run, met);
235 let (at, len) = codec::span_of(run, from, to);
236 let bytes = self.backend.get_range(&name_of(*file), at, len).await;
237 let mut samples = codec::read_chunks(&bytes.ok()??, run, from, to)?;
238 samples.start += shift;
239 out.push(samples);
240 }
241 }
242 Samples::Staged { chunks, shift, .. } => {
243 for (name, e) in chunks {
244 if !e.intersect(over.shifted(-shift)).is_empty() {
245 let bytes = self.staging.get(name).await.ok()??;
246 let mut samples = codec::read_chunk(&bytes)?;
247 samples.start += shift;
248 out.push(samples);
249 }
250 }
251 }
252 }
253 Some(out)
254 }
255
256 pub(crate) async fn stage(&self, key: Hash, samples: &Buffer) -> Result<(), String> {
257 let n = {
258 let mut written = locked(&self.written);
259 *written += 1;
260 *written
261 };
262 let name = staged_name(key, &n.to_string());
263 self.staging.put(&name, &codec::chunk(samples)).await?;
264 locked(&self.staged)
265 .entry(key)
266 .or_default()
267 .chunks
268 .push((name, samples.extent()));
269 Ok(())
270 }
271
272 pub(crate) async fn stage_meta(&self, key: Hash, head: &Header) -> Result<(), String> {
273 let name = staged_name(key, META);
274 self.staging.put(&name, &codec::entry(head, &[])).await?;
275 locked(&self.staged).entry(key).or_default().meta = true;
276 Ok(())
277 }
278
279 pub(crate) async fn persist(&self) -> Result<Persisted, String> {
284 let idle = {
285 let index = locked(&self.index);
286 index.bytes <= self.max_bytes && index.unsynced().is_empty()
287 };
288 if idle && locked(&self.staged).is_empty() {
289 return Ok(Persisted::default());
290 }
291 let _held = self.backend.lock().await?;
292 let (mut index, seen) = self.current().await?;
293 let mut done = Persisted::default();
294 let mut moving = Vec::new();
295 let keys: Vec<Hash> = locked(&self.staged).keys().copied().collect();
296 for key in keys {
297 let Some(held) = locked(&self.staged).get(&key).cloned() else {
298 continue;
299 };
300 match self.prepared(key, &held).await? {
301 Prepared::Ready { bytes } => {
302 moving.push((key, held, bytes));
303 continue;
304 }
305 Prepared::Dropped => {}
306 Prepared::Refused => done.refused += 1,
307 }
308 self.unstaged(key, &held).await?;
309 }
310 if !moving.is_empty() {
311 let mut naming = index.clone();
312 for (key, _, bytes) in &moving {
313 naming.touch(*key, *bytes);
314 }
315 let text = naming.text(&version());
316 self.backend.put(INDEX_NAME, text.as_bytes()).await?;
317 }
318 for (key, held, bytes) in moving {
319 if !self.staging.rename(&name_of(key), &self.backend).await? {
320 continue;
321 }
322 index.touch(key, bytes);
323 done.written += 1;
324 self.unstaged(key, &held).await?;
325 }
326 for key in index.oldest_first() {
327 if index.bytes <= self.max_bytes {
328 break;
329 }
330 if self.backend.delete(&name_of(key)).await? {
331 index.forget(key);
332 done.evicted += 1;
333 }
334 }
335 self.backend
336 .put(INDEX_NAME, index.text(&version()).as_bytes())
337 .await?;
338 index.synced();
339 let mut local = locked(&self.index);
340 for key in local.used_since(seen) {
341 index.used(key);
342 }
343 *local = index;
344 Ok(done)
345 }
346
347 async fn current(&self) -> Result<(Index, u64), String> {
349 let found = self.backend.get(INDEX_NAME).await?;
350 let mut index = match found.and_then(|text| Index::read(&text, &version())) {
351 Some(index) => index,
352 None => {
353 let listed = self.backend.list().await?;
354 let listed = listed
355 .into_iter()
356 .filter_map(|(name, bytes)| Some((key_of(&name)?, bytes)));
357 Index::listed(listed.collect(), &locked(&self.index))
358 }
359 };
360 let local = locked(&self.index);
361 for key in local.unsynced() {
362 index.used(key);
363 }
364 Ok((index, local.clock()))
365 }
366
367 async fn unstaged(&self, key: Hash, held: &Staged) -> Result<(), String> {
368 for (chunk, _) in &held.chunks {
369 self.staging.delete(chunk).await?;
370 }
371 self.staging.delete(&staged_name(key, META)).await?;
372 locked(&self.staged).remove(&key);
373 Ok(())
374 }
375
376 async fn prepared(&self, key: Hash, held: &Staged) -> Result<Prepared, String> {
378 let whole = match held.meta {
379 true => self.staged_value_of(key, held).await?,
380 false => None,
381 };
382 let whole = whole.filter(|(head, runs)| !runs.is_empty() || head.refers());
383 let Some((head, mut runs)) = whole else {
384 return Ok(Prepared::Dropped);
385 };
386 let name = name_of(key);
387 if !head.refers() {
388 let held = self.backend.get(&name).await?;
389 if let Some(held) = held.and_then(|bytes| codec::read_runs(&bytes, key)) {
390 runs = joined_runs(runs, held);
391 }
392 }
393 let bytes = codec::entry(&head, &runs);
394 if bytes.len() as u64 > self.max_bytes {
395 return Ok(Prepared::Refused);
396 }
397 self.staging.put(&name, &bytes).await?;
398 Ok(Prepared::Ready {
399 bytes: bytes.len() as u64,
400 })
401 }
402
403 async fn staged_value_of(
404 &self,
405 key: Hash,
406 held: &Staged,
407 ) -> Result<Option<(Header, Vec<Buffer>)>, String> {
408 let Some(meta) = self.staging.get(&staged_name(key, META)).await? else {
409 return Ok(None);
410 };
411 let Some((head, _)) = codec::read_head(&meta, key) else {
412 return Ok(None);
413 };
414 let mut runs = Vec::new();
415 for (chunk, _) in &held.chunks {
416 let bytes = self.staging.get(chunk).await?;
417 match bytes.as_deref().and_then(codec::read_chunk) {
418 Some(samples) => joined(&mut runs, vec![Arc::new(samples)]),
419 None => return Ok(None),
420 }
421 }
422 let runs = runs.into_iter().map(Arc::unwrap_or_clone).collect();
423 Ok(Some((head, runs)))
424 }
425}
426
427fn joined_runs(staged: Vec<Buffer>, held: Vec<Buffer>) -> Vec<Buffer> {
428 let mut runs: Vec<Arc<Buffer>> = held.into_iter().map(Arc::new).collect();
429 joined(&mut runs, staged.into_iter().map(Arc::new).collect());
430 runs.into_iter().map(Arc::unwrap_or_clone).collect()
431}
432
433fn staged_name(key: Hash, part: &str) -> String {
434 format!("{}.{part}", name_of(key))
435}