Azoth Storage Subsystem
Azoth is a high-performance embedded database for state management and event sourcing. It combines fast transactional writes, append-only event logs, queryable SQL projections, and deterministic backupsβmaking it ideal for applications requiring ACID guarantees with event sourcing, including blockchain systems, game servers, financial applications, and any system needing reliable state management with audit trails.
Features
- π TEE-Safe: Designed for trusted execution environments with deterministic behavior
- β‘ High Performance: 10K+ tx/sec writes, 1M+ reads/sec
- π Stripe Locking: Parallel preflight validation with per-key locking
- π¦ Event Sourcing: Append-only event log with ACID guarantees
- π SQL Projections: Queryable views derived from events (SQLite)
- πΎ Deterministic Backup: Pausable ingestion with sealed snapshots
- π― Atomic Transactions: State + events committed atomically
- π Batch Processing: 979K+ events/sec with batching
- π Dead Letter Queue: Failed event recovery and replay
Quick Start
use *;
use ;
// Open database
let db = open?;
// Execute a transaction with preflight validation
new
.require
.execute?;
// Run projector to update SQL views
db.projector.run_once?;
Architecture
ββββββββββββββββββββ
β Transaction β Lock-based preflight validation
βββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Canonical Store β State: LMDB (transactional KV)
β (LMDB) β Events: Append-only log
ββββββββββ¬βββββββββ
β events
βΌ
βββββββββββββββββββ
β Projector β Pull mode, batching, error handling
ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β SQL Projection β SQLite with cursor tracking
β (SQLite) β + Dead Letter Queue
βββββββββββββββββββ
Key Concepts
Simplified API
Azoth provides a clean, semantic API:
// Reading state
let value = ctx.get?; // Get typed value
let opt = ctx.get_opt?; // Optional get
let exists = ctx.exists?; // Check existence
// Writing state
ctx.set?; // Set value
ctx.delete?; // Delete key
ctx.update?;
// Logging events
ctx.log?; // Structured event
ctx.log_many?; // Multiple events
ctx.log_bytes?; // Raw bytes
Three-Phase Transactions
- Preflight: Validate constraints with stripe locks (parallel for non-conflicting keys)
- State Update: Commit to LMDB (single-writer, fast)
- Event Append: Write to log (sequential, very fast)
new
// Phase 1: Preflight with stripe locks
.require_exists
.require_min
// Phase 2 & 3: State updates + event logging
.execute?;
Event Processing with Error Handling
use ;
let processor = builder
.with_handler
.with_error_strategy
.with_dead_letter_queue
.build;
processor.run.await?;
Error strategies:
- FailFast: Stop on first error
- LogAndSkip: Log error and continue
- DeadLetterQueue: Store failed events for replay
- RetryWithBackoff: Exponential backoff retry
- Custom: User-defined error handling
Performance
Benchmark Results (Single-threaded, Release Build)
| Operation | Throughput | Latency |
|---|---|---|
| State Writes | 11,243 tx/sec | 89ΞΌs |
| Event Appends | 13,092 events/sec | 76ΞΌs |
| Batch Appends | 979,323 events/sec | 1ΞΌs |
| Atomic (State + Event) | 10,266 tx/sec | 97ΞΌs |
| Transaction API | 8,849 tx/sec | 113ΞΌs |
| State Reads | 977,975 reads/sec | 1ΞΌs |
| Event Iteration | 1,141,680 events/sec | 0.9ΞΌs |
Key Insights:
- Batch processing provides 75x speedup over individual appends
- Read performance is exceptional at nearly 1M reads/sec
- Single transactions maintain ~10K tx/sec with full ACID guarantees
- Event iteration is blazing fast for projector catch-up
Run benchmarks:
Crates
| Crate | Purpose | LOC |
|---|---|---|
azoth |
Unified API and utilities | 2000+ |
azoth-core |
Traits, types, errors | 400 |
azoth-lmdb |
LMDB canonical store | 800 |
azoth-sqlite |
SQLite projection store | 600 |
azoth-file-log |
File-based event log (future) | 600 |
azoth-projector |
Event processor | 500 |
Examples
See the crates/azoth/examples/ directory:
basic_usage.rs- Simple CRUD operations with TypedValuelambda_require.rs- Lambda-based preflight validationevent_handlers.rs- Custom event handler implementationevent_processor_builder.rs- Event processing with error handlingcustom_errors.rs- Custom business logic errorscontinuous_processor.rs- Long-running event processor
Testing
# Run all tests (52 passing β)
# Run integration tests
# Run stress tests
# Run doctests
Dead Letter Queue
Failed events are automatically stored in the DLQ for later analysis:
// Setup DLQ
let dlq = new;
let processor = builder
.with_error_strategy
.with_dead_letter_queue
.build;
// Later, inspect and retry failed events
let failed = dlq.list?;
for event in failed
Typed Values
Azoth supports rich typed values:
// Integer types
I64
U256
// Collections
Set
Array
// Raw bytes
Bytes
// Strings
String
Backup & Restore
Deterministic backup with sealed snapshots:
# Backup (automatically pauses ingestion, seals, and resumes)
);
# Restore
Backup includes:
- Sealed canonical state (LMDB)
- SQL projection with cursor
- Manifest with sealed event ID
Status
- All core features implemented
- Comprehensive test coverage (52 tests passing)
- Clean, semantic API
- Benchmarked performance
- Dead letter queue for reliability
- Error handling strategies
- Deterministic backup/restore
Future Work
- Multiple concurrent projections
- Automatic DLQ replay
- Metrics and observability
- Circuit breaker pattern
- Incremental snapshots
License
MIT OR Apache-2.0