haematite_cli/serve/storage/
dead.rs1use std::num::NonZeroUsize;
9
10use haematite::branch::Timestamp;
11use haematite::branch::history::{Key, Row};
12use haematite::db::ReadOnlyDatabase;
13use haematite::tree::Hash;
14use serde::{Deserialize, Serialize};
15
16use super::{BranchRefRecord, Failure, SERVICE, Storage, branch_name};
17use crate::serve::protocol::Version;
18
19const DEAD_PREFIX: &[u8] = b"dead-generation\0";
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24#[serde(deny_unknown_fields)]
25pub struct Dead {
26 pub branch: String,
27 pub last: Version,
28 pub discarded_at: Timestamp,
29}
30
31pub(super) fn dead_key(branch: &str, generation: u64) -> Vec<u8> {
34 let mut key = name_prefix(branch);
35 key.extend_from_slice(&generation.to_be_bytes());
36 key
37}
38
39fn name_prefix(branch: &str) -> Vec<u8> {
40 let mut key = Vec::with_capacity(DEAD_PREFIX.len() + 32 + 8);
41 key.extend_from_slice(DEAD_PREFIX);
42 key.extend_from_slice(Hash::of(branch.as_bytes()).as_bytes());
43 key
44}
45
46fn past(prefix: &[u8]) -> Vec<u8> {
48 let mut end = prefix.to_vec();
49 if let Some(last) = end.last_mut() {
50 *last = last.saturating_add(1);
51 }
52 end
53}
54
55fn page(
58 reader: &ReadOnlyDatabase,
59 service: &BranchRefRecord,
60 (start, end): (&[u8], &[u8]),
61 limit: NonZeroUsize,
62) -> Result<Vec<(Vec<u8>, Dead)>, Failure> {
63 let mut found = Vec::new();
64 for shard in &service.shards {
65 let read = reader
66 .range_page_at(shard.shard_id, shard.head, start, end, limit)
67 .map_err(Failure::observer)?;
68 for (key, bytes) in read.entries {
69 let dead = serde_json::from_slice(&bytes)
70 .map_err(|error| Failure::new("dead_generation_corrupt", error.to_string()))?;
71 found.push((key, dead));
72 }
73 }
74 found.sort_unstable_by(|(left, _), (right, _)| left.cmp(right));
75 Ok(found)
76}
77
78pub(super) fn of_name(
84 reader: &ReadOnlyDatabase,
85 service: &BranchRefRecord,
86 branch: &str,
87 limit: NonZeroUsize,
88) -> Result<Vec<Dead>, Failure> {
89 let start = name_prefix(branch);
90 let end = past(&start);
91 let mut found = page(reader, service, (&start, &end), limit)?;
92 found.truncate(limit.get());
93 Ok(found.into_iter().map(|(_key, dead)| dead).collect())
94}
95
96pub(super) fn marker(
102 reader: &ReadOnlyDatabase,
103 service: &BranchRefRecord,
104 branch: &str,
105 generation: u64,
106) -> Result<Option<Dead>, Failure> {
107 #[cfg(test)]
108 super::history::MARKER_READS.with(|reads| reads.set(reads.get() + 1));
109 let key = dead_key(branch, generation);
110 let end = past(&key);
111 let found = page(reader, service, (&key, &end), NonZeroUsize::MIN)?;
112 Ok(found
113 .into_iter()
114 .find(|(found, _dead)| *found == key)
115 .map(|(_key, dead)| dead))
116}
117
118pub(super) fn discard_row(branch: &str, at: Version, written: Timestamp) -> Row {
123 Row {
124 key: Key {
125 branch: Hash::of(branch.as_bytes()),
126 generation: at.generation,
127 sequence: at.sequence,
128 },
129 timestamp: written,
130 shards: Vec::new(),
131 }
132}
133
134pub(super) fn ended(
141 reader: &ReadOnlyDatabase,
142 service: &BranchRefRecord,
143 branch: &str,
144 taken: &[Version],
145) -> Result<Vec<Vec<u8>>, Failure> {
146 let mut generations: Vec<u64> = taken.iter().map(|at| at.generation).collect();
147 generations.dedup();
148 let mut ended = Vec::new();
149 for generation in generations {
150 if let Some(dead) = marker(reader, service, branch, generation)?
151 && taken.contains(&dead.last)
152 {
153 ended.push(dead_key(branch, generation));
154 }
155 }
156 Ok(ended)
157}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct DeadPage {
164 pub names: Vec<String>,
166 pub last: Option<Vec<u8>>,
168 pub end: bool,
170}
171
172impl Storage {
173 pub fn dead_page(
180 &self,
181 after: Option<&[u8]>,
182 limit: NonZeroUsize,
183 ) -> Result<DeadPage, Failure> {
184 let service = self
185 .refs
186 .get(SERVICE)
187 .ok_or_else(|| Failure::new("reference_missing", "missing service namespace"))?;
188 let start = after.map_or_else(
190 || DEAD_PREFIX.to_vec(),
191 |key| {
192 let mut start = key.to_vec();
193 start.push(0);
194 start
195 },
196 );
197 let read = NonZeroUsize::new(limit.get().saturating_add(1)).unwrap_or(NonZeroUsize::MAX);
198 let mut found = page(&self.reader, service, (&start, &past(DEAD_PREFIX)), read)?;
199 let end = found.len() <= limit.get();
200 found.truncate(limit.get());
201 let last = found.last().map(|(key, _dead)| key.clone());
202 let mut names: Vec<String> = found
203 .into_iter()
204 .map(|(_key, dead)| dead.branch)
205 .filter(|name| self.refs.get(&branch_name(name)).is_none())
206 .collect();
207 names.dedup();
208 Ok(DeadPage { names, last, end })
209 }
210}