# vsdb API Examples
This document provides examples for selected public APIs in the `vsdb` crate.
## Namespaces
All collection types support namespaces — independently-rooted engine instances
for physical placement, hard isolation, and O(1) bulk reclaim. Everyday code
stays parameterless; placement is expressed through the object graph.
```rust
use vsdb::{Namespace, NamespaceOpts, basic::mapx::Mapx, basic::mapx_ord::MapxOrd};
// ---- Everyday tier: zero parameters, no names, no paths ----
let cold = Namespace::create().unwrap();
// Ambient placement: everything created inside scope() lands in `cold`.
// Co-location: "put this data together with that data".
let index = Mapx::<u64, u64>::new_in(&archive.namespace());
assert_eq!(archive.namespace().id(), index.namespace().id());
// ---- Persist and recover via InstanceId ----
let id = archive.save_meta().unwrap();
// id.to_string() => "42@1" (map 42 in namespace 1)
let restored: Mapx<u64, String> = Mapx::from_meta(id).unwrap();
// Bare u64 from pre-namespace code still works for default-namespace maps.
let legacy: Mapx<u64, String> = Mapx::new();
let old_id = legacy.save_meta().unwrap().map_id;
let from_old: Mapx<u64, String> = Mapx::from_meta(old_id).unwrap();
// ---- Advanced tier (opt-in): explicit volume, shards, budget ----
let fast = Namespace::create_with(NamespaceOpts {
path: Some("/mnt/nvme/vsdb".into()),
shards: 8,
mem_budget_mb: 2048,
}).unwrap();
let hot: MapxOrd<i32, String> = MapxOrd::new_in(&fast);
// ---- Cross-namespace copy: `clone_in` (the `Clone` counterpart) ----
// Deep-copies into a brand-new instance placed in the target namespace
// (chunked; never whole-map in memory). Available on MapxRaw, Mapx,
// MapxOrd, MapxOrdRawKey, and Orphan.
let archive_copy = archive.clone_in(&Namespace::default_ns()).unwrap();
// Admin functions (from vsdb_core, re-exported by vsdb):
// vsdb_ns_list() -> Result<Vec<NsInfo>>
// vsdb_ns_close(id) -> Result<()>
// ns.close() -> consuming form; refusal returns the handle
// vsdb_ns_destroy(id) -> Result<()>
// vsdb_ns_relocate(id, new_path) -> Result<()>
```
Key rules:
- `Mapx::new()` targets the implicit default namespace — existing code needs zero changes.
- A composite structure (`VerMap`, `SlotDex`, …) always lives wholly inside one namespace.
- Cross-namespace atomic transactions do not exist (separate WALs).
- Reads, writes, and deserialization always route via the handle's own namespace —
ambient scope affects creation only.
- Memory budgets are static and per-engine: the default namespace defaults to
a fixed 2 GiB, every other namespace to a fixed 512 MB — nothing sizes from
the host's RAM or its cgroup. `VSDB_MEM_BUDGET_MB` (applied verbatim) sets
the default engine's budget; a larger budget enlarges the block cache and
write buffers, which directly improves performance. Deployments that open
many namespaces should give each an explicit
`NamespaceOpts { mem_budget_mb, .. }` so the sum stays inside the process's
memory line.
## Mapx
`Mapx` is a hash map-like data structure that stores key-value pairs.
```rust
use vsdb::Mapx;
let mut map: Mapx<String, String> = Mapx::new();
// Insert some key-value pairs
map.insert(&"key1".to_string(), &"value1".to_string());
map.insert(&"key2".to_string(), &"value2".to_string());
// Get a value
assert_eq!(map.get(&"key1".to_string()), Some("value1".to_string()));
// Check if a key exists
assert!(map.contains_key(&"key2".to_string()));
// Iterate over the key-value pairs
for (key, value) in map.iter() {
println!("{}: {}", key, value);
}
// Remove a key-value pair
map.remove(&"key1".to_string());
```
## MapxOrd
`MapxOrd` is a B-tree map-like data structure that stores key-value pairs in a sorted order.
```rust
use vsdb::MapxOrd;
let mut map: MapxOrd<i32, String> = MapxOrd::new();
// Insert some key-value pairs
map.insert(&3, &"three".to_string());
map.insert(&1, &"one".to_string());
map.insert(&2, &"two".to_string());
// Get a value
assert_eq!(map.get(&1), Some("one".to_string()));
// Iterate over the key-value pairs in sorted order
for (key, value) in map.iter() {
println!("{}: {}", key, value);
}
// Get the first and last key-value pairs
assert_eq!(map.first(), Some((1, "one".to_string())));
assert_eq!(map.last(), Some((3, "three".to_string())));
```
## VerMap
`VerMap` provides Git-style versioned storage with branching, commits, merge, and rollback (`rollback_to`).
For a detailed architecture guide with diagrams, see [Versioned Module — Architecture & Internals](versioned.md).
```rust
use vsdb::versioned::map::VerMap;
use vsdb::versioned::BranchId;
// 1. Create an empty versioned map (starts with a "main" branch).
let mut m: VerMap<u32, String> = VerMap::new();
let main = m.main_branch();
// 2. Write on the main branch and commit a snapshot.
m.insert(main, &1, &"hello".into()).unwrap();
m.commit(main).unwrap();
// 3. Fork a feature branch — cheap, no data copied.
let feat: BranchId = m.create_branch("feature", main).unwrap();
m.insert(feat, &1, &"updated".into()).unwrap();
m.commit(feat).unwrap();
// 4. Branches are isolated.
assert_eq!(m.get(main, &1).unwrap(), Some("hello".into()));
assert_eq!(m.get(feat, &1).unwrap(), Some("updated".into()));
// 5. Three-way merge: feature → main (source wins on conflict).
m.merge(feat, main).unwrap();
assert_eq!(m.get(main, &1).unwrap(), Some("updated".into()));
// 6. Clean up: deleting the branch reclaims unreachable commits and
// B+ tree nodes automatically — no manual gc() call required
// (gc() exists for crash recovery only).
m.delete_branch(feat).unwrap();
```
## MptCalc / SmtCalc (Merkle Trie)
`MptCalc` and `SmtCalc` are stateless, in-memory Merkle trie implementations:
they hold an ephemeral trie for root computation while all persistence and
versioning is handled by an external store (e.g. `VerMap`).
```rust,ignore
use vsdb::trie::MptCalc;
// Build a trie
let mut mpt = MptCalc::new();
mpt.insert(b"key1", b"value1").unwrap();
mpt.insert(b"key2", b"value2").unwrap();
// MPT keys must be at most 1024 bytes (MAX_MPT_KEY_LEN); insert,
// batch_update, and from_entries return an error for longer keys.
// SMT keys are hashed to a fixed 256-bit path and have no length limit.
// Compute the 32-byte Merkle root hash
let root = mpt.root_hash().unwrap();
assert_eq!(root.len(), 32);
// Lookup
assert_eq!(mpt.get(b"key1").unwrap(), Some(b"value1".to_vec()));
// Remove
mpt.remove(b"key1").unwrap();
assert_eq!(mpt.get(b"key1").unwrap(), None);
// Batch update
mpt.batch_update(&[
(b"k1".as_ref(), Some(b"v1".as_ref())),
(b"k2".as_ref(), None), // remove
]).unwrap();
// Disposable cache (low-level API, used internally by VerMapWithProof):
// mpt.save_cache(cache_id, sync_tag).unwrap();
// let (loaded, tag, hash) = MptCalc::load_cache(cache_id).unwrap();
// When using VerMapWithProof, caching is fully automatic.
```
### SmtCalc with Proofs
```rust,ignore
use vsdb::trie::SmtCalc;
let mut smt = SmtCalc::new();
smt.insert(b"alice", b"100").unwrap();
smt.insert(b"bob", b"200").unwrap();
let root = smt.root_hash().unwrap();
let root32: [u8; 32] = root.try_into().unwrap();
// Membership proof
let proof = smt.prove(b"alice").unwrap();
assert_eq!(proof.value(), Some(b"100".as_ref()));
assert!(SmtCalc::verify_proof(&root32, b"alice", &proof).unwrap());
// Non-membership proof
let proof = smt.prove(b"charlie").unwrap();
assert_eq!(proof.value(), None);
assert!(SmtCalc::verify_proof(&root32, b"charlie", &proof).unwrap());
```
## VerMapWithProof
Integrates `VerMap` with `MptCalc` for versioned Merkle root computation.
```rust,ignore
use vsdb::trie::{MptCalc, VerMapWithProof};
let mut vmp: VerMapWithProof<Vec<u8>, Vec<u8>, MptCalc> = VerMapWithProof::new();
let main = vmp.map().main_branch();
// Write data and commit
vmp.map_mut().insert(main, &b"key1".to_vec(), &b"val1".to_vec()).unwrap();
vmp.map_mut().commit(main).unwrap();
// Compute the Merkle root (incrementally maintained)
let root = vmp.merkle_root(main).unwrap();
assert_eq!(root.len(), 32);
// Cache is auto-saved on Drop and auto-loaded on construction.
// No manual save_cache / load_cache calls needed.
```
## Slotdex
`SlotDex` (in the `slotdex` module) is a skip-list-like index for efficient, timestamp-based paged queries.
```rust,ignore
use vsdb::SlotDex64; // SlotDex<u64, K> alias — slot type is u64
let mut db = SlotDex64::<String>::new(10u64, false); // tier_capacity must be >= 2
// Insert entries into slots (e.g., timestamps)
db.insert(100, "entry_a".to_string()).unwrap();
db.insert(100, "entry_b".to_string()).unwrap();
db.insert(200, "entry_c".to_string()).unwrap();
db.insert(300, "entry_d".to_string()).unwrap();
assert_eq!(db.total(), 4);
// Paged queries
let page = db.get_entries_by_page(2, 0, true); // page_size=2, page_index=0, reverse=true
assert_eq!(page, vec!["entry_d".to_string(), "entry_c".to_string()]);
// Slot-range queries
let entries = db.get_entries_by_page_slot(Some(100), Some(200), 10, 0, false);
assert_eq!(entries.len(), 3);
// Remove
db.remove(100, &"entry_a".to_string());
assert_eq!(db.total(), 3);
// Bulk insertion (amortizes engine writes; ideal for imports/rebuilds)
db.insert_batch([(400u64, "entry_e".to_string()), (400, "entry_f".to_string())])
.unwrap();
assert_eq!(db.total(), 5);
```