Skip to main content

coven_database/
file_database_prepared_pin.rs

1//! A pin prepared before the write that commits it (§17.8): each file's
2//! whole cache copy written, synced and checked under a name reserved in
3//! this store, or a checked whole copy already kept. Preparing pins
4//! nothing; the write's `pin` step does, for every file at once.
5
6use super::{CacheReservation, FileDatabase};
7use crate::{file_removals::OwnedFile, DbError, FileRef};
8use coven_foundation::files::FileName;
9use coven_foundation::locks::read_unpoisoned;
10use std::sync::Arc;
11
12/// Files ready to pin in one write (§17.8). It holds nothing close waits
13/// for. Dropped uncommitted, or after the store closes, its copies' names go
14/// free, and the next write or open removes their bytes.
15pub struct PreparedPin {
16    /// The names the store that prepared the pin reserves.
17    reserved: Arc<crate::reserved_names::ReservedNames>,
18    /// Each file's whole copy, under the name reserved for it.
19    copies: Vec<(FileRef, FileName)>,
20    /// Files a checked whole copy of which was already kept when prepared.
21    kept: Vec<FileRef>,
22}
23
24impl FileDatabase {
25    /// A pin of `copies`, whole files written, synced and checked, and of
26    /// `kept`, files already kept whole. The copies' names stay reserved
27    /// until a write commits the pin or it is dropped.
28    pub fn prepared_pin(
29        &self,
30        copies: Vec<CacheReservation>,
31        kept: Vec<FileRef>,
32    ) -> Result<PreparedPin, DbError> {
33        let reserved = {
34            let slot = read_unpoisoned(&self.database.inner);
35            slot.as_ref().ok_or(DbError::StoreClosed)?.staging.clone()
36        };
37        let copies = copies
38            .into_iter()
39            .map(|mut reservation| {
40                // The name stays reserved, now for the prepared pin, which
41                // holds nothing close or a reopen waits for.
42                let _writing = reservation.writing.take();
43                (reservation.file.clone(), reservation.name.clone())
44            })
45            .collect();
46        Ok(PreparedPin { reserved, copies, kept })
47    }
48
49    /// Commit a prepared pin in a write of its own, as an app's write does
50    /// with its `pin` step, for tests.
51    #[cfg(any(test, feature = "test-utils"))]
52    pub async fn commit_pin(&self, prepared: PreparedPin) -> Result<(), DbError> {
53        self.database.write(move |sql| sql.pin(prepared)).await
54    }
55
56    /// Whether a checked whole copy of this stored file is kept here, pinned.
57    pub async fn kept(&self, file: &FileRef) -> Result<bool, DbError> {
58        let file = file.clone();
59        self.read(move |db| super::cache::kept(db, &file)).await
60    }
61}
62
63impl PreparedPin {
64    /// The names reserved for its copies.
65    pub(crate) fn names(&self) -> impl Iterator<Item = OwnedFile> + '_ {
66        self.copies.iter().map(|(_, name)| OwnedFile::Cached(name.clone()))
67    }
68
69    /// Its copies and kept files, for the write committing them; dropping
70    /// what is left releases nothing.
71    pub(crate) fn take(mut self) -> (Vec<(FileRef, FileName)>, Vec<FileRef>) {
72        (std::mem::take(&mut self.copies), std::mem::take(&mut self.kept))
73    }
74}
75
76impl std::fmt::Debug for PreparedPin {
77    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78        f.debug_struct("PreparedPin")
79            .field("copies", &self.copies.len())
80            .field("kept", &self.kept.len())
81            .finish_non_exhaustive()
82    }
83}
84
85impl Drop for PreparedPin {
86    fn drop(&mut self) {
87        self.reserved.release(self.names());
88    }
89}