| Crates.io | kotoba-storage |
| lib.rs | kotoba-storage |
| version | 0.1.22 |
| created_at | 2025-09-14 07:45:33.044822+00 |
| updated_at | 2025-09-19 15:35:28.67748+00 |
| description | Advanced persistent storage layer with MVCC, LSM-Tree, and Merkle DAG |
| homepage | https://github.com/com-junkawasaki/kotoba |
| repository | https://github.com/com-junkawasaki/kotoba |
| max_upload_size | |
| id | 1838440 |
| size | 67,221 |
Advanced persistent storage layer for the Kotoba graph processing system. Implements MVCC (Multi-Version Concurrency Control), LSM-Tree storage, and Merkle DAG for immutable, versioned data management with ACID compliance.
Kotoba Storage serves as the persistence foundation for the entire Kotoba ecosystem, providing:
lsm.rs)// Log-Structured Merge Tree for high-throughput storage
#[derive(Debug)]
pub struct LSMTree {
memtable: RwLock<MemTable>,
levels: Vec<SSTable>,
wal: WriteAheadLog,
}
impl LSMTree {
pub fn new(path: &str) -> Result<Self>;
pub fn put(&self, key: &[u8], value: &[u8]) -> Result<()>;
pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>>;
pub fn delete(&self, key: &[u8]) -> Result<()>;
}
mvcc.rs)// Multi-Version Concurrency Control for transactions
#[derive(Debug)]
pub struct MVCCManager {
active_txs: RwLock<HashMap<TxId, Transaction>>,
snapshots: RwLock<HashMap<u64, Snapshot>>,
}
impl MVCCManager {
pub fn begin_transaction(&self) -> Result<Transaction>;
pub fn commit_transaction(&self, tx: Transaction) -> Result<()>;
pub fn abort_transaction(&self, tx: Transaction) -> Result<()>;
}
merkle.rs)// Immutable, content-addressed data structures
#[derive(Debug)]
pub struct MerkleTree {
nodes: HashMap<ContentHash, MerkleNode>,
root: Option<ContentHash>,
}
impl MerkleTree {
pub fn new() -> Self;
pub fn add_node(&mut self, data: &[u8]) -> ContentHash;
pub fn get_node(&self, hash: &ContentHash) -> Option<&MerkleNode>;
pub fn root_hash(&self) -> String;
}
redis.rs)// Redis integration for caching and real-time features (supports Upstash, Redis Cloud, etc.)
#[derive(Debug, Clone)]
pub struct RedisClient {
url: String,
token: Option<String>,
client: reqwest::Client,
}
impl RedisClient {
pub fn new(url: &str) -> Result<Self>;
pub fn with_token(url: &str, token: &str) -> Result<Self>;
pub async fn get(&self, key: &str) -> Result<Option<String>>;
pub async fn set(&self, key: &str, value: &str, ttl: Option<u64>) -> Result<()>;
pub async fn publish(&self, channel: &str, message: &str) -> Result<()>;
}
Kotoba Storage supports a hybrid approach combining local LSM-Tree storage with Redis for optimal performance:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Application Layer โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Redis Cache Layer (Hot Data) โ
โ - Session storage, real-time features โ
โ - Frequently accessed graph nodes/edges โ
โ - Distributed locks and coordination โ
โ - Supports Upstash, Redis Cloud, ElastiCache, etc. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ LSM-Tree Layer (Cold Data) โ
โ - Persistent storage with ACID compliance โ
โ - Historical data and large datasets โ
โ - Immutable data with Merkle DAG verification โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Merkle DAG Layer โ
โ - Content-addressed immutable storage โ
โ - Cryptographic integrity verification โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
| Metric | Status |
|---|---|
| Compilation | โ Clean (with RocksDB dependency) |
| Tests | โ Comprehensive storage layer tests |
| Documentation | โ Complete API docs |
| Performance | โ LSM-Tree optimization |
| ACID Compliance | โ MVCC transactions |
| Data Integrity | โ Merkle DAG verification |
| Redis Integration | โ Serverless Redis caching (Upstash, Redis Cloud, etc.) |
| Hybrid Architecture | โ Hot/cold data separation |
use kotoba_storage::storage::lsm::LSMTree;
use tempfile::tempdir;
// Create LSM-Tree instance
let temp_dir = tempdir()?;
let lsm = LSMTree::new(temp_dir.path().to_str().unwrap())?;
// Basic operations
lsm.put(b"user:123", b"{\"name\":\"Alice\",\"age\":30}")?;
let user_data = lsm.get(b"user:123")?;
assert_eq!(user_data, Some(b"{\"name\":\"Alice\",\"age\":30}".to_vec()));
use kotoba_storage::storage::mvcc::{MVCCManager, Transaction};
// Create MVCC manager
let mvcc = MVCCManager::new();
// Begin transaction
let mut tx = mvcc.begin_transaction()?;
// Perform operations within transaction
tx.put(b"user:456", b"{\"name\":\"Bob\",\"age\":25}")?;
tx.put(b"user:789", b"{\"name\":\"Charlie\",\"age\":35}")?;
// Commit transaction
let committed_tx = tx.commit();
mvcc.commit_transaction(committed_tx)?;
use kotoba_storage::storage::merkle::MerkleTree;
// Create Merkle tree
let mut tree = MerkleTree::new();
// Add data nodes
let node1_hash = tree.add_node(b"Block 1 data");
let node2_hash = tree.add_node(b"Block 2 data");
// Get root hash for integrity verification
let root_hash = tree.root_hash();
println!("Merkle root: {}", root_hash);
// Verify data integrity
assert!(tree.verify_integrity());
use kotoba_storage::prelude::*;
use kotoba_graph::graph::Graph;
use kotoba_core::types::*;
// Create full storage stack
let temp_dir = tempdir()?;
let lsm = LSMTree::new(temp_dir.path().to_str().unwrap())?;
let mvcc = MVCCManager::new_with_lsm(lsm);
let merkle = MerkleTree::new();
// Store graph data transactionally
let mut tx = mvcc.begin_transaction()?;
// Store vertices
let vertex_data = VertexData {
id: VertexId::new_v4(),
labels: vec!["Person".to_string()],
props: HashMap::new(),
};
let vertex_key = StorageKey::vertex(vertex_data.id);
let vertex_value = StorageValue::Vertex(vertex_data);
tx.put(&vertex_key.0.as_bytes(), &serde_json::to_vec(&vertex_value)?)?;
tx.commit();
use kotoba_storage::redis::RedisClient;
use serde_json;
// Initialize Redis client (works with Upstash, Redis Cloud, etc.)
let redis = RedisClient::with_token(
"https://your-db.upstash.io",
"your-token-here"
)?;
// Or for Redis without token authentication:
// let redis = RedisClient::new("redis://localhost:6379")?;
// Cache frequently accessed graph data
let user_key = "user:alice:profile";
let user_data = serde_json::json!({
"id": "alice",
"name": "Alice Johnson",
"last_login": "2024-01-15T10:30:00Z"
});
// Cache with TTL (1 hour)
redis.set(user_key, &user_data.to_string(), Some(3600)).await?;
// Retrieve cached data
if let Some(cached_data) = redis.get(user_key).await? {
let profile: serde_json::Value = serde_json::from_str(&cached_data)?;
println!("Cached user: {}", profile["name"]);
}
use kotoba_storage::redis::RedisClient;
use kotoba_graph::graph::GraphUpdate;
// Publish graph changes to subscribers
let graph_update = GraphUpdate {
node_id: "user:alice".to_string(),
operation: "update".to_string(),
data: serde_json::json!({"status": "online"}),
};
redis.publish(
"graph-updates",
&serde_json::to_string(&graph_update)?
).await?;
use kotoba_storage::prelude::*;
use kotoba_storage::redis::RedisClient;
// Create hybrid storage manager
let temp_dir = tempdir()?;
let lsm = LSMTree::new(temp_dir.path().to_str().unwrap())?;
let redis = RedisClient::with_token(redis_url, redis_token)?;
let hybrid_storage = HybridStorageManager::new(lsm, redis);
// Hot path: Check cache first, then persistent storage
let user_id = "user:alice";
let cache_key = format!("cache:{}", user_id);
if let Some(cached_data) = hybrid_storage.redis.get(&cache_key).await? {
// Return cached data
serde_json::from_str(&cached_data)?
} else {
// Fetch from LSM-Tree and cache
let key = StorageKey::user(user_id);
let data = hybrid_storage.lsm.get(&key.0.as_bytes())?;
if let Some(data_bytes) = data {
let data_str = String::from_utf8(data_bytes)?;
// Cache for 30 minutes
hybrid_storage.redis.set(&cache_key, &data_str, Some(1800)).await?;
serde_json::from_str(&data_str)?
} else {
None
}
}
use kotoba_storage::redis::RedisClient;
// Session storage for web applications (works with any Redis provider)
#[derive(serde::Serialize, serde::Deserialize)]
struct UserSession {
user_id: String,
token: String,
expires_at: u64,
permissions: Vec<String>,
}
let redis = RedisClient::with_token(
"https://your-redis-provider.com",
"your-token"
)?;
let session_manager = SessionManager::new(redis);
// Store user session
let session = UserSession {
user_id: "alice".to_string(),
token: "jwt-token-here".to_string(),
expires_at: 1640995200, // Unix timestamp
permissions: vec!["read".to_string(), "write".to_string()],
};
let session_key = format!("session:{}", session.user_id);
session_manager.store_session(&session_key, &session, 3600).await?;
// Retrieve and validate session
if let Some(valid_session) = session_manager.get_session::<UserSession>(&session_key).await? {
// Session is valid and not expired
println!("User {} has permissions: {:?}", valid_session.user_id, valid_session.permissions);
}
Kotoba Storage is the persistence foundation:
| Component | Purpose | Integration |
|---|---|---|
kotoba-core |
Required | Types, hashing, serialization |
kotoba-graph |
Required | Graph data persistence |
kotoba-execution |
Required | Transactional query execution |
kotoba-rewrite |
Optional | Transformation persistence |
kotoba-server |
Required | Distributed storage coordination |
| Redis | Optional | Caching, sessions, real-time features (Upstash, Redis Cloud, etc.) |
| Hybrid Storage | Optional | LSM-Tree + Redis for optimal performance |
cargo test -p kotoba-storage
Test Coverage:
LSMTree] - Log-structured merge tree storageMVCCManager] - Multi-version concurrency controlMerkleTree] - Immutable content-addressed dataTransaction] - Transaction with isolationStorageKey] - Typed key generationStorageValue] - Typed value storageMVCCManager::begin_transaction()] - Start new transactionTransaction::put()] / [Transaction::get()] - Key-value operationsTransaction::commit()] / [Transaction::abort()] - Transaction completionMerkleTree::add_node()] - Add immutable dataMerkleTree::get_node()] - Retrieve by content hashMerkleTree::root_hash()] - Get Merkle rootMerkleTree::verify_integrity()] - Cryptographic verificationRedisClient::new()] - Create Redis client (basic auth)RedisClient::with_token()] - Create Redis client with token authRedisClient::get()] - Retrieve cached data by keyRedisClient::set()] - Store data with optional TTLRedisClient::publish()] - Publish messages to channelsRedisClient::subscribe()] - Subscribe to real-time updatesSee the main Kotoba repository for contribution guidelines.
Licensed under MIT OR Apache-2.0. See LICENSE for details.