1use slate_kv_core::config::{SchedCfg, OP_DEL, OP_PUT};
2use slate_kv_core::epoch::{EngineState, MountError, SecurityMode};
3use slate_kv_core::gc::SegTable;
4use slate_kv_core::index::Index;
5use slate_kv_core::log::{HeadState, Log};
6use slate_kv_core::metrics::Metrics;
7use slate_kv_core::sched::Scheduler;
8use slate_kv_core::slate::Slate;
9use slate_kv_crypto::sealer::CryptoSealer;
10use std::fs::OpenOptions;
11use std::path::{Path, PathBuf};
12use std::sync::Mutex;
13
14use crate::file_counter::FileCounter;
15use crate::file_flash::FileFlash;
16
17#[derive(Debug)]
18pub enum DbError {
19 Core(slate_kv_core::error::Error),
20 Mount(slate_kv_core::epoch::MountError),
21 Io(std::io::Error),
22 Config(String),
23 InvalidArg(String),
24}
25
26impl std::fmt::Display for DbError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 DbError::Core(e) => write!(f, "engine error: {e:?}"),
33 DbError::Mount(e) => write!(f, "mount failed: {e:?}"),
34 DbError::Io(e) => write!(f, "i/o error: {e}"),
35 DbError::Config(m) => write!(f, "configuration error: {m}"),
36 DbError::InvalidArg(m) => write!(f, "invalid argument: {m}"),
37 }
38 }
39}
40
41impl std::error::Error for DbError {
42 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
43 match self {
46 DbError::Io(e) => Some(e),
47 _ => None,
48 }
49 }
50}
51
52impl From<slate_kv_core::error::Error> for DbError {
53 fn from(e: slate_kv_core::error::Error) -> Self {
54 DbError::Core(e)
55 }
56}
57impl From<slate_kv_core::epoch::MountError> for DbError {
58 fn from(e: slate_kv_core::epoch::MountError) -> Self {
59 DbError::Mount(e)
60 }
61}
62impl From<std::io::Error> for DbError {
63 fn from(e: std::io::Error) -> Self {
64 DbError::Io(e)
65 }
66}
67impl From<String> for DbError {
68 fn from(e: String) -> Self {
69 DbError::Config(e)
70 }
71}
72impl From<&str> for DbError {
73 fn from(e: &str) -> Self {
74 DbError::Config(e.to_string())
75 }
76}
77
78pub enum KeySource {
79 Bytes([u8; 32]),
80 File(PathBuf),
81 Env(&'static str),
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85pub enum Profile {
86 Esp32,
87 Pi,
88}
89
90#[derive(Clone, Debug)]
96pub struct Options {
97 pub capacity: u32,
98 pub b_commit: u32,
99 pub auto_b: bool,
100 pub staleness_budget_ms: u32,
101 pub n_keys: usize,
102 pub profile: Profile,
103 pub durability: crate::file_flash::Durability,
104}
105
106impl Default for Options {
107 fn default() -> Self {
108 Self {
109 capacity: 4 * 1024 * 1024,
110 b_commit: 8,
111 auto_b: true,
112 staleness_budget_ms: 1000,
113 n_keys: 8192,
114 profile: Profile::Pi,
115 durability: crate::file_flash::Durability::Full,
116 }
117 }
118}
119
120pub struct ScrubReport {
121 pub errors_found: u32,
122 pub errors_fixed: u32,
123}
124
125#[derive(Debug, Default, Clone, Copy)]
134pub struct MountReport {
135 pub had_checkpoint: bool,
137 pub replay_from: u32,
139 pub head_pos: u32,
141 pub scan_bytes: u32,
143 pub records_replayed: u64,
145 pub ckpt_index_bytes: usize,
147 pub keys: usize,
149 pub flash: crate::file_flash::FlashCounters,
151 pub index_slots: usize,
153 pub ckpt_slots_verified: u8,
155 pub flash_after_ckpt: crate::file_flash::FlashCounters,
161 pub key_verify_calls: u64,
169}
170
171#[derive(Default, Clone, Debug)]
173pub struct Stats {
174 pub commits: u64,
175 pub wakes: u64,
176 pub user_bytes: u64,
178 pub gc_bytes: u64,
180 pub parity_bytes: u64,
182 pub marker_bytes: u64,
184 pub ckpt_bytes: u64,
186 pub erases: u64,
187 pub segments: u32,
189 pub segments_sealed: u32,
191 pub segments_free: u32,
193 pub ckpt_seg_seq: u64,
196 pub cur_seg_seq: u64,
198 pub gc_scanned: u64,
200 pub gc_relocated: u64,
202 pub gc_open_failed: u64,
204 pub gc_segments_freed: u64,
206 pub hot_head: u32,
208 pub cold_head: u32,
210 pub seg_end: u32,
212}
213
214impl Stats {
215 pub fn flash_bytes(&self) -> u64 {
217 self.user_bytes + self.gc_bytes + self.parity_bytes + self.marker_bytes + self.ckpt_bytes
218 }
219
220 pub fn write_amplification(&self) -> Option<f64> {
226 if self.user_bytes == 0 {
227 None
228 } else {
229 Some(self.flash_bytes() as f64 / self.user_bytes as f64)
230 }
231 }
232}
233
234struct Buffers {
236 hot: *mut [u8],
237 cold: *mut [u8],
238 index: *mut [u32],
239 ckpt: *mut [u8],
240}
241
242unsafe impl Send for Buffers {}
244unsafe impl Sync for Buffers {}
245
246impl Drop for Buffers {
247 fn drop(&mut self) {
248 unsafe {
250 let _ = Box::from_raw(self.hot);
251 let _ = Box::from_raw(self.cold);
252 let _ = Box::from_raw(self.index);
253 let _ = Box::from_raw(self.ckpt);
254 }
255 }
256}
257
258struct OwnedEngine {
259 slate: slate_kv_core::slate::Slate<
260 'static,
261 slate_kv_hal::BlockingFlash<FileFlash>,
262 slate_kv_hal::BlockingCounter<FileCounter>,
263 CryptoSealer,
264 >,
265 #[allow(dead_code)]
266 bufs: Buffers, }
268
269unsafe impl Send for OwnedEngine {}
271
272pub struct Db {
273 inner: Mutex<OwnedEngine>,
274 mount: MountReport,
275}
276
277impl Drop for Db {
278 fn drop(&mut self) {
287 if let Ok(mut inner) = self.inner.lock() {
288 let _ = inner.slate.commit();
289 }
290 }
291}
292
293impl Db {
294 #[allow(clippy::collapsible_if)]
295 pub fn open(path: &Path, key: KeySource, opts: Options) -> Result<Self, DbError> {
296 let root_key = match key {
297 KeySource::Bytes(k) => k,
298 KeySource::File(p) => {
299 let mut k = [0u8; 32];
300 let b = std::fs::read(&p)?;
301 if b.len() < 32 {
302 return Err(DbError::Config("Key file too short".into()));
303 }
304 k.copy_from_slice(&b[0..32]);
305 k
306 }
307 KeySource::Env(var) => {
308 let mut k = [0u8; 32];
309 let val = std::env::var(var).map_err(|e| DbError::Config(e.to_string()))?;
310 let b = val.as_bytes();
311 if b.len() < 32 {
312 return Err(DbError::Config("Env key too short".into()));
313 }
314 k.copy_from_slice(&b[0..32]);
315 k
316 }
317 };
318
319 use slate_kv_core::log::Sealer;
320 std::fs::create_dir_all(path)?;
321
322 let flash_path = path.join("data.bin");
323 let counter_path = path.join("counter.bin");
324
325 let flash_file = OpenOptions::new()
326 .read(true)
327 .write(true)
328 .create(true)
329 .truncate(false)
330 .open(flash_path)?;
331 let counter_file = OpenOptions::new()
332 .read(true)
333 .write(true)
334 .create(true)
335 .truncate(false)
336 .open(counter_path)?;
337
338 let mut flash = FileFlash::new(flash_file, opts.capacity, 256, 4096, opts.durability)
339 .map_err(|e| DbError::Config(e.to_string()))?;
340 let device_key = slate_kv_crypto::keys::DeviceKey(root_key);
341 let keyset = slate_kv_crypto::keys::KeySet::derive(&device_key, 1);
342 let k_ctr = keyset.k_ctr;
343
344 let mut counter = FileCounter::new(counter_file, k_ctr, u64::MAX).map_err(|e| match e {
345 crate::file_counter::FileCounterError::FormatError => {
349 DbError::Mount(MountError::Tampered)
350 }
351 crate::file_counter::FileCounterError::Io(err) => DbError::Io(err),
352 crate::file_counter::FileCounterError::Exhausted => {
353 DbError::Config("monotonic counter budget exhausted".to_string())
354 }
355 })?;
356
357 let mut sealer = CryptoSealer::new(keyset);
358
359 let hot_box = vec![0u8; 65536].into_boxed_slice();
361 let cold_box = vec![0u8; 65536].into_boxed_slice();
362 let n_buckets = (opts.n_keys.max(2048) as f64 / 0.95) as usize;
368 let index_slots_count = n_buckets.next_power_of_two() * slate_kv_core::config::BUCKET_SLOTS;
369 let max_slots = slate_kv_core::config::max_index_slots();
370 if index_slots_count > max_slots {
371 return Err(DbError::Config(format!(
372 "n_keys={} needs {} index slots but the checkpoint format allows at most {} \
373 (MAX_CKPT_LEN={}); reduce n_keys to at most {}",
374 opts.n_keys,
375 index_slots_count,
376 max_slots,
377 slate_kv_core::config::MAX_CKPT_LEN,
378 max_slots / slate_kv_core::config::BUCKET_SLOTS * 95 / 100,
379 )));
380 }
381 let index_box = vec![0u32; index_slots_count].into_boxed_slice();
382 let index_len = index_box.len();
383
384 let ckpt_box =
389 vec![0u8; slate_kv_core::config::ckpt_len_for_slots(index_len)].into_boxed_slice();
390 let ckpt_ptr = Box::into_raw(ckpt_box);
391 let ckpt_slice = unsafe { &mut *ckpt_ptr };
392
393 let data_base = slate_kv_core::config::data_base_offset(4096);
398 let mut had_checkpoint = true;
399 let mut ckpt_slots_verified = 0u8;
400 let (engine_state, plain_len, replay_from, ckpt_seg_seq) =
401 match slate_kv_core::epoch::mount(&mut flash, &mut counter, &mut sealer, ckpt_slice) {
402 Ok(mi) => {
403 ckpt_slots_verified = mi.ckpt_slots_verified;
404 (
405 mi.state,
406 mi.plain_len,
407 mi.ckpt_write_offset.max(data_base),
408 mi.ckpt_seg_seq,
409 )
410 }
411 Err(MountError::FormatError) => {
412 had_checkpoint = false;
413 let mut st = EngineState {
415 epoch: 1,
416 next_seq: 1,
417 acked_seq: 0,
418 d_ckpt: [0u8; 32],
419 chain: slate_kv_core::chain::Chain::anchor(1, &[0u8; 32]),
420 records_in_epoch: 0,
421 security_mode: SecurityMode::BestEffortRollback,
422 active_ckpt_slot: 0,
423 };
424 let mut page_buf = [0u8; 512];
429 slate_kv_core::epoch::seal_epoch(
430 &mut st,
431 &mut flash,
432 &mut counter,
433 &mut sealer,
434 1,
435 data_base,
436 0,
437 ckpt_slice,
438 0,
439 &mut page_buf,
440 )?;
441 (st, 0, data_base, 1)
442 }
443 Err(e) => return Err(e.into()),
444 };
445
446 let flash_after_ckpt = flash.counters();
447 sealer.roll_epoch(engine_state.epoch);
448
449 let hot_ptr = Box::into_raw(hot_box);
450 let cold_ptr = Box::into_raw(cold_box);
451 let index_ptr = Box::into_raw(index_box);
452
453 let bufs = Buffers {
454 hot: hot_ptr,
455 cold: cold_ptr,
456 index: index_ptr,
457 ckpt: ckpt_ptr,
458 };
459
460 let hot_slice = unsafe { &mut *hot_ptr };
462 let cold_slice = unsafe { &mut *cold_ptr };
463 let index_slice = unsafe { &mut *index_ptr };
464
465 let data_base = slate_kv_core::config::data_base_offset(4096);
470 let log_hot = Log::new(
471 hot_slice,
472 HeadState {
473 seg_seq: 1,
474 write_offset: data_base,
475 block_idx: 0,
476 },
477 );
478 let log_cold = Log::new(
479 cold_slice,
480 HeadState {
481 seg_seq: 1,
482 write_offset: data_base,
483 block_idx: 0,
484 },
485 );
486
487 let sched_cfg = SchedCfg {
488 auto_b: opts.auto_b,
489 fixed_cost_uj: match opts.profile {
490 Profile::Esp32 => 400,
491 Profile::Pi => 150,
492 },
493 staleness_budget_ms: opts.staleness_budget_ms,
494 deadline_ms: match opts.profile {
495 Profile::Esp32 => 1000,
496 Profile::Pi => 500,
497 },
498 b_min: 1,
499 b_max: 128,
500 b_commit: opts.b_commit,
501 };
502
503 let rng_seed = engine_state.epoch.max(1) ^ 42;
504 let mut slate = Slate {
505 flash: slate_kv_hal::BlockingFlash(flash),
506 counter: slate_kv_hal::BlockingCounter(counter),
507 sealer,
508 engine: engine_state,
509 log_hot,
510 log_cold,
511 index: Index::new(index_slice, index_len / 4),
512 segs: SegTable::with_base(
513 data_base,
514 slate_kv_core::gc::segments_in(data_base, opts.capacity),
515 ),
516 ckpt_seg_seq,
520 sched: Scheduler::new(sched_cfg),
521 metrics: Metrics::default(),
522 ckpt_buf: ckpt_slice,
523 rng: slate_kv_core::index::XorShift64::new(rng_seed),
524 scratch_buf: slate_kv_core::slate::ScratchWorkspace::new(),
525 };
526
527 if plain_len > 0 {
528 slate.index.deserialize(
529 &slate.ckpt_buf[slate_kv_core::checkpoint::CKPT_HDR_LEN
530 ..slate_kv_core::checkpoint::CKPT_HDR_LEN + plain_len],
531 );
532 }
533
534 let mut index_upsert_error = false;
535 let mut key_verify_calls: u64 = 0;
536 let mut rng = slate_kv_core::index::XorShift64::new(42);
537 let mut workspace = Box::new(slate_kv_core::recover::RecoverWorkspace::new());
538 let rec_info = slate_kv_core::recover::recover(
544 &mut slate.flash,
545 &mut slate.sealer,
546 &mut slate.engine.chain,
547 slate.engine.epoch,
548 replay_from,
549 &mut workspace,
550 |flash, sealer, _seq, off, op, key| {
551 if op == slate_kv_core::config::OP_PUT {
555 let res = slate.index.upsert(key, off, &mut rng, |cand_off| {
556 key_verify_calls += 1;
557 slate_kv_core::recover::record_key_eq(flash, sealer, cand_off, key)
558 });
559 if res.is_err() {
560 index_upsert_error = true;
561 }
562 } else if op == slate_kv_core::config::OP_DEL {
563 slate.index.remove(key, |cand_off| {
564 key_verify_calls += 1;
565 slate_kv_core::recover::record_key_eq(flash, sealer, cand_off, key)
566 });
567 }
568 },
569 )
570 .map_err(|e| DbError::Config(format!("{:?}", e)))?;
571
572 slate.log_hot.head.write_offset = rec_info.head_pos.max(replay_from);
576 slate.log_cold.head.write_offset = slate.log_hot.head.write_offset;
577 let mount_next = slate.engine.next_seq;
583 slate.engine.next_seq = (rec_info.committed_upto + 1).max(mount_next);
584 slate.engine.acked_seq = rec_info.committed_upto.max(mount_next.saturating_sub(1));
585
586 if index_upsert_error {
587 return Err("Index capacity exceeded during recovery".into());
588 }
589
590 let mount = MountReport {
591 had_checkpoint,
592 replay_from,
593 head_pos: rec_info.head_pos,
594 scan_bytes: rec_info.scan_bytes,
595 records_replayed: rec_info.records_applied,
596 ckpt_index_bytes: plain_len,
597 keys: slate.index.len(),
598 flash: slate.flash.0.counters(),
599 index_slots: index_len,
600 ckpt_slots_verified,
601 flash_after_ckpt,
602 key_verify_calls,
603 };
604
605 Ok(Db {
606 inner: Mutex::new(OwnedEngine { slate, bufs }),
607 mount,
608 })
609 }
610
611 pub fn mount_report(&self) -> MountReport {
617 self.mount
618 }
619
620 pub fn put(&self, key: &[u8], val: &[u8]) -> Result<(), DbError> {
621 if key.len() > slate_kv_core::config::MAX_KEY_LEN
622 || val.len() > slate_kv_core::config::MAX_VAL_LEN
623 {
624 return Err(DbError::InvalidArg("key or value too large".into()));
625 }
626 let mut inner = self.inner.lock().unwrap();
627 let OwnedEngine { slate, .. } = &mut *inner;
628 let now_ms = std::time::SystemTime::now()
629 .duration_since(std::time::UNIX_EPOCH)
630 .unwrap()
631 .as_millis() as u64;
632
633 let offset = slate.append_hot(OP_PUT, key, val)?;
634 slate.index_update_offset(key, offset)?;
635 if slate.sched.on_append(now_ms) {
640 slate.commit()?;
641 }
642 Ok(())
643 }
644
645 pub fn put_durable(&self, key: &[u8], val: &[u8]) -> Result<(), DbError> {
646 if key.len() > slate_kv_core::config::MAX_KEY_LEN
647 || val.len() > slate_kv_core::config::MAX_VAL_LEN
648 {
649 return Err(DbError::InvalidArg("key or value too large".into()));
650 }
651 self.put(key, val)?;
652 self.commit()
653 }
654
655 #[allow(clippy::collapsible_if)]
656 pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, DbError> {
657 if key.len() > slate_kv_core::config::MAX_KEY_LEN {
658 return Err(DbError::InvalidArg("key too large".into()));
659 }
660 let mut inner = self.inner.lock().unwrap();
661 let mut cbuf = slate_kv_core::index::CandidateBuf::new();
662 inner.slate.index.candidates(key, &mut cbuf);
663
664 let mut best_val: Option<Vec<u8>> = None;
665 let mut best_seq = 0;
666
667 for &off in cbuf.as_slice() {
668 let mut rec_bytes = None;
669
670 let hot_write_off = inner.slate.log_hot.head.write_offset;
672 let hot_data = inner.slate.log_hot.batch.data();
673 if off >= hot_write_off && off < hot_write_off + hot_data.len() as u32 {
674 let rec_off = (off - hot_write_off) as usize;
675 if rec_off + slate_kv_core::config::REC_HDR_LEN <= hot_data.len() {
676 let mut hdr_bytes = [0u8; slate_kv_core::config::REC_HDR_LEN];
677 hdr_bytes.copy_from_slice(
678 &hot_data[rec_off..rec_off + slate_kv_core::config::REC_HDR_LEN],
679 );
680 if let Ok(hdr) = slate_kv_core::record::RecordHeader::decode(&hdr_bytes) {
681 let total_len = slate_kv_core::config::REC_OVERHEAD
682 + hdr.klen as usize
683 + hdr.vlen as usize;
684 if rec_off + total_len <= hot_data.len() {
685 rec_bytes =
686 Some((hdr_bytes, hot_data[rec_off..rec_off + total_len].to_vec()));
687 }
688 }
689 }
690 }
691
692 let cold_write_off = inner.slate.log_cold.head.write_offset;
694 let cold_data = inner.slate.log_cold.batch.data();
695 if rec_bytes.is_none()
696 && off >= cold_write_off
697 && off < cold_write_off + cold_data.len() as u32
698 {
699 let rec_off = (off - cold_write_off) as usize;
700 if rec_off + slate_kv_core::config::REC_HDR_LEN <= cold_data.len() {
701 let mut hdr_bytes = [0u8; slate_kv_core::config::REC_HDR_LEN];
702 hdr_bytes.copy_from_slice(
703 &cold_data[rec_off..rec_off + slate_kv_core::config::REC_HDR_LEN],
704 );
705 if let Ok(hdr) = slate_kv_core::record::RecordHeader::decode(&hdr_bytes) {
706 let total_len = slate_kv_core::config::REC_OVERHEAD
707 + hdr.klen as usize
708 + hdr.vlen as usize;
709 if rec_off + total_len <= cold_data.len() {
710 rec_bytes =
711 Some((hdr_bytes, cold_data[rec_off..rec_off + total_len].to_vec()));
712 }
713 }
714 }
715 }
716
717 if rec_bytes.is_none() {
719 use slate_kv_hal::Flash;
720 let mut hdr_bytes = [0u8; slate_kv_core::config::REC_HDR_LEN];
721 if inner.slate.flash.read(off, &mut hdr_bytes).is_ok() {
722 if let Ok(hdr) = slate_kv_core::record::RecordHeader::decode(&hdr_bytes) {
723 let total_len = slate_kv_core::config::REC_OVERHEAD
724 + hdr.klen as usize
725 + hdr.vlen as usize;
726 let mut rb = vec![0u8; total_len];
727 if inner.slate.flash.read(off, &mut rb).is_ok() {
728 rec_bytes = Some((hdr_bytes, rb));
729 }
730 }
731 }
732 }
733
734 if let Some((hdr_bytes, rb)) = rec_bytes {
735 if let Ok(hdr) = slate_kv_core::record::RecordHeader::decode(&hdr_bytes) {
736 let mut plain_out = vec![0u8; rb.len() - 16];
737 use slate_kv_core::log::Sealer;
738 if inner
739 .slate
740 .sealer
741 .open_record(
742 &hdr_bytes,
743 &rb[slate_kv_core::config::REC_HDR_LEN..],
744 &mut plain_out,
745 )
746 .is_ok()
747 {
748 if plain_out[..hdr.klen as usize] == *key {
749 if hdr.seq >= best_seq {
750 best_seq = hdr.seq;
751 if hdr.op == slate_kv_core::config::OP_PUT {
752 let val_start = hdr.klen as usize;
753 best_val = Some(
754 plain_out[val_start..val_start + hdr.vlen as usize]
755 .to_vec(),
756 );
757 } else {
758 best_val = None;
759 }
760 }
761 }
762 }
763 }
764 }
765 }
766
767 Ok(best_val)
768 }
769
770 pub fn delete_durable(&self, key: &[u8]) -> Result<(), DbError> {
781 if key.len() > slate_kv_core::config::MAX_KEY_LEN {
782 return Err(DbError::InvalidArg("key too large".into()));
783 }
784 self.delete(key)?;
785 self.commit()
786 }
787
788 pub fn delete(&self, key: &[u8]) -> Result<(), DbError> {
789 if key.len() > slate_kv_core::config::MAX_KEY_LEN {
790 return Err(DbError::InvalidArg("key too large".into()));
791 }
792 let mut inner = self.inner.lock().unwrap();
793 let OwnedEngine { slate, .. } = &mut *inner;
794 let now_ms = std::time::SystemTime::now()
795 .duration_since(std::time::UNIX_EPOCH)
796 .unwrap()
797 .as_millis() as u64;
798
799 let _ = slate.append_hot(OP_DEL, key, &[])?;
800
801 slate.index_remove_key(key);
802 if slate.sched.on_append(now_ms) {
805 slate.commit()?;
806 }
807 Ok(())
808 }
809
810 pub fn commit(&self) -> Result<(), DbError> {
811 let mut inner = self.inner.lock().unwrap();
812 inner.slate.commit()?;
813 Ok(())
814 }
815
816 pub fn compact(&self) -> Result<(), DbError> {
817 let mut inner = self.inner.lock().unwrap();
818 inner.slate.compact()?;
819 Ok(())
820 }
821
822 pub fn seal_epoch(&self) -> Result<(), DbError> {
829 let mut inner = self.inner.lock().unwrap();
830 inner.slate.commit()?;
831 inner.slate.seal_epoch_now()?;
832 Ok(())
833 }
834
835 pub fn scrub(&self) -> Result<ScrubReport, DbError> {
836 Ok(ScrubReport {
837 errors_found: 0,
838 errors_fixed: 0,
839 })
840 }
841
842 pub fn security_mode(&self) -> SecurityMode {
843 let inner = self.inner.lock().unwrap();
844 inner.slate.engine.security_mode
845 }
846
847 pub fn acked_seq(&self) -> u64 {
848 self.inner.lock().unwrap().slate.engine.acked_seq
849 }
850
851 pub fn next_seq(&self) -> u64 {
852 self.inner.lock().unwrap().slate.engine.next_seq
853 }
854
855 pub fn epoch(&self) -> u64 {
856 self.inner.lock().unwrap().slate.engine.epoch
857 }
858
859 pub fn len(&self) -> usize {
860 self.inner.lock().unwrap().slate.index.len()
861 }
862
863 pub fn is_empty(&self) -> bool {
864 self.len() == 0
865 }
866
867 pub fn stats(&self) -> Stats {
868 let inner = self.inner.lock().unwrap();
869 let m = &inner.slate.metrics;
870 Stats {
871 commits: m.commits,
872 wakes: m.wakes,
873 user_bytes: m.user_bytes,
874 gc_bytes: m.gc_bytes,
875 parity_bytes: m.parity_bytes,
876 marker_bytes: m.marker_bytes,
877 ckpt_bytes: m.ckpt_bytes,
878 erases: m.erases,
879 segments: inner.slate.segs.num_segments,
880 segments_sealed: inner
881 .slate
882 .segs
883 .count_in_state(slate_kv_core::gc::SegState::Sealed),
884 segments_free: inner.slate.segs.free_count(),
885 ckpt_seg_seq: inner.slate.ckpt_seg_seq,
886 cur_seg_seq: inner.slate.segs.current_seg_seq(),
887 gc_scanned: m.gc_scanned,
888 gc_relocated: m.gc_relocated,
889 gc_open_failed: m.gc_open_failed,
890 gc_segments_freed: m.gc_segments_freed,
891 hot_head: inner.slate.log_hot.head.write_offset,
892 cold_head: inner.slate.log_cold.head.write_offset,
893 seg_end: inner.slate.segs.end_addr(),
894 }
895 }
896}