db/
db.rs

1use std::{fs, path::Path};
2
3use seqdb::{Database, PAGE_SIZE, Result};
4
5fn main() -> Result<()> {
6    let _ = fs::remove_dir_all("vecs");
7
8    let database = Database::open(Path::new("vecs"))?;
9
10    // let seqdb_min_len = PAGE_SIZE * 1_000_000;
11    // let min_regions = 20_000;
12
13    // seqdb.set_min_len(seqdb_min_len)?;
14    // seqdb.set_min_regions(min_regions)?;
15
16    let (region1_i, _) = database.create_region_if_needed("region1")?;
17
18    {
19        let layout = database.layout();
20        assert!(layout.start_to_index().len() == 1);
21        assert!(layout.start_to_index().first_key_value() == Some((&0, &0)));
22        assert!(layout.start_to_hole().is_empty());
23
24        let regions = database.regions();
25        assert!(
26            regions
27                .get_region_index_from_id("region1")
28                .is_some_and(|i| i == region1_i)
29        );
30
31        let region = database.get_region(region1_i.into())?;
32        assert!(region.start() == 0);
33        assert!(region.len() == 0);
34        assert!(region.reserved() == PAGE_SIZE);
35    }
36
37    database.write_all_to_region(region1_i.into(), &[0, 1, 2, 3, 4])?;
38
39    {
40        let region = database.get_region(region1_i.into())?;
41        assert!(region.start() == 0);
42        assert!(region.len() == 5);
43        assert!(region.reserved() == PAGE_SIZE);
44
45        assert!(database.mmap()[0..10] == [0, 1, 2, 3, 4, 0, 0, 0, 0, 0]);
46    }
47
48    database.write_all_to_region(region1_i.into(), &[5, 6, 7, 8, 9])?;
49
50    {
51        let region = database.get_region(region1_i.into())?;
52        assert!(region.start() == 0);
53        assert!(region.len() == 10);
54        assert!(region.reserved() == PAGE_SIZE);
55
56        assert!(database.mmap()[0..10] == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
57    }
58
59    database.write_all_to_region_at(region1_i.into(), &[1, 2], 0)?;
60
61    {
62        let region = database.get_region(region1_i.into())?;
63        assert!(region.start() == 0);
64        assert!(region.len() == 10);
65        assert!(region.reserved() == PAGE_SIZE);
66
67        assert!(database.mmap()[0..10] == [1, 2, 2, 3, 4, 5, 6, 7, 8, 9]);
68    }
69
70    database.write_all_to_region_at(region1_i.into(), &[10, 11, 12, 13, 14, 15, 16, 17, 18], 4)?;
71
72    {
73        let region = database.get_region(region1_i.into())?;
74        assert!(region.start() == 0);
75        assert!(region.len() == 13);
76        assert!(region.reserved() == PAGE_SIZE);
77
78        assert!(
79            database.mmap()[0..20]
80                == [
81                    1, 2, 2, 3, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0
82                ]
83        );
84    }
85
86    database.write_all_to_region_at(region1_i.into(), &[0, 0, 0, 0, 0, 1], 13)?;
87
88    {
89        let region = database.get_region(region1_i.into())?;
90        assert!(region.start() == 0);
91        assert!(region.len() == 19);
92        assert!(region.reserved() == PAGE_SIZE);
93
94        assert!(
95            database.mmap()[0..20]
96                == [
97                    1, 2, 2, 3, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0, 0, 0, 0, 0, 1, 0
98                ]
99        );
100    }
101
102    dbg!(1);
103
104    database.write_all_to_region_at(region1_i.into(), &[1; 8000], 0)?;
105
106    {
107        let region = database.get_region(region1_i.into())?;
108        assert!(region.start() == 0);
109        assert!(region.len() == 8000);
110        assert!(region.reserved() == PAGE_SIZE * 2);
111
112        assert!(database.mmap()[0..8000] == [1; 8000]);
113        assert!(database.mmap()[8000..8001] == [0]);
114    }
115
116    println!("Disk usage - pre sync: {}", database.disk_usage());
117    database.flush()?;
118    println!("Disk usage - post sync: {}", database.disk_usage());
119
120    database.truncate_region(region1_i.into(), 10)?;
121    database.punch_holes()?;
122
123    {
124        let region = database.get_region(region1_i.into())?;
125        assert!(region.start() == 0);
126        assert!(region.len() == 10);
127        assert!(region.reserved() == PAGE_SIZE * 2);
128        // We only punch a hole in whole pages (4096 bytes)
129        // Thus the last byte of the page where the is still data wasn't overwritten when truncating
130        // And the first byte of the punched page was set to 0
131        assert!(database.mmap()[4095..=4096] == [1, 0]);
132    }
133
134    database.flush()?;
135    println!("Disk usage - post trunc: {}", database.disk_usage());
136
137    database.remove_region(region1_i.into())?;
138
139    database.flush()?;
140
141    println!("Disk usage - post remove: {}", database.disk_usage());
142
143    {
144        let regions = database.regions();
145        let index_to_region = regions.index_to_region();
146        assert!(index_to_region.len() == 1);
147        assert!(index_to_region[0].is_none());
148        assert!(regions.id_to_index().is_empty());
149
150        let layout = database.layout();
151        assert!(layout.start_to_index().is_empty());
152        assert!(layout.start_to_hole().len() == 1);
153    }
154
155    let (region1_i, _) = database.create_region_if_needed("region1")?;
156    let (region2_i, _) = database.create_region_if_needed("region2")?;
157    let (region3_i, _) = database.create_region_if_needed("region3")?;
158
159    // dbg!(seqdb.layout());
160
161    {
162        let regions = database.regions();
163        let index_to_region = regions.index_to_region();
164        assert!(index_to_region.len() == 3);
165        let region1 = database.get_region(region1_i.into())?;
166        assert!(region1.start() == 0);
167        assert!(region1.len() == 0);
168        assert!(region1.reserved() == PAGE_SIZE);
169        let region2 = database.get_region(region2_i.into())?;
170        assert!(region2.start() == PAGE_SIZE);
171        assert!(region2.len() == 0);
172        assert!(region2.reserved() == PAGE_SIZE);
173        let region3 = database.get_region(region3_i.into())?;
174        assert!(region3.start() == PAGE_SIZE * 2);
175        assert!(region3.len() == 0);
176        assert!(region3.reserved() == PAGE_SIZE);
177        let id_to_index = regions.id_to_index();
178        assert!(id_to_index.len() == 3);
179        assert!(id_to_index.get("region1") == Some(&0));
180        assert!(id_to_index.get("region2") == Some(&1));
181        assert!(id_to_index.get("region3") == Some(&2));
182
183        let layout = database.layout();
184        let start_to_index = layout.start_to_index();
185        assert!(start_to_index.len() == 3);
186        assert!(start_to_index.get(&0) == Some(&0));
187        assert!(start_to_index.get(&PAGE_SIZE) == Some(&1));
188        assert!(start_to_index.get(&(PAGE_SIZE * 2)) == Some(&2));
189        assert!(layout.start_to_hole().is_empty());
190    }
191
192    database.remove_region(region2_i.into())?;
193
194    {
195        let regions = database.regions();
196        let index_to_region = regions.index_to_region();
197        assert!(index_to_region.len() == 3);
198        let region1 = database.get_region(region1_i.into())?;
199        assert!(region1.start() == 0);
200        assert!(region1.len() == 0);
201        assert!(region1.reserved() == PAGE_SIZE);
202        assert!(database.get_region(region2_i.into()).is_err());
203        assert!(
204            index_to_region
205                .get(region2_i)
206                .is_some_and(|opt| opt.is_none())
207        );
208        let region3 = database.get_region(region3_i.into())?;
209        assert!(region3.start() == PAGE_SIZE * 2);
210        assert!(region3.len() == 0);
211        assert!(region3.reserved() == PAGE_SIZE);
212        let id_to_index = regions.id_to_index();
213        assert!(id_to_index.len() == 2);
214        assert!(id_to_index.get("region1") == Some(&0));
215        assert!(id_to_index.get("region2").is_none());
216        assert!(id_to_index.get("region3") == Some(&2));
217
218        let layout = database.layout();
219        let start_to_index = layout.start_to_index();
220        assert!(start_to_index.len() == 2);
221        assert!(start_to_index.get(&0) == Some(&region1_i));
222        assert!(start_to_index.get(&(PAGE_SIZE * 2)) == Some(&region3_i));
223        let start_to_hole = layout.start_to_hole();
224        assert!(start_to_hole.len() == 1);
225        assert!(start_to_hole.get(&PAGE_SIZE) == Some(&PAGE_SIZE));
226
227        drop(regions);
228        drop(layout);
229        assert!(
230            database
231                .remove_region(region2_i.into())
232                .is_ok_and(|o| o.is_none())
233        );
234    }
235
236    let (region2_i, _) = database.create_region_if_needed("region2")?;
237
238    {
239        assert!(region2_i == 1)
240    }
241
242    database.remove_region(region2_i.into())?;
243
244    {
245        let regions = database.regions();
246        let index_to_region = regions.index_to_region();
247        assert!(index_to_region.len() == 3);
248        let region1 = database.get_region(region1_i.into())?;
249        assert!(region1.start() == 0);
250        assert!(region1.len() == 0);
251        assert!(region1.reserved() == PAGE_SIZE);
252        assert!(database.get_region(region2_i.into()).is_err());
253        assert!(
254            index_to_region
255                .get(region2_i)
256                .is_some_and(|opt| opt.is_none())
257        );
258        let region3 = database.get_region(region3_i.into())?;
259        assert!(region3.start() == PAGE_SIZE * 2);
260        assert!(region3.len() == 0);
261        assert!(region3.reserved() == PAGE_SIZE);
262        let id_to_index = regions.id_to_index();
263        assert!(id_to_index.len() == 2);
264        assert!(id_to_index.get("region1") == Some(&0));
265        assert!(id_to_index.get("region2").is_none());
266        assert!(id_to_index.get("region3") == Some(&2));
267
268        let layout = database.layout();
269        let start_to_index = layout.start_to_index();
270        assert!(start_to_index.len() == 2);
271        assert!(start_to_index.get(&0) == Some(&region1_i));
272        assert!(start_to_index.get(&(PAGE_SIZE * 2)) == Some(&region3_i));
273        let start_to_hole = layout.start_to_hole();
274        assert!(start_to_hole.len() == 1);
275        assert!(start_to_hole.get(&PAGE_SIZE) == Some(&PAGE_SIZE));
276
277        drop(regions);
278        drop(layout);
279        assert!(
280            database
281                .remove_region(region2_i.into())
282                .is_ok_and(|o| o.is_none())
283        );
284    }
285
286    database.write_all_to_region_at(region1_i.into(), &[1; 8000], 0)?;
287
288    {
289        let regions = database.regions();
290        let index_to_region = regions.index_to_region();
291        assert!(index_to_region.len() == 3);
292        let region1 = database.get_region(region1_i.into())?;
293        assert!(region1.start() == 0);
294        assert!(region1.len() == 8000);
295        assert!(region1.reserved() == 2 * PAGE_SIZE);
296        assert!(database.get_region(region2_i.into()).is_err());
297        assert!(
298            index_to_region
299                .get(region2_i)
300                .is_some_and(|opt| opt.is_none())
301        );
302        let region3 = database.get_region(region3_i.into())?;
303        assert!(region3.start() == PAGE_SIZE * 2);
304        assert!(region3.len() == 0);
305        assert!(region3.reserved() == PAGE_SIZE);
306        let id_to_index = regions.id_to_index();
307        assert!(id_to_index.len() == 2);
308        assert!(id_to_index.get("region1") == Some(&0));
309        assert!(id_to_index.get("region2").is_none());
310        assert!(id_to_index.get("region3") == Some(&2));
311
312        let layout = database.layout();
313        let start_to_index = layout.start_to_index();
314        assert!(start_to_index.len() == 2);
315        assert!(start_to_index.get(&0) == Some(&region1_i));
316        assert!(start_to_index.get(&(PAGE_SIZE * 2)) == Some(&region3_i));
317        let start_to_hole = layout.start_to_hole();
318        assert!(start_to_hole.is_empty());
319    }
320
321    let (region2_i, _) = database.create_region_if_needed("region2")?;
322
323    {
324        let regions = database.regions();
325        let index_to_region = regions.index_to_region();
326        assert!(index_to_region.len() == 3);
327        let region1 = database.get_region(region1_i.into())?;
328        assert!(region1.start() == 0);
329        assert!(region1.len() == 8000);
330        assert!(region1.reserved() == 2 * PAGE_SIZE);
331        let region2 = database.get_region(region2_i.into())?;
332        assert!(region2.start() == PAGE_SIZE * 3);
333        assert!(region2.len() == 0);
334        assert!(region2.reserved() == PAGE_SIZE);
335        let region3 = database.get_region(region3_i.into())?;
336        assert!(region3.start() == PAGE_SIZE * 2);
337        assert!(region3.len() == 0);
338        assert!(region3.reserved() == PAGE_SIZE);
339        let id_to_index = regions.id_to_index();
340        assert!(id_to_index.len() == 3);
341        assert!(id_to_index.get("region1") == Some(&0));
342        assert!(id_to_index.get("region2") == Some(&1));
343        assert!(id_to_index.get("region3") == Some(&2));
344
345        let layout = database.layout();
346        let start_to_index = layout.start_to_index();
347        assert!(start_to_index.len() == 3);
348        assert!(start_to_index.get(&0) == Some(&region1_i));
349        assert!(start_to_index.get(&(PAGE_SIZE * 2)) == Some(&region3_i));
350        assert!(start_to_index.get(&(PAGE_SIZE * 3)) == Some(&region2_i));
351        let start_to_hole = layout.start_to_hole();
352        assert!(start_to_hole.is_empty());
353    }
354
355    database.remove_region(region3_i.into())?;
356
357    {
358        let regions = database.regions();
359        let index_to_region = regions.index_to_region();
360        assert!(index_to_region.len() == 3);
361        let region1 = database.get_region(region1_i.into())?;
362        assert!(region1.start() == 0);
363        assert!(region1.len() == 8000);
364        assert!(region1.reserved() == 2 * PAGE_SIZE);
365        let region2 = database.get_region(region2_i.into())?;
366        assert!(region2.start() == PAGE_SIZE * 3);
367        assert!(region2.len() == 0);
368        assert!(region2.reserved() == PAGE_SIZE);
369        assert!(database.get_region(region3_i.into()).is_err());
370        let id_to_index = regions.id_to_index();
371        assert!(id_to_index.len() == 2);
372        assert!(id_to_index.get("region1") == Some(&0));
373        assert!(id_to_index.get("region2") == Some(&1));
374        assert!(id_to_index.get("region3").is_none());
375
376        let layout = database.layout();
377        let start_to_index = layout.start_to_index();
378        assert!(start_to_index.len() == 2);
379        assert!(start_to_index.get(&0) == Some(&region1_i));
380        assert!(start_to_index.get(&(PAGE_SIZE * 3)) == Some(&region2_i));
381        let start_to_hole = layout.start_to_hole();
382        assert!(start_to_hole.get(&(PAGE_SIZE * 2)) == Some(&PAGE_SIZE));
383    }
384
385    database.write_all_to_region(region1_i.into(), &[1; 8000])?;
386
387    {
388        let regions = database.regions();
389        let index_to_region = regions.index_to_region();
390        assert!(index_to_region.len() == 3);
391        let region1 = database.get_region(region1_i.into())?;
392        assert!(region1.start() == PAGE_SIZE * 4);
393        assert!(region1.len() == 16_000);
394        assert!(region1.reserved() == 4 * PAGE_SIZE);
395        let region2 = database.get_region(region2_i.into())?;
396        assert!(region2.start() == PAGE_SIZE * 3);
397        assert!(region2.len() == 0);
398        assert!(region2.reserved() == PAGE_SIZE);
399        assert!(database.get_region(region3_i.into()).is_err());
400        let id_to_index = regions.id_to_index();
401        assert!(id_to_index.len() == 2);
402        assert!(id_to_index.get("region1") == Some(&0));
403        assert!(id_to_index.get("region2") == Some(&1));
404        assert!(id_to_index.get("region3").is_none());
405
406        let layout = database.layout();
407        let start_to_index = layout.start_to_index();
408        assert!(start_to_index.len() == 2);
409        assert!(start_to_index.get(&(PAGE_SIZE * 4)) == Some(&region1_i));
410        assert!(start_to_index.get(&(PAGE_SIZE * 3)) == Some(&region2_i));
411        let start_to_hole = layout.start_to_hole();
412        assert!(start_to_hole.get(&0) == Some(&(PAGE_SIZE * 3)));
413    }
414
415    database.write_all_to_region(region2_i.into(), &[1; 6000])?;
416
417    let (region4_i, _) = database.create_region_if_needed("region4")?;
418    database.remove_region(region2_i.into())?;
419    database.remove_region(region4_i.into())?;
420
421    let regions = database.regions();
422    dbg!(&regions);
423    let layout = database.layout();
424    dbg!(&layout);
425
426    Ok(())
427}