Skip to main content

somatize_store/
cache.rs

1//! The engine's [`Keeper`], filled in by a [`Store`]: an algorithm to hash with
2//! and somewhere for bytes to live, the two things the core cannot have.
3//!
4//! ```text
5//! key(root) = sha256( the input, in bytes )
6//! key(node) = sha256( identity | declaration | state | salt | the keys above )
7//! ```
8//!
9//! The pieces are **framed by their length** before being hashed: run together,
10//! `["ab", "c"]` and `["a", "bc"]` would be one string, and two recipes under
11//! one name is the failure a cache must not have.
12//!
13//! A cached value is bound under `value:<key>` and an artifact under
14//! `artifact:<kind>:<id>`, so one directory holds both without an id that reads
15//! like a key being mistaken for one.
16//!
17//! The bytes are written with MessagePack, the same way the wire writes them,
18//! and those few lines are duplicated on purpose: a wire's two ends are the same
19//! binary, and a store outlives every binary that wrote into it.
20
21use crate::{Digest, Meta, Store, StoreError};
22use somatize_core::{Keeper, KeeperError, Kept, Key, Value};
23
24/// Names what a graph produces, and keeps it in a store.
25pub struct Cache<'a> {
26    store: &'a dyn Store,
27}
28
29impl<'a> Cache<'a> {
30    /// The cache kept in this store.
31    pub fn over(store: &'a dyn Store) -> Self {
32        Self { store }
33    }
34}
35
36impl Keeper for Cache<'_> {
37    fn key_of(&self, value: &Value) -> Option<Key> {
38        match value.travels() {
39            true => bytes_of(value).ok().map(|bytes| key(Digest::of(&bytes))),
40            false => None,
41        }
42    }
43
44    fn combine(&self, parts: &[&str]) -> Key {
45        let mut recipe = Vec::new();
46        for part in parts {
47            // The length first, so the pieces cannot run into each other.
48            recipe.extend_from_slice(&(part.len() as u64).to_le_bytes());
49            recipe.extend_from_slice(part.as_bytes());
50        }
51        key(Digest::of(&recipe))
52    }
53
54    /// One scan and **no fetches**, which is the whole reason the engine asks
55    /// this instead of reading: what it wants to know is whether it can skip
56    /// the node underneath, and the bytes of the answer are somebody else's
57    /// business — often nobody's.
58    fn present(&self, keys: &[&Key]) -> Result<Vec<bool>, KeeperError> {
59        let names: Vec<String> = keys.iter().map(|key| name_of(key)).collect();
60        let asked: Vec<&str> = names.iter().map(String::as_str).collect();
61        Ok(self
62            .store
63            .resolve_many(&asked)
64            .map_err(failed)?
65            .into_iter()
66            .map(|bound| bound.is_some())
67            .collect())
68    }
69
70    fn recall(&self, keys: &[&Key]) -> Result<Vec<Option<Kept>>, KeeperError> {
71        let names: Vec<String> = keys.iter().map(|key| name_of(key)).collect();
72        let asked: Vec<&str> = names.iter().map(String::as_str).collect();
73        let bound = self.store.resolve_many(&asked).map_err(failed)?;
74
75        // Two round trips and not two per key: the names first, then the bytes
76        // of every one that answered. It is the whole reason both questions are
77        // batched in the trait.
78        let wanted: Vec<&Digest> = bound.iter().flatten().map(|bound| &bound.digest).collect();
79        let mut bytes = self.store.get_many(&wanted).map_err(failed)?.into_iter();
80
81        bound
82            .into_iter()
83            .map(|bound| {
84                let Some(bound) = bound else { return Ok(None) };
85                // A name that answers and bytes that are gone is a miss, not a
86                // failure: a store can be swept, and what it says it has is a
87                // record, not a promise.
88                let Some(Some(bytes)) = bytes.next() else {
89                    return Ok(None);
90                };
91                Ok(Some(Kept {
92                    value: value_of(&bytes)?,
93                    meta: bound.meta,
94                }))
95            })
96            .collect()
97    }
98
99    fn keep(&self, key: &Key, value: &Value, meta: &[(&str, &str)]) -> Result<(), KeeperError> {
100        let bytes = bytes_of(value)?;
101        let digest = self.store.put(&bytes).map_err(failed)?;
102        let meta: Meta = meta
103            .iter()
104            .map(|(what, said)| (what.to_string(), said.to_string()))
105            .collect();
106        self.store
107            .bind(&name_of(key), &digest, meta)
108            .map_err(failed)
109    }
110}
111
112/// A digest, read as the name of what a recipe produces.
113fn key(digest: Digest) -> Key {
114    Key::new(digest.to_string())
115}
116
117/// Where a value is bound, which is not where an artifact is.
118///
119/// Public: a key is what a recipe is called and this is where its value lives,
120/// and every reader carrying its own `format!` of the difference is two places
121/// saying one thing.
122pub fn name_of(key: &Key) -> String {
123    format!("value:{key}")
124}
125
126/// A value in bytes, refusing what only exists in this process.
127pub fn bytes_of(value: &Value) -> Result<Vec<u8>, KeeperError> {
128    if !value.travels() {
129        return Err(KeeperError::new(
130            "an opaque value cannot be kept: what it carries only exists in this process, \
131             and a store outlives it. Whoever knows how to turn it into bytes has to do so \
132             before it gets here",
133        ));
134    }
135    rmp_serde::to_vec(value)
136        .map_err(|e| KeeperError::new(format!("that value could not be written down: {e}")))
137}
138
139/// And back. **Nothing may be left over**: leftovers are as suspicious as
140/// missing bytes, and no format checks that for you.
141pub fn value_of(bytes: &[u8]) -> Result<Value, KeeperError> {
142    let mut rest = bytes;
143    let value: Value = rmp_serde::from_read(&mut rest)
144        .map_err(|e| KeeperError::new(format!("what is kept there cannot be read: {e}")))?;
145    match rest.len() {
146        0 => Ok(value),
147        left => Err(KeeperError::new(format!(
148            "what is kept there has {left} bytes too many at the end"
149        ))),
150    }
151}
152
153fn failed(e: StoreError) -> KeeperError {
154    KeeperError::new(e.to_string())
155}