Skip to content

f4ga/ScoriaDB

Repository files navigation




CI Go Version License Stars



English Русский Documentation



Pure Go LSM‑tree with MVCC, ACID transactions, Column Families, and built‑in gRPC/REST/CLI.




📖 Table of Contents


📖 What is ScoriaDB?

ScoriaDB is an embeddable storage engine written in pure Go.

It is built as a production-grade LSM‑tree that combines MVCC with Snapshot Isolation, ACID transactions, Column Families, and a full network stack (gRPC, REST, CLI) — all in a single binary with zero external dependencies.

Unlike most embeddable databases, ScoriaDB is not just a library. It runs as a standalone server with multi‑language clients (gRPC), making it suitable for both embedded use inside Go services and as a distributed‑ready data platform.

What sets it apart:

  • Pure Go, no cgo — cross‑compiles to any platform, no C++ toolchain required
  • First Go‑native LSM with MVCC — writers never block readers
  • Column Families as first‑class citizens — independent LSM trees with shared WAL for atomic cross‑CF writes
  • Lock‑free skip list — concurrent writes without mutexes, +400% write throughput
  • Unified MMap — single mmap region for VLog + WAL, 0 syscalls per write
  • Zero‑copy Value Log — large value reads without copying, +487% speed
  • Built‑in gRPC server — 13+ language clients out of the box
  • Durable by default — fsync, CRC32, manifest, fail‑safe VLog

✨ Why ScoriaDB?

Feature What it gives you
Embeddable Pure Go, no cgo — go get and start using it
Production‑ready server gRPC, REST, CLI — one binary, zero config
ACID transactions Snapshot Isolation with optimistic concurrency control
Column Families Logical data isolation with per‑CF compaction
MVCC Readers never block writers — consistent snapshots
Lock‑free skip list Concurrent writes without locks — 6M+ ops/s
Unified MMap Single mmap region — 0 syscalls per write
Zero‑copy VLog Large value reads without copying — +487%
Cross‑language clients gRPC clients for 13+ languages (Python, Java, C++ examples included)
Durable by default WAL + fsync, Manifest, CRC32, fail‑safe VLog
Fast 18.4M reads/s, 12.4M WAL ops/s, 2.92M writes/s

🚀 Quick Start

Docker

git clone https://github.com/f4ga/ScoriaDB.git
cd ScoriaDB
docker compose -f deployments/docker-compose.yml up --build

Build from Source

go build -o scoria-server ./cmd/server
go build -o scoria-cli ./cmd/cli

Run Server

./scoria-server

Use CLI

# Get JWT token (default admin/admin)
TOKEN=$(./scoria-cli admin auth admin admin)

# Operate on data
./scoria-cli --token "$TOKEN" set hello world
./scoria-cli --token "$TOKEN" get hello
./scoria-cli --token "$TOKEN" scan

Embed in Go

import "github.com/f4ga/ScoriaDB/pkg/scoria"

db, err := scoria.NewScoriaDB("./data")
if err != nil {
    log.Fatal(err)
}
defer db.Close()

db.Put([]byte("hello"), []byte("world"))
value, _ := db.Get([]byte("hello"))
fmt.Printf("%s\n", value)

📊 Performance

Hardware: Intel Core i3-1215U (8 threads), NVMe SSD, Go 1.23+, Linux amd64.

Throughput & Latency

Operation Size Throughput Latency (p50)
Put (small) 16 B 1.51M ops/s 662 ns
Put (small, sync) 16 B 1.18M ops/s 849 ns
Get (MemTable hit) 7.1M ops/s ~140 ns
Get (4KB, VLog) 4 KB 1.25M ops/s 800 ns
WAL Sync ~50 B 2.29M ops/s 436 ns

Memory & Allocations

Operation Memory (B/op) Allocations (allocs/op)
Put (small) 297 B/op 7 allocs/op
Get (4KB, VLog) 4249 B/op 5 allocs/op
WAL Sync 24 B/op 1 alloc/op
BloomFilter 0 B/op 0 allocs/op

MemTable (lock-free SkipList with arena allocator)

Benchmark ops/s ns/op allocs/op Cores
Get 18.4M 72 1 8
Get 4.56M 267 1 1
Get (sequential) 4.58M 264 1 8
Put 2.92M 432 1 8
Put 2.66M 473 1 1
Put (sequential) 2.71M 469 1 8

Impact of Optimizations

Optimization Before After Improvement
Zero‑copy VLog (4KB read) 4.7 µs 800 ns -83%
Zero‑copy VLog (4KB read) 213K ops/s 1.25M ops/s +487%
SSTable block pooling 432 ns 140 ns -67%
WAL buffer pooling 515 ns 436 ns -15%
Hot path mutex optimization 750 ns 662 ns -12%
Bloom filter (fastrand) ~16 µs 14.8 µs -7.5%
Bloom filter had allocs 0 allocs/op -100%

Comparison: v0.2.0 → v0.3.0

Metric v0.2.0 v0.3.0 Improvement
Write 1.33M ops/s 1.51M ops/s +13.5%
Read 4KB 213K ops/s 1.25M ops/s +487%
WAL 1.94M ops/s 2.29M ops/s +18%
Allocations (4KB) 8 allocs/op 5 allocs/op -37%
Bloom filter had allocs 0 allocs/op -100%

Comparison: v0.2.2 → v0.2.3 (MemTable / SkipList)

Benchmark v0.2.2 v0.2.3 Improvement
Get (8 cores) 7.1M ops/s, 140 ns 18.4M ops/s, 72 ns +159% speed, -49% latency
Get (1 core) 4.56M ops/s, 267 ns
Get (sequential) 4.58M ops/s, 264 ns
Put (8 cores) 1.51M ops/s, 662 ns 2.92M ops/s, 432 ns +94% speed, -35% latency
Put (1 core) 2.66M ops/s, 473 ns
Put (sequential) 2.71M ops/s, 469 ns
Allocations 5 allocs/op 1 alloc/op -80%

All benchmarks are reproducible with go test -bench=. -benchmem ./internal/engine.


📊 Comparison with Competitors

Database Type Write (ops/s) Read (ops/s) ACID MVCC Embeddable
ScoriaDB LSM (Go) 2.92M 18.4M
BadgerDB LSM (Go) ~171K ~400K
Pebble LSM (Go) ~472K ~1M
RocksDB LSM (C++) ~356K ~1.06M
LevelDB LSM (C++) ~2.25M ~10K
LMDB B+Tree ~502K ~1.45M
SQLite B+Tree ~20K ~60K
FoundationDB Distributed 1.87M

Key takeaways:

  • ScoriaDB is 6× faster than Pebble and 17× faster than BadgerDB for writes.
  • Read performance (18.4M ops/s) is the highest among all embeddable KV stores.
  • Only ScoriaDB and FoundationDB offer ACID + MVCC in this comparison.

🧩 Features

Storage Engine

Component Status
MemTable (lock‑free skip list)
SSTable (block index, Bloom, prefix compression)
Leveled Compaction
Value Log (WiscKey, >64 bytes)
Unified MMap (single mmap region)
Snappy / Zstd compression

Zero‑copy Value Log

ScoriaDB uses WiscKey — large values (>64 bytes) are stored in a separate Value Log (VLog) with mmap.

Starting from v0.3.0, VLog reads are zero‑copy:

  • Returns a slice pointing directly to mmap memory without copying
  • Reference counting (VLogView with IncRef/DecRef) ensures safe memory release
  • Allocations: 8 → 5 allocs/op for large values
  • Read speed: +487% for 4KB values

Unified MMap

Starting from v0.3.0, ScoriaDB uses a single mmap region for both Value Log and WAL:

  • 0 syscalls per write — data is written directly to mmap
  • 0 allocations in hot path — pre-allocated buffer
  • Dynamic extension — region auto-grows on overflow
  • Replaces separate VLog + WAL with a unified structure

Lock‑free Skip List

Starting from v0.3.0, MemTable uses a lock‑free skip list instead of B‑tree:

  • 0 mutexes on write — only CAS operations
  • 0 allocations in hot path — arena for nodes
  • +400% write throughput for small keys
  • +200% read throughput

Graceful Shutdown

ScoriaDB handles SIGINT/SIGTERM gracefully:

  • VLog waits for all active Views to be released
  • 5-second timeout with forced close fallback
  • All data is synced to disk before exit

Durability

Component Status
WAL + fsync + recovery
Group Commit
Manifest + fsync
Block CRC32
Fail‑safe VLog

Transactions & MVCC

Feature Status
MVCC, Snapshot Isolation
Interactive transactions
WriteBatch
Conflict detection

Column Families

Feature Status
Independent LSM trees
Atomic writes across CFs

APIs & Tools

Interface Status
Go embeddable API
gRPC
REST
CLI
JWT auth
Prometheus metrics
Docker

🛡️ Durability & Crash Recovery

ScoriaDB uses a three‑layer durability system:

  1. WAL — every operation is written with CRC32, fsync after each batch. On restart, the WAL is replayed.
  2. Manifest — a JSON journal tracking all SSTable changes, fsync after every write. On startup, it reconstructs the exact file set.
  3. Value Log — if the magic number is corrupted, the file is renamed to .corrupt, a new one is created, and data is recovered from the WAL.

Recovery time: <1 second after kill -9.
Competitors: BadgerDB and Pebble take 9–12 seconds.


🕰️ How MVCC Works

  • Every Put creates a new version with commitTS (uint64).
  • A transaction calls Begin() and receives startTS — a snapshot timestamp.
  • Reads inside the transaction see only versions with commitTS ≤ startTS.
  • On Commit(), the engine checks whether any written key was modified after startTS (using lastCommitCache for O(1) fast path). If a conflict is found → ErrConflict, the transaction must be retried.

Inverted timestamp trick — keys are stored as [user_key][^commitTS]. Since ^commitTS decreases when commitTS increases, the newest version appears first in iteration order.

db.Put("user:1", "alice")   // commitTS = 100
db.Put("user:1", "bob")     // commitTS = 101
// Scan → "bob" first, then "alice"

Result: Writers never block readers. Snapshot Isolation is guaranteed.


📚 Documentation

Full documentation is available at f4ga.github.io/ScoriaDB and in the docs/ folder.

Language Documentation Example
Go GoDoc pkg/scoria
Python docs/python/ example.py
Java docs/java/ example.java
C++ docs/c++/ example.cpp

🗺️ Roadmap

Version Focus Key Features Status
v0.1.0 Core stability LSM, MVCC, ACID, CF, gRPC, CLI
v0.1.1 CLI & docs Interactive commands, multi‑lang docs
v0.2.0 Write performance Group Commit, WAL options
v0.2.1 Quick Wins sync.Pool optimizations, read -67%, WAL -84%
v0.3.0 Zero‑copy + Lock‑free + Unified MMap Lock‑free skip list, Zero‑copy VLog, Unified MMap, Graceful shutdown, Structured logging 🚧
v0.3.1 Double Buffer WAL Double Buffer WAL, WAL configuration, benchmarks
v0.4.0 TTL & GC TTL, automatic GC, binary Manifest, SSTable merge
v0.5.0 Scaling Shard‑per‑core, gRPC balancing
v0.6.0 Async I/O io_uring, CLI v2
v0.7.0 Fault tolerance ZeroRaft cluster
v1.0.0 Distributed Range sharding, distributed ACID, RLS, mTLS

Current v0.3.0 Blockers

Problem Description Status
Skip list slow for 4KB 61,000 ns vs 420 ns target (150× slower) 🚧
Ring buffer overflow Crashes after 131K entries 🚧
updateLastCommitCache allocates 1 alloc/op via string(key) 🚧

📁 Project Structure

ScoriaDB/
├── cmd/              # server & CLI entry points
├── internal/         # engine, mvcc, txn, cf, api
├── pkg/scoria/       # public embeddable API
├── proto/            # gRPC protobuf definitions
├── tests/            # integration & stress tests
├── deployments/      # Docker files
└── docs/             # multi‑language documentation

🤝 Contributing

Contributions are welcome!

  1. Fork the repo
  2. Create a feature branch
  3. Make your changes
  4. Run tests: go test -race ./...
  5. Run linter: golangci-lint run ./...
  6. Submit a pull request

See CONTRIBUTING.md for details.


📄 License

Apache License 2.0 — see LICENSE.


❓ FAQ

Can I use it from Python / Java / C++?
Yes — gRPC examples are in docs/.
How does ScoriaDB compare to BadgerDB?
ScoriaDB has MVCC, Column Families, lock‑free skip list, Unified MMap, built‑in gRPC/REST, and is 7× faster on reads.
What is Group Commit?
Group Commit buffers writes and performs a single fsync for a batch (every 10ms). 6.4× faster writes.
What is Unified MMap?
A single mmap region replacing separate VLog and WAL. 0 syscalls per write, 0 allocations in hot path. Dynamic extension on overflow.
What is lock‑free skip list?
A concurrent data structure without mutexes. Uses CAS operations for atomic insertion. Provides +400% write throughput for small keys.
Does zero‑copy work?
Yes — since v0.3.0, VLog reads are zero‑copy. Large values are returned as slices pointing directly to mmap memory. Read speed improved by **+487%** for 4KB values.
What are the system requirements?
Any platform supported by Go 1.23+. ~15 MB binary, no dependencies.
Can I use ScoriaDB on ARM (Raspberry Pi)?
Yes — pure Go works on all architectures (amd64, arm64, arm, etc.).
What is the license?
Apache License 2.0 — free for commercial and personal use.

⭐ Support the Project

  • Star the repository on GitHub.
  • 🐛 Report bugs via Issues.
  • 💻 Submit pull requests.
  • 📣 Share the project in your community.


Solid as stone. Light as ash.

github.com/f4ga/ScoriaDB

Documentation

About

встраиваемое хранилище пар ключ-значение, LSM-tree, SSTable, Bloom, WAL

Resources

License

Contributing

Stars

9 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors