1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! `DBImpl` — the database handle, plus public option structs.
//!
//! This is the headline module. Open a database, put / get / delete
//! / write batches, run iterators, take snapshots, compact ranges,
//! query properties.
//!
//! # Quickstart
//!
//! ```
//! use novakv::prelude::*;
//!
//! let env = MemEnv::new();
//! let db = DBImpl::open("/db", env, BytewiseComparator, Options::default()).unwrap();
//!
//! db.put(b"hello", b"world").unwrap();
//! assert_eq!(db.get(b"hello").unwrap(), Some(b"world".to_vec()));
//!
//! db.delete(b"hello").unwrap();
//! assert_eq!(db.get(b"hello").unwrap(), None);
//! ```
//!
//! # Concurrency
//!
//! `DBImpl` is `Send + Sync`. Every method takes `&self`; interior
//! mutability is handled via `Arc<(Mutex<DBImplCore>, Condvar)>`.
//! Multiple threads can share an `Arc<DBImpl>` directly — no outer
//! `Mutex` needed.
//!
//! ```
//! use std::sync::Arc;
//! use std::thread;
//! use novakv::prelude::*;
//!
//! let db = Arc::new(
//! DBImpl::open("/db", MemEnv::new(), BytewiseComparator, Options::default()).unwrap()
//! );
//! let handles: Vec<_> = (0..4).map(|i| {
//! let db = db.clone();
//! thread::spawn(move || {
//! db.put(format!("k{i}").as_bytes(), b"v").unwrap();
//! })
//! }).collect();
//! for h in handles { h.join().unwrap(); }
//! ```
//!
//! # Batched / atomic writes
//!
//! ```
//! use novakv::prelude::*;
//!
//! let db = DBImpl::open("/db", MemEnv::new(), BytewiseComparator, Options::default()).unwrap();
//! let mut batch = WriteBatch::new();
//! batch.put(b"a", b"1");
//! batch.put(b"b", b"2");
//! batch.delete(b"old");
//! db.write(&batch).unwrap(); // all three or none
//! ```
//!
//! # Iterators
//!
//! ```
//! use novakv::prelude::*;
//!
//! let db = DBImpl::open("/db", MemEnv::new(), BytewiseComparator, Options::default()).unwrap();
//! db.put(b"a", b"1").unwrap();
//! db.put(b"b", b"2").unwrap();
//!
//! let mut iter = db.new_iterator().unwrap();
//! iter.seek_to_first();
//! while iter.valid() {
//! println!("{:?} => {:?}", iter.key(), iter.value());
//! iter.next();
//! }
//! iter.status().unwrap(); // surface any read errors
//! ```
//!
//! # Snapshots
//!
//! `get_snapshot` pins a sequence number; reads with that snapshot
//! see the database as of that moment. Drop the `Arc<Snapshot>`
//! (or call `release_snapshot`) when done — compaction can't drop
//! tombstones older than the oldest live snapshot.
//!
//! ```
//! use novakv::prelude::*;
//!
//! let db = DBImpl::open("/db", MemEnv::new(), BytewiseComparator, Options::default()).unwrap();
//! db.put(b"k", b"v1").unwrap();
//! let snap = db.get_snapshot();
//! db.put(b"k", b"v2").unwrap();
//!
//! let mut ropts = ReadOptions::default();
//! ropts.snapshot = Some(snap);
//! assert_eq!(db.get_with_options(&ropts, b"k").unwrap(), Some(b"v1".to_vec()));
//! ```
//!
//! # Options
//!
//! All knobs live on [`Options`]. Common ones:
//!
//! | Field | Purpose |
//! |-------|---------|
//! | `create_if_missing` | open errors if false and DB doesn't exist (default `true` for convenience) |
//! | `error_if_exists` | open errors if true and DB exists |
//! | `paranoid_checks` | force CRC verify on every block read |
//! | `write_buffer_size` | memtable size before flush (4 MB default) |
//! | `max_open_files` | open-Table-cache capacity |
//! | `block_size` | bytes per data block (4096 default) |
//! | `block_restart_interval` | restart points per block (16 default) |
//! | `block_cache_size` | size for the default block cache when `block_cache` is None |
//! | `block_cache` | custom `Arc<dyn Cache<Arc<Block>>>` — share across DBs |
//! | `filter_policy` | `Arc<dyn FilterPolicy>` — Bloom or custom |
//! | `compressor` | `Arc<dyn Compressor>` — Snappy/LZ4/etc. (no built-in) |
//! | `info_log` | optional `Arc<dyn Logger>` for diagnostic messages |
//!
//! # ReadOptions
//!
//! - `snapshot` — pin to a snapshot
//! - `verify_checksums` — force per-call CRC verify (overrides
//! `paranoid_checks` only if true)
//! - `fill_cache` — set false on big scans to avoid evicting hot
//! blocks
//!
//! # WriteOptions
//!
//! - `sync` — call `fsync` on the WAL after the write, for
//! durability against power loss
//!
//! # Properties
//!
//! `db.get_property("novakv.stats")` returns the per-level
//! compaction stats table.
//! `db.get_property("novakv.num-files-at-level<N>")` returns the
//! file count at level N. `db.get_property("novakv.sstables")`
//! returns a per-file listing.
//!
//! # Background work
//!
//! Flush + compaction run on a single background thread spawned at
//! `DBImpl::open`. The thread joins on `Drop`. Errors in the bg
//! worker are stashed in `bg_error` and surfaced on the next
//! `get` / `put` / `new_iterator`.
//!
//! # Logger
//!
//! [`Logger`] is a `fn log(&self, message: &str)` trait. Currently
//! accepted via `Options::info_log` but no internal call sites emit
//! messages yet — the field is forward-compat scaffolding.
pub use *;