nodedb_lite/engine/array/
manifest.rs1use std::sync::Arc;
8
9use nodedb_types::Namespace;
10use serde::{Deserialize, Serialize};
11
12use crate::error::LiteError;
13use crate::storage::engine::{StorageEngine, WriteOp};
14
15const MANIFEST_PREFIX: &str = "manifest:";
16
17#[derive(
19 Debug,
20 Clone,
21 PartialEq,
22 Eq,
23 Serialize,
24 Deserialize,
25 zerompk::ToMessagePack,
26 zerompk::FromMessagePack,
27)]
28pub struct SegmentRef {
29 pub id: u64,
31 pub byte_len: u64,
33}
34
35#[derive(
37 Debug, Clone, Default, Serialize, Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
38)]
39pub struct ArrayManifest {
40 pub segments: Vec<SegmentRef>,
41 pub next_id: u64,
43}
44
45impl ArrayManifest {
46 pub fn new() -> Self {
47 Self::default()
48 }
49
50 pub fn push_segment(&mut self, byte_len: u64) -> u64 {
52 let id = self.next_id;
53 self.next_id += 1;
54 self.segments.push(SegmentRef { id, byte_len });
55 id
56 }
57
58 pub fn drop_segments(&mut self, drop_ids: &[u64]) {
60 let set: std::collections::HashSet<u64> = drop_ids.iter().copied().collect();
61 self.segments.retain(|s| !set.contains(&s.id));
62 }
63}
64
65fn manifest_key(name: &str) -> Vec<u8> {
66 let mut k = MANIFEST_PREFIX.as_bytes().to_vec();
67 k.extend_from_slice(name.as_bytes());
68 k
69}
70
71pub fn segment_key(name: &str, id: u64) -> Vec<u8> {
72 format!("segment:{name}:{id}").into_bytes()
73}
74
75pub async fn save_manifest<S: StorageEngine>(
77 storage: &Arc<S>,
78 name: &str,
79 manifest: &ArrayManifest,
80) -> Result<(), LiteError> {
81 let bytes = zerompk::to_msgpack_vec(manifest).map_err(|e| LiteError::Serialization {
82 detail: format!("encode ArrayManifest: {e}"),
83 })?;
84 storage
85 .put(Namespace::Array, &manifest_key(name), &bytes)
86 .await
87}
88
89pub async fn load_manifest<S: StorageEngine>(
92 storage: &Arc<S>,
93 name: &str,
94) -> Result<ArrayManifest, LiteError> {
95 match storage.get(Namespace::Array, &manifest_key(name)).await? {
96 Some(bytes) => zerompk::from_msgpack(&bytes).map_err(|e| LiteError::Serialization {
97 detail: format!("decode ArrayManifest: {e}"),
98 }),
99 None => Ok(ArrayManifest::new()),
100 }
101}
102
103pub async fn drop_manifest<S: StorageEngine>(
110 storage: &Arc<S>,
111 name: &str,
112 manifest: &ArrayManifest,
113 extra_ops: Vec<WriteOp>,
114) -> Result<(), LiteError> {
115 #[cfg(not(target_arch = "wasm32"))]
116 if let Some(ext) = storage.as_array_segment_ext() {
117 for s in &manifest.segments {
118 ext.delete_array_segment(name, s.id).await?;
119 }
120 }
121 let mut ops: Vec<WriteOp> = manifest
122 .segments
123 .iter()
124 .map(|s| WriteOp::Delete {
125 ns: Namespace::Array,
126 key: segment_key(name, s.id),
127 })
128 .collect();
129 ops.push(WriteOp::Delete {
130 ns: Namespace::Array,
131 key: manifest_key(name),
132 });
133 ops.extend(extra_ops);
134 storage.batch_write(&ops).await
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use crate::storage::pagedb_storage::PagedbStorageMem;
141 use std::sync::Arc;
142
143 use crate::storage::engine::StorageEngine;
144
145 #[tokio::test]
146 async fn manifest_push_and_persist() {
147 let storage = Arc::new(PagedbStorageMem::open_in_memory().await.unwrap());
148 let mut m = ArrayManifest::new();
149 m.push_segment(128);
150 m.push_segment(256);
151 save_manifest(&storage, "a", &m).await.unwrap();
152
153 let m2 = load_manifest(&storage, "a").await.unwrap();
154 assert_eq!(m2.segments.len(), 2);
155 assert_eq!(m2.segments[0].id, 0);
156 assert_eq!(m2.segments[1].id, 1);
157 assert_eq!(m2.next_id, 2);
158 }
159
160 #[tokio::test]
161 async fn load_missing_returns_empty() {
162 let storage = Arc::new(PagedbStorageMem::open_in_memory().await.unwrap());
163 let m = load_manifest(&storage, "no_such").await.unwrap();
164 assert!(m.segments.is_empty());
165 assert_eq!(m.next_id, 0);
166 }
167
168 #[tokio::test]
169 async fn drop_removes_manifest_and_segments() {
170 let storage = Arc::new(PagedbStorageMem::open_in_memory().await.unwrap());
171 let mut m = ArrayManifest::new();
172 let id = m.push_segment(64);
173 let seg_bytes = b"fake_segment_bytes";
174 storage
175 .put(Namespace::Array, &segment_key("b", id), seg_bytes)
176 .await
177 .unwrap();
178 save_manifest(&storage, "b", &m).await.unwrap();
179
180 drop_manifest(&storage, "b", &m, Vec::new()).await.unwrap();
181
182 assert!(
183 load_manifest(&storage, "b")
184 .await
185 .unwrap()
186 .segments
187 .is_empty()
188 );
189 assert!(
190 storage
191 .get(Namespace::Array, &segment_key("b", id))
192 .await
193 .unwrap()
194 .is_none()
195 );
196 }
197
198 #[test]
199 fn drop_segments_filter() {
200 let mut m = ArrayManifest::new();
201 m.push_segment(10);
202 m.push_segment(20);
203 m.push_segment(30);
204 m.drop_segments(&[0, 2]);
205 assert_eq!(m.segments.len(), 1);
206 assert_eq!(m.segments[0].id, 1);
207 }
208}