# Netabase Store
A type-safe, multi-backend key-value storage library for Rust with support for native (Sled, Redb) and WASM (IndexedDB) environments.
# This crate is still in early development and will change frequently as it stabalises. It is not advised to use this in a production environment until it stabalises.
## Features
### Current Features
- **Multi-Backend Support**:
- **Sled**: High-performance embedded database for native platforms
- **Redb**: Memory-efficient embedded database with ACID guarantees
- **IndexedDB**: Browser-based storage for WASM applications
- **Type-Safe Schema Definition**:
- Derive macros for automatic schema generation
- Primary and secondary key support
- Compile-time type checking for all database operations
- **Cross-Platform**:
- Unified API across native and WASM targets
- Feature flags for platform-specific backends
- **Zero-Copy Deserialization**: Efficient data access with minimal overhead
- **Secondary Key Indexing**: Fast lookups using secondary keys
- **Iterators**: Efficient iteration over stored data
- **Benchmarking**: Comprehensive benchmarks for performance analysis
- **libp2p Integration**: Optional record store for distributed systems (via `record-store` feature)
### TODO for 1.0.0
- [ ] **Default Profiles/Modes**:
- Simple mode: Pre-configured for common use cases with sensible defaults
- Performance mode: Optimized for high-throughput applications
- Compact mode: Optimized for minimal storage footprint
- [ ] **Migration Tools**:
- Schema migration utilities
- Data import/export functionality
- Backend conversion tools
- [ ] **Query Builder**:
- Fluent API for complex queries
- Compound secondary key queries
- Range queries on ordered keys
- [ ] **Transaction Support**:
- Multi-operation ACID transactions
- Batch operations for improved performance
- [ ] **Async API**:
- Fully async/await compatible operations
- Non-blocking I/O for all backends
- [ ] **Compression**:
- Optional transparent compression
- Configurable compression algorithms
- [ ] **Encryption**:
- At-rest encryption support
- Transparent encryption/decryption
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
netabase_store = "0.0.1"
netabase_deps = "0.0.1"
# Required dependencies for macros to work
bincode = { version = "2.0", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
strum = { version = "0.27.2", features = ["derive"] }
derive_more = { version = "2.0.1", features = ["from", "try_into", "into"] }
anyhow = "1.0" # For error handling
# For WASM
# netabase_store = { version = "0.0.1", features = ["wasm"] }
# netabase_deps = "0.0.1"
```
**Why so many dependencies?** The procedural macros generate code that uses these crates. Due to Rust's macro hygiene rules, they must be available in your dependency list.
## Quick Start
### Define Your Schema
```rust
use netabase_store::netabase_definition_module;
use netabase_store::traits::model::NetabaseModelTrait;
#[netabase_definition_module(BlogDefinition, BlogKeys)]
pub mod blog_schema {
use netabase_store::{NetabaseModel, netabase};
#[derive(NetabaseModel, bincode::Encode, bincode::Decode, Clone, Debug)]
#[netabase(BlogDefinition)]
pub struct User {
#[primary_key]
pub id: u64,
pub username: String,
#[secondary_key]
pub email: String,
}
#[derive(NetabaseModel, bincode::Encode, bincode::Decode, Clone, Debug)]
#[netabase(BlogDefinition)]
pub struct Post {
#[primary_key]
pub id: u64,
pub title: String,
pub content: String,
#[secondary_key]
pub author_id: u64,
}
}
use blog_schema::*;
```
### Use with Sled (Native)
```rust
use netabase_store::databases::sled_store::SledStore;
fn main() -> anyhow::Result<()> {
// Create a temporary store (or use ::new("path") for persistent)
let store = SledStore::<BlogDefinition>::temp()?;
// Open a tree for users
let user_tree = store.open_tree::<User>();
// Insert a user
let user = User {
id: 1,
username: "alice".to_string(),
email: "alice@example.com".to_string(),
};
user_tree.put(user.clone())?;
// Get by primary key
let retrieved = user_tree.get(user.primary_key())?.unwrap();
assert_eq!(retrieved.username, "alice");
// Query by secondary key
let users_by_email = user_tree.get_by_secondary_key(
user.secondary_keys().first().unwrap().clone()
)?;
assert_eq!(users_by_email.len(), 1);
// Iterate over all users (iter() returns Result tuples)
for result in user_tree.iter() {
let (_key, user) = result?;
println!("User: {} - {}", user.username, user.email);
}
Ok(())
}
```
### Use with Redb (Native)
```rust
use netabase_store::databases::redb_store::RedbStore;
fn main() -> anyhow::Result<()> {
// Create a store (or use ::temp() for temporary)
let store = RedbStore::<BlogDefinition>::new("my_database.redb")?;
// API is identical to SledStore
let user_tree = store.open_tree::<User>();
let user = User {
id: 2,
username: "bob".to_string(),
email: "bob@example.com".to_string(),
};
user_tree.put(user)?;
Ok(())
}
```
### Use with IndexedDB (WASM)
```rust
#[cfg(target_arch = "wasm32")]
use netabase_store::databases::indexed_db::IndexedDbStore;
#[cfg(target_arch = "wasm32")]
async fn wasm_example() -> Result<(), Box<dyn std::error::Error>> {
// Create a store
let store = IndexedDbStore::<BlogDefinition>::new("my_database").await?;
// API is identical to native backends
let user_tree = store.open_tree::<User>();
let user = User {
id: "user789".to_string(),
username: "charlie".to_string(),
email: "charlie@example.com".to_string(),
age: 28,
};
user_tree.put(user).await?;
Ok(())
}
```
## Advanced Usage
### Secondary Keys
Secondary keys enable efficient lookups on non-primary fields:
```rust
// Define a model with secondary keys
#[derive(NetabaseModel, Clone, bincode::Encode, bincode::Decode)]
#[netabase(BlogDefinition)]
pub struct Article {
#[primary_key]
pub id: u64,
pub title: String,
#[secondary_key]
pub category: String,
#[secondary_key]
pub published: bool,
}
// Query by secondary key
let published_articles = article_tree
.get_by_secondary_key(ArticleSecondaryKeys::Published(PublishedSecondaryKey(true)))?;
let tech_articles = article_tree
.get_by_secondary_key(ArticleSecondaryKeys::Category(CategorySecondaryKey("tech".to_string())))?;
```
### Multiple Models in One Store
```rust
let store = SledStore::<BlogDefinition>::new("blog_db")?;
// Different trees for different models
let user_tree = store.open_tree::<User>();
let post_tree = store.open_tree::<Post>();
// Each tree is independent but shares the same underlying database
user_tree.put(user)?;
post_tree.put(post)?;
```
## Benchmarks
Run benchmarks to compare backend performance:
```bash
# Sled benchmarks
cargo bench --bench sled_wrapper_overhead
# Redb benchmarks
cargo bench --bench redb_wrapper_overhead
```
Benchmark categories:
- Insert performance
- Get performance
- Iteration performance
- Secondary key lookup performance
## Architecture
### Backend Abstraction
Each backend implements a common set of operations:
- `put(model)`: Insert or update a model
- `get(primary_key)`: Retrieve a model by primary key
- `remove(primary_key)`: Delete a model
- `iter()`: Iterate over all models
- `get_by_secondary_key(key)`: Query by secondary key
- `len()`: Get count of stored models
- `is_empty()`: Check if store is empty
- `clear()`: Remove all models
### Type Safety
The library uses Rust's type system to ensure:
- Keys match their models
- Secondary keys exist for the model
- Backend stores only hold their defined models
- Compile-time verification of all operations
## Feature Flags
- `native` (default): Enable sled and redb backends
- `wasm`: Enable IndexedDB backend
- `libp2p`: Enable libp2p integration
- `record-store`: Enable record store for distributed systems (requires `libp2p`)
## Examples
See the `examples/` directory for complete examples:
- `basic_store.rs`: Basic CRUD operations
- More examples coming in 1.0.0
## Testing
```bash
# Run all tests
cargo test --all-features
# Run native tests only
cargo test --features native
# Run WASM tests (requires wasm-pack)
wasm-pack test --node --features wasm
```
## Performance
Netabase Store is designed for high performance:
- Minimal overhead over raw backend operations (typically <5%)
- Zero-copy deserialization where possible
- Efficient secondary key indexing
- Batch operation support (coming in 1.0.0)
### Performance Considerations
**Abstraction Overhead**: While netabase_store provides a powerful type-safe abstraction layer over multiple database backends, this abstraction does come with some overhead. The cost primarily comes from:
- Type conversions between models and the underlying storage format
- The `RecordStore` trait implementation which adds indirection
- Automatic secondary key indexing which requires additional storage operations
For most applications, this overhead is minimal (typically <5-10%) and well worth the benefits of type safety, portability, and developer ergonomics. However, for extremely performance-critical applications that need to squeeze out every last bit of performance, direct use of the underlying backends (Sled, Redb) without the abstraction layer may be preferred.
**Future Optimizations**: We plan to reduce this overhead in future releases through:
- **Direct backend integration for Redb**: Currently in research phase. The challenge is maintaining the `RecordStore` trait abstraction while allowing for more direct access patterns.
- **Compile-time optimization**: Leveraging Rust's zero-cost abstractions more aggressively
- **Lazy secondary key updates**: Optional deferred indexing for write-heavy workloads
- **Custom serialization strategies**: Per-backend optimized serialization paths
### Distributed Systems Support
**P2P Networking Plans**: We are actively developing enhanced support for decentralized peer-to-peer applications:
- **Configurable profiles and modes**: Simple presets for different use cases (local-only, DHT-backed, full replication, etc.)
- **Network protocols abstraction**: Making it easier to use netabase_store over libp2p, WebRTC, or other transport layers
- **Distributed RecordStore**: Currently in development - will provide automatic record distribution and discovery across peers
- **Conflict resolution strategies**: Pluggable CRDT and last-write-wins implementations for eventual consistency
The vision is to make it trivial to build distributed applications where your local database automatically synchronizes with peers without complex manual synchronization logic.
## License
This project is licensed under the GNU Apache License - see the LICENSE file for details.
## Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines (coming in 1.0.0).
## Links
- [Netabase (networking layer)](../netabase)
- [GDELT Fetcher](../gdelt_fetcher)
- [Example Usage](../test_netabase)