Skip to content

Commit 0d8e015

Browse files
committed
docs+ready for publish
1 parent bb9dc9a commit 0d8e015

File tree

5 files changed

+114
-197
lines changed

5 files changed

+114
-197
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ prost = "0.12"
3232
tower-http = { version = "0.3", features = ["cors"] }
3333

3434
# Local crate references
35-
shorterdb = { path = "crates/shorterdb" }
35+
shorterdb = { path = "crates/shorterdb", version = "0.2.0" }

crates/shorterdb/ARCHITECTURE.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# ShorterDB Architecture
2+
3+
ShorterDB implements a Log-Structured Merge-Tree (LSM-Tree) inspired architecture, designed for high write throughput and durability.
4+
5+
## High-Level Design
6+
7+
```
8+
┌─────────────────────────────────────────────────────────────────┐
9+
│ ShorterDB │
10+
│ │
11+
│ ┌─────────────────────────────────────────────────────────┐ │
12+
│ │ MEMTABLE │ │
13+
│ │ (In-Memory SkipList) │ │
14+
│ │ All writes go here first for fast performance │ │
15+
│ └────────────────────────┬────────────────────────────────┘ │
16+
│ │ │
17+
│ ┌─────────────────┼─────────────────┐ │
18+
│ ▼ │ ▼ │
19+
│ ┌─────────────┐ │ ┌─────────────┐ │
20+
│ │ WAL │ │ │ FLUSHER │ │
21+
│ │ (Append- │ │ │ (Background│ │
22+
│ │ Only Log) │ │ │ Thread) │ │
23+
│ └─────────────┘ │ └──────┬──────┘ │
24+
│ │ │ │
25+
│ │ ▼ │
26+
│ │ ┌───────────────────┐ │
27+
│ │ │ SST │ │
28+
│ │ │ (Sorted String │ │
29+
│ │ │ Tables on Disk) │ │
30+
│ │ └───────────────────┘ │
31+
│ │ │
32+
└─────────────────────────────────────────────────────────────────┘
33+
```
34+
35+
## Core Components
36+
37+
### 1. Memtable (In-Memory Buffer)
38+
39+
The **Memtable** serves as the primary write buffer.
40+
41+
- **Data Structure**: Lock-free SkipList (`crossbeam-skiplist`).
42+
- **Function**: Accepts all `set` and `delete` operations.
43+
- **Concurrency**: Lock-free reads; supports high concurrency.
44+
- **Flush Trigger**: When the size exceeds the threshold (default 4MB), it becomes immutable and is scheduled for flushing to disk.
45+
46+
### 2. Write-Ahead Log (WAL)
47+
48+
The **WAL** ensures data durability/persistence.
49+
50+
- **Mechanism**: Every write to the Memtable is simultaneously appended to the WAL file.
51+
- **Sync**: Writes are `fsync`'d for durability guarantees.
52+
- **Recovery**: On startup, the WAL is replayed to reconstruct the Memtable state, ensuring no data loss in case of a crash.
53+
- **Rotation**: The WAL is truncated/cleared only after the Memtable has been successfully flushed to an SST file.
54+
55+
### 3. Sorted String Table (SST)
56+
57+
**SSTs** form the persistent storage layer on disk.
58+
59+
- **Immutability**: Once written, SST files are never modified.
60+
- **Format**:
61+
- **Data Block**: Sorted key-value pairs.
62+
- **Sparse Index**: Facilitates efficient disk reads (binary search).
63+
- **Footer**: Metadata and magic numbers for integrity.
64+
- **Levels**: Organized into levels (currently simplistic L0) to manage compaction.
65+
66+
## ACID Properties
67+
68+
ShorterDB provides specific ACID guarantees suitable for a key-value store:
69+
70+
- **Atomicity**: Single-key operations (`set`, `delete`) are atomic. They either fully succeed or fail.
71+
- **Consistency**: The database maintains internal consistency. Deletes (tombstones) are correctly propagated, and ordering is maintained.
72+
- **Isolation**: Snapshot-like isolation for reads. Readers perceive the state at the start of their operation and are not blocked by writers.
73+
- **Durability**: Strong durability. Writes are acknowledged only after being persisted to the WAL.
74+
75+
## Flush and Compaction
76+
77+
- **Flush**: Background thread handles the conversion of immutable Memtables to SST files to avoid blocking writes.
78+
- **Compaction**: Basic compaction (merging multiple SST files) prevents potential "read amplification" (reading too many files) and reclaims space taken by obsolete or deleted records.

crates/shorterdb/README.md

Lines changed: 35 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -1,221 +1,71 @@
11
# ShorterDB
22

3-
A lightweight embedded key-value store for Rust, inspired by the Log-Structured Merge-Tree (LSM-Tree) architecture.
3+
A high-performance, embedded key-value store for Rust, built with a Log-Structured Merge-Tree (LSM-Tree) architecture.
44

55
[![Crates.io](https://img.shields.io/crates/v/shorterdb.svg)](https://crates.io/crates/shorterdb)
66
[![Documentation](https://docs.rs/shorterdb/badge.svg)](https://docs.rs/shorterdb)
77
[![License](https://img.shields.io/crates/l/shorterdb.svg)](../../LICENSE)
88

9-
## Overview
9+
## Features
1010

11-
ShorterDB is an embedded key-value database that combines simplicity with proven database design patterns. Built for learning, experimentation, and lightweight applications that need persistent storage.
11+
- **Embedded**: Runs directly in your application process (no external server required).
12+
- **Persistent**: Data is durably stored using a Write-Ahead Log (WAL) and SSTables.
13+
- **Fast**: In-memory writers using lock-free SkipLists.
14+
- **Simple API**: Minimalistic `get`, `set`, and `delete` interface.
15+
- **Thread-Safe**: Designed for concurrent access.
1216

13-
## Architecture
14-
15-
ShorterDB implements an LSM-Tree inspired architecture with three core components:
16-
17-
```
18-
┌─────────────────────────────────────────────────────────────────┐
19-
│ ShorterDB │
20-
│ │
21-
│ ┌─────────────────────────────────────────────────────────┐ │
22-
│ │ MEMTABLE │ │
23-
│ │ (In-Memory SkipList) │ │
24-
│ │ All writes go here first for fast performance │ │
25-
│ └────────────────────────┬────────────────────────────────┘ │
26-
│ │ │
27-
│ ┌─────────────────┼─────────────────┐ │
28-
│ ▼ │ ▼ │
29-
│ ┌─────────────┐ │ ┌─────────────┐ │
30-
│ │ WAL │ │ │ FLUSHER │ │
31-
│ │ (Append- │ │ │ (Background│ │
32-
│ │ Only Log) │ │ │ Thread) │ │
33-
│ └─────────────┘ │ └──────┬──────┘ │
34-
│ │ │ │
35-
│ │ ▼ │
36-
│ │ ┌───────────────────┐ │
37-
│ │ │ SST │ │
38-
│ │ │ (Sorted String │ │
39-
│ │ │ Tables on Disk) │ │
40-
│ │ └───────────────────┘ │
41-
└─────────────────────────────────────────────────────────────────┘
42-
```
43-
44-
---
45-
46-
## Core Components
47-
48-
### Memtable
49-
50-
The **Memtable** is an in-memory sorted data structure that serves as the write buffer for all incoming operations.
51-
52-
| Property | Implementation |
53-
|----------|----------------|
54-
| **Data Structure** | Lock-free SkipList (`crossbeam-skiplist`) |
55-
| **Ordering** | Keys are always sorted in byte order |
56-
| **Concurrency** | Thread-safe reads without locks |
57-
| **Size Tracking** | Approximate byte-based tracking with atomic counters |
58-
| **Threshold** | Configurable (default 4MB), triggers flush when exceeded |
59-
60-
**Key Features:**
61-
- All writes (set/delete) are first inserted into the memtable
62-
- Deletes are represented as **tombstones** (markers indicating deletion)
63-
- Provides the fastest read path since data is in memory
64-
- When full, becomes **immutable** and is flushed to SST in the background
65-
66-
---
67-
68-
### Write-Ahead Log (WAL)
69-
70-
The **WAL** is an append-only log file that ensures durability by persisting every write before it's applied to the memtable.
71-
72-
| Property | Implementation |
73-
|----------|----------------|
74-
| **Format** | Binary: `[op(1B)][key_len(4B)][key][value_len(4B)][value]` |
75-
| **Sync Mode** | `fsync` after every write for durability |
76-
| **Recovery** | Replayed on startup to restore uncommitted state |
77-
| **Rotation** | Truncated after successful SST flush |
78-
79-
**Key Features:**
80-
- Guarantees no data loss on crash (writes survive before acknowledgment)
81-
- Supports both `Set` and `Delete` operations
82-
- Corruption-tolerant recovery (stops at first invalid entry)
83-
- Sanity checks prevent OOM from corrupted length fields (100MB max)
84-
85-
---
86-
87-
### Sorted String Table (SST)
88-
89-
**SST files** are immutable, sorted key-value files stored on disk. They represent the long-term persistent storage layer.
90-
91-
| Property | Implementation |
92-
|----------|----------------|
93-
| **File Format** | `[Data Entries][Sparse Index][Footer(24B)]` |
94-
| **Index** | Sparse index every 16 entries for O(log n) lookups |
95-
| **Magic Number** | `SSTFILE\0` for corruption detection |
96-
| **Tombstones** | Preserved until compaction |
97-
98-
**File Structure:**
99-
```
100-
┌────────────────────────────────┐
101-
│ Data Entries │ Sorted key-value pairs
102-
│ [key_len][key][val_len] │
103-
│ [value][type_marker] │
104-
├────────────────────────────────┤
105-
│ Sparse Index │ Every 16th key → offset
106-
│ [key_len][key][offset(8B)] │
107-
├────────────────────────────────┤
108-
│ Footer │ data_end(8B) + index_off(8B)
109-
│ │ + magic(8B) = 24 bytes
110-
└────────────────────────────────┘
111-
```
112-
113-
**Key Features:**
114-
- Binary search on sparse index for efficient lookups
115-
- Supports both data entries and tombstones
116-
- L0 compaction when >4 files accumulate (merges and removes tombstones)
117-
- Files are never modified, only created and deleted
118-
119-
---
17+
## Installation
12018

121-
## ACID Properties
19+
Add this to your `Cargo.toml`:
12220

123-
### Atomicity
124-
125-
Each individual operation (`set`, `delete`) is atomic:
126-
127-
- **Single-key atomicity**: A write either fully completes or doesn't happen
128-
- **WAL-first**: Operations are logged before being applied
129-
- **No partial writes**: If crash occurs mid-operation, recovery ignores incomplete WAL entries
130-
131-
> **Limitation**: Multi-key transactions are not supported. Each operation is independent.
132-
133-
---
134-
135-
### Consistency
136-
137-
The database maintains a consistent view of data:
138-
139-
- **Ordered key-value store**: Keys are always sorted, enabling range scans
140-
- **Tombstone semantics**: Deletes are properly propagated through all layers
141-
- **Read consistency**: Queries check memtable → immutable memtable → SST in order
142-
- **No phantom reads**: A key is either present with its latest value or absent
143-
144-
---
145-
146-
### Isolation
147-
148-
ShorterDB provides **snapshot-like** isolation for reads:
149-
150-
- **Read path**: Checks layers in order (newest to oldest), returns first match
151-
- **Write path**: New writes don't affect in-progress reads
152-
- **Background flush**: Uses immutable memtable copy, reads continue on original
153-
154-
> **Isolation Level**: Roughly equivalent to "Read Committed" — you see committed data, but concurrent writes may be visible.
155-
156-
---
157-
158-
### Durability
159-
160-
Writes are durable once `set()` or `delete()` returns:
161-
162-
| Guarantee | Mechanism |
163-
|-----------|-----------|
164-
| **Write durability** | WAL is `fsync`'d after every write |
165-
| **Crash recovery** | WAL is replayed on startup to restore state |
166-
| **SST durability** | Files are `sync_all`'d after creation |
167-
| **WAL rotation** | Only cleared after SST is confirmed on disk |
168-
169-
**Recovery Flow:**
170-
```
171-
1. Open database
172-
2. Load existing SST files
173-
3. Replay WAL entries into fresh memtable
174-
4. Resume normal operation
21+
```toml
22+
[dependencies]
23+
shorterdb = "0.2.0"
17524
```
17625

177-
---
178-
179-
## Usage
26+
## Quick Start
18027

18128
```rust
18229
use shorterdb::ShorterDB;
18330
use std::path::Path;
18431

18532
fn main() -> Result<(), Box<dyn std::error::Error>> {
186-
// Open database with default settings (4MB memtable)
187-
let mut db = ShorterDB::new(Path::new("./my_db"))?;
33+
// Open the database (creates directory if missing)
34+
let mut db = ShorterDB::new(Path::new("/tmp/my_db"))?;
18835

189-
// Set values (accepts &str or &[u8])
190-
db.set("user:1", "alice")?;
191-
db.set("user:2", "bob")?;
36+
// Store a key-value pair
37+
db.set(b"username", b"ferris")?;
19238

193-
// Get values
194-
if let Some(value) = db.get("user:1")? {
195-
println!("Found: {}", std::str::from_utf8(&value)?);
39+
// Retrieve the value
40+
if let Some(value) = db.get(b"username")? {
41+
println!("Found user: {}", String::from_utf8_lossy(&value));
19642
}
19743

198-
// Delete values
199-
let existed = db.delete("user:1")?;
200-
println!("Key existed: {}", existed);
201-
202-
// Graceful shutdown (also called automatically on drop)
203-
db.close()?;
44+
// Delete the key
45+
db.delete(b"username")?;
20446

20547
Ok(())
20648
}
20749
```
20850

51+
## Architecture
52+
53+
ShorterDB uses a classic LSM-Tree design with a Memtable, Write-Ahead Log, and background Flusher.
54+
55+
For deep technical details on the internal design, file formats, and ACID guarantees, see [ARCHITECTURE.md](ARCHITECTURE.md).
56+
20957
## Examples
21058

59+
The project workspace includes examples you can run locally:
60+
21161
```bash
212-
# Run the embedded example
213-
cargo run --example embedded
62+
# Basic embedded usage
63+
cargo run -p examples --bin embedded
21464

215-
# Run the interactive REPL
216-
cargo run --example repl
65+
# Interactive REPL
66+
cargo run -p examples --bin repl
21767
```
21868

21969
## License
22070

221-
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
71+
Licensed under either of [Apache License, Version 2.0](../../LICENSE-APACHE) or [MIT license](../../LICENSE-MIT) at your option.

verify_multiple

-433 KB
Binary file not shown.

verify_multiple.rs

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)