Session Management API Documentation
Session Management API Documentation
Scope — read this first. The session and lock primitives described on this page are internal plumbing, not a supported public embedded API. They exist because the PostgreSQL/MySQL wire server needs per-connection sessions; they are not covered by any compatibility promise, and they are not the recommended way to get independent transactions from embedded code.
On the plain embedded API, an
EmbeddedDatabasehandle has one process-global transaction slot shared across its clones, so independent overlapping explicit transactions are not safe. Independent transactions are supported today over separate PostgreSQL or MySQL wire connections.For what is actually supported, and what to do instead, see Concurrency and transactions.
Version note: this page was written against Nano 3.1.0 and has not been re-verified against the current release (4.31.1). Treat the signatures below as an architecture note, not as a current API contract.
Table of Contents
- Overview
- SessionManager API
- LockManager API
- Transaction API Updates
- DumpManager API
- Type Definitions
- Error Types
- Code Examples
- Thread Safety Guarantees
- Concurrency Patterns
- Integration Examples
Overview
HeliosDB Nano v3.1.0 introduces comprehensive session management APIs for building multi-user applications with ACID transactions. The API provides:
- SessionManager: Multi-user session coordination
- LockManager: Concurrency control with deadlock detection
- Transaction API: Enhanced transaction isolation levels
- DumpManager: Programmatic dump/restore operations
Module Structure:
heliosdb_nano::├── session::│ ├── SessionManager # Session lifecycle management│ ├── Session # Individual session state│ ├── SessionId # Session identifiers│ └── IsolationLevel # Transaction isolation levels├── storage::│ ├── LockManager # Lock acquisition and deadlock detection│ ├── LockMode # Lock types (Shared, Exclusive, Update)│ ├── DumpManager # Dump/restore operations│ └── DirtyTracker # Dirty state tracking└── EmbeddedDatabase # Main database interfaceMinimum Rust Version: 1.75+
SessionManager API
Module
use heliosdb_nano::session::{SessionManager, Session, SessionId, IsolationLevel};Struct Definition
pub struct SessionManager { // Internal fields (private)}
impl SessionManager { pub fn new(config: SessionConfig) -> Self; pub fn create_session( &self, user: &User, isolation: IsolationLevel ) -> Result<SessionId, Error>; pub fn destroy_session(&self, session_id: SessionId) -> Result<(), Error>; pub fn begin_transaction(&self, session_id: SessionId) -> Result<TransactionId, Error>; pub fn commit_transaction(&self, session_id: SessionId) -> Result<(), Error>; pub fn rollback_transaction(&self, session_id: SessionId) -> Result<(), Error>; pub fn get_session(&self, session_id: SessionId) -> Result<Arc<RwLock<Session>>, Error>; pub fn list_active_sessions(&self) -> Vec<SessionId>; pub fn session_count(&self) -> usize; pub fn session_count_for_user(&self, user_id: UserId) -> usize;}Methods
SessionManager::new
Create a new session manager instance.
Signature:
pub fn new(config: SessionConfig) -> SelfParameters:
config:SessionConfig- Configuration for session management
Example:
use heliosdb_nano::session::{SessionManager, SessionConfig};
let config = SessionConfig { timeout_secs: 3600, max_sessions_per_user: 10, default_isolation: IsolationLevel::ReadCommitted,};
let manager = SessionManager::new(config);create_session
Create a new user session with specified isolation level.
Signature:
pub fn create_session( &self, user: &User, isolation: IsolationLevel) -> Result<SessionId, Error>Parameters:
user:&User- User credentials and metadataisolation:IsolationLevel- Transaction isolation level
Returns: Result<SessionId, Error>
Errors:
Error::AuthenticationFailed- User authentication failedError::QuotaExceeded- Session limit exceeded for user
Example:
use heliosdb_nano::session::{User, IsolationLevel};
let user = User::new("alice", "password123");let session_id = manager.create_session(&user, IsolationLevel::ReadCommitted)?;println!("Created session: {}", session_id);begin_transaction
Begin a new transaction within a session.
Signature:
pub fn begin_transaction(&self, session_id: SessionId) -> Result<TransactionId, Error>Parameters:
session_id:SessionId- Active session identifier
Returns: Result<TransactionId, Error>
Errors:
Error::SessionNotFound- Invalid session IDError::TransactionAlreadyActive- Transaction already in progress
Example:
let txn_id = manager.begin_transaction(session_id)?;// Execute queries within transaction...manager.commit_transaction(session_id)?;commit_transaction
Commit the active transaction in a session.
Signature:
pub fn commit_transaction(&self, session_id: SessionId) -> Result<(), Error>Errors:
Error::NoActiveTransaction- No transaction to commitError::SerializationFailure- Serializable isolation conflict
Example:
manager.begin_transaction(session_id)?;// Perform database operations...match manager.commit_transaction(session_id) { Ok(()) => println!("Transaction committed"), Err(Error::SerializationFailure(msg)) => { // Retry transaction println!("Serialization conflict: {}", msg); } Err(e) => return Err(e),}list_active_sessions
Get list of all active session IDs.
Signature:
pub fn list_active_sessions(&self) -> Vec<SessionId>Returns: Vec<SessionId> - All active sessions
Example:
let sessions = manager.list_active_sessions();println!("Active sessions: {}", sessions.len());for session_id in sessions { println!(" - Session {}", session_id);}LockManager API
Module
use heliosdb_nano::storage::{LockManager, LockMode, Key};Struct Definition
pub struct LockManager { // Internal fields (private)}
impl LockManager { pub fn new(config: LockConfig) -> Self; pub fn acquire_lock( &self, txn_id: TransactionId, key: Key, mode: LockMode ) -> Result<(), Error>; pub fn release_lock( &self, txn_id: TransactionId, key: Key ) -> Result<(), Error>; pub fn release_all_locks(&self, txn_id: TransactionId) -> Result<(), Error>; pub fn has_lock( &self, txn_id: TransactionId, key: &Key, mode: LockMode ) -> bool; pub fn detect_deadlock(&self, txn_id: TransactionId) -> Result<bool, Error>;}Enums
LockMode
#[derive(Clone, Copy, PartialEq, Eq, Debug)]pub enum LockMode { Shared, // Read lock (S) Exclusive, // Write lock (X) Update, // Intent to update (U)}
impl LockMode { pub fn is_compatible(&self, other: LockMode) -> bool;}Lock Compatibility Matrix:
S X U S ✓ ✗ ✓ X ✗ ✗ ✗ U ✓ ✗ ✗Methods
acquire_lock
Acquire a lock on a key with specified mode.
Signature:
pub fn acquire_lock( &self, txn_id: TransactionId, key: Key, mode: LockMode) -> Result<(), Error>Parameters:
txn_id:TransactionId- Transaction acquiring lockkey:Key- Resource to lock (table, row, page)mode:LockMode- Lock type
Errors:
Error::LockTimeout- Timeout waiting for lockError::Deadlock- Deadlock detected
Example:
use heliosdb_nano::storage::{LockManager, LockMode, Key};
let lock_manager = LockManager::new(LockConfig::default());
// Acquire shared lock for readlet key = Key::row("users", 123);lock_manager.acquire_lock(txn_id, key.clone(), LockMode::Shared)?;
// Read data...
// Upgrade to exclusive lock for writelock_manager.acquire_lock(txn_id, key.clone(), LockMode::Exclusive)?;
// Write data...
// Release lockslock_manager.release_all_locks(txn_id)?;detect_deadlock
Check if transaction is involved in a deadlock.
Signature:
pub fn detect_deadlock(&self, txn_id: TransactionId) -> Result<bool, Error>Returns: Result<bool, Error> - true if deadlock detected
Example:
if lock_manager.detect_deadlock(txn_id)? { println!("Deadlock detected, aborting transaction"); manager.rollback_transaction(session_id)?;}Transaction API Updates
Enhanced Transaction Methods
use heliosdb_nano::storage::Transaction;
impl Transaction { pub fn new( db: Arc<StorageEngine>, snapshot: Snapshot, lock_manager: Arc<LockManager> ) -> Self;
pub fn set_isolation_level(&mut self, level: IsolationLevel); pub fn get_isolation_level(&self) -> IsolationLevel; pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>, Error>; pub fn put(&mut self, key: Vec<u8>, value: Vec<u8>) -> Result<(), Error>; pub fn delete(&mut self, key: &[u8]) -> Result<(), Error>; pub fn commit(self) -> Result<(), Error>; pub fn rollback(self) -> Result<(), Error>;}Isolation Levels
#[derive(Clone, Copy, PartialEq, Eq, Debug)]pub enum IsolationLevel { ReadCommitted, RepeatableRead, Serializable,}Example:
use heliosdb_nano::storage::{Transaction, IsolationLevel};
let mut txn = Transaction::new(db.clone(), snapshot, lock_manager.clone());txn.set_isolation_level(IsolationLevel::Serializable);
txn.put(b"key".to_vec(), b"value".to_vec())?;txn.commit()?;DumpManager API
Module
use heliosdb_nano::storage::dump::{DumpManager, DumpOptions, RestoreOptions};Struct Definition
pub struct DumpManager { // Internal fields (private)}
impl DumpManager { pub fn new(storage: Arc<StorageEngine>) -> Self; pub fn dump(&self, options: DumpOptions) -> Result<DumpReport, Error>; pub fn restore(&self, options: RestoreOptions) -> Result<RestoreReport, Error>; pub fn list_dumps(&self) -> Result<Vec<DumpMetadata>, Error>; pub fn verify_dump(&self, path: &Path) -> Result<bool, Error>;}Options Structs
pub struct DumpOptions { pub output_path: PathBuf, pub mode: DumpMode, pub compress: bool, pub compression_type: CompressionType, pub tables: Option<Vec<String>>, pub append: bool,}
pub struct RestoreOptions { pub input_path: PathBuf, pub tables: Option<Vec<String>>, pub mode: RestoreMode, pub on_conflict: ConflictResolution,}
#[derive(Clone, Copy)]pub enum DumpMode { Full, Incremental,}
#[derive(Clone, Copy)]pub enum CompressionType { None, Lz4, Zstd,}
#[derive(Clone, Copy)]pub enum RestoreMode { Clean, Append,}
#[derive(Clone, Copy)]pub enum ConflictResolution { Error, Skip, Update,}Methods
dump
Dump database to file.
Signature:
pub fn dump(&self, options: DumpOptions) -> Result<DumpReport, Error>Example:
use heliosdb_nano::storage::dump::{DumpManager, DumpOptions, DumpMode, CompressionType};use std::path::PathBuf;
let dump_manager = DumpManager::new(storage.clone());
let options = DumpOptions { output_path: PathBuf::from("/backups/db.heliodump"), mode: DumpMode::Full, compress: true, compression_type: CompressionType::Zstd, tables: None, // All tables append: false,};
let report = dump_manager.dump(options)?;println!("Dumped {} tables, {} rows, {} bytes", report.tables_dumped, report.rows_dumped, report.bytes_written);restore
Restore database from dump file.
Signature:
pub fn restore(&self, options: RestoreOptions) -> Result<RestoreReport, Error>Example:
use heliosdb_nano::storage::dump::{RestoreOptions, RestoreMode, ConflictResolution};
let options = RestoreOptions { input_path: PathBuf::from("/backups/db.heliodump"), tables: None, // All tables mode: RestoreMode::Clean, on_conflict: ConflictResolution::Error,};
let report = dump_manager.restore(options)?;println!("Restored {} tables, {} rows", report.tables_restored, report.rows_restored);Type Definitions
SessionId
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]pub struct SessionId(u64);
impl SessionId { pub fn new() -> Self; pub fn as_u64(&self) -> u64;}UserId
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]pub struct UserId(u64);TransactionId
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]pub struct TransactionId(u64);
impl TransactionId { pub fn new() -> Self; pub fn as_u64(&self) -> u64;}User
pub struct User { pub id: UserId, pub username: String, pub roles: Vec<String>,}
impl User { pub fn new(username: impl Into<String>, password: impl AsRef<str>) -> Self; pub fn has_role(&self, role: &str) -> bool;}Key
pub enum Key { Table(String), Row(String, RowId), Page(String, PageId),}
impl Key { pub fn table(name: impl Into<String>) -> Self; pub fn row(table: impl Into<String>, row_id: RowId) -> Self; pub fn page(table: impl Into<String>, page_id: PageId) -> Self;}Error Types
Error Enum
#[derive(Debug)]pub enum Error { // Session errors SessionNotFound(SessionId), SessionExpired(SessionId), QuotaExceeded(String),
// Transaction errors TransactionAlreadyActive, NoActiveTransaction, SerializationFailure(String),
// Lock errors LockTimeout, Deadlock(String),
// Dump/restore errors DumpError(String), RestoreError(String), VersionIncompatible(String),
// General errors AuthenticationFailed, AuthorizationFailed, Io(std::io::Error), Other(String),}
impl std::fmt::Display for Error { ... }impl std::error::Error for Error { ... }Result Type
pub type Result<T> = std::result::Result<T, Error>;Code Examples
Example 1: Multi-User Application
use heliosdb_nano::{EmbeddedDatabase, session::*};use std::sync::Arc;use std::thread;
fn main() -> Result<(), Box<dyn std::error::Error>> { // Create in-memory database let db = Arc::new(EmbeddedDatabase::new_in_memory()?);
// Create session manager let manager = Arc::new(SessionManager::new(SessionConfig::default()));
// Setup schema db.execute("CREATE TABLE accounts (id INT PRIMARY KEY, balance INT)")?; db.execute("INSERT INTO accounts VALUES (1, 1000)")?;
// Spawn multiple user sessions let mut handles = vec![];
for i in 0..10 { let db = db.clone(); let manager = manager.clone();
let handle = thread::spawn(move || { let user = User::new(format!("user{}", i), "password"); let session_id = manager.create_session(&user, IsolationLevel::ReadCommitted)?;
// Perform transaction manager.begin_transaction(session_id)?;
let balance: i32 = db.query_one( "SELECT balance FROM accounts WHERE id = 1", &[] )?;
db.execute(&format!( "UPDATE accounts SET balance = {} WHERE id = 1", balance - 10 ))?;
manager.commit_transaction(session_id)?; manager.destroy_session(session_id)?;
Ok::<_, Box<dyn std::error::Error>>(()) });
handles.push(handle); }
// Wait for all sessions for handle in handles { handle.join().unwrap()?; }
// Verify final balance let final_balance: i32 = db.query_one( "SELECT balance FROM accounts WHERE id = 1", &[] )?;
println!("Final balance: {}", final_balance); // Should be 900
Ok(())}Example 2: Serializable Transaction with Retry
use heliosdb_nano::session::*;
fn transfer_with_retry( manager: &SessionManager, db: &EmbeddedDatabase, session_id: SessionId, from_id: i32, to_id: i32, amount: i32,) -> Result<(), Error> { let max_retries = 3;
for attempt in 0..max_retries { manager.begin_transaction(session_id)?;
match perform_transfer(db, from_id, to_id, amount) { Ok(()) => { match manager.commit_transaction(session_id) { Ok(()) => return Ok(()), Err(Error::SerializationFailure(msg)) if attempt < max_retries - 1 => { println!("Serialization conflict (attempt {}), retrying...", attempt + 1); std::thread::sleep(std::time::Duration::from_millis(100 * (2_u64.pow(attempt)))); continue; } Err(e) => return Err(e), } } Err(e) => { manager.rollback_transaction(session_id)?; return Err(e); } } }
Err(Error::SerializationFailure("Max retries exceeded".to_string()))}
fn perform_transfer(db: &EmbeddedDatabase, from_id: i32, to_id: i32, amount: i32) -> Result<(), Error> { // Debit from account db.execute(&format!( "UPDATE accounts SET balance = balance - {} WHERE id = {}", amount, from_id ))?;
// Credit to account db.execute(&format!( "UPDATE accounts SET balance = balance + {} WHERE id = {}", amount, to_id ))?;
Ok(())}Example 3: Programmatic Dump/Restore
use heliosdb_nano::storage::dump::*;use std::path::PathBuf;
fn backup_and_restore() -> Result<(), Error> { let db = EmbeddedDatabase::new_in_memory()?; let dump_manager = DumpManager::new(db.storage_engine());
// Create test data db.execute("CREATE TABLE users (id INT PRIMARY KEY, name TEXT)")?; db.execute("INSERT INTO users VALUES (1, 'Alice'), (2, 'Bob')")?;
// Dump database let dump_opts = DumpOptions { output_path: PathBuf::from("/tmp/backup.heliodump"), mode: DumpMode::Full, compress: true, compression_type: CompressionType::Zstd, tables: None, append: false, };
let dump_report = dump_manager.dump(dump_opts)?; println!("Dump complete: {} rows, {} bytes", dump_report.rows_dumped, dump_report.bytes_written );
// Verify dump let valid = dump_manager.verify_dump(&PathBuf::from("/tmp/backup.heliodump"))?; assert!(valid, "Dump verification failed");
// Create new database let db2 = EmbeddedDatabase::new_in_memory()?; let restore_manager = DumpManager::new(db2.storage_engine());
// Restore from dump let restore_opts = RestoreOptions { input_path: PathBuf::from("/tmp/backup.heliodump"), tables: None, mode: RestoreMode::Clean, on_conflict: ConflictResolution::Error, };
let restore_report = restore_manager.restore(restore_opts)?; println!("Restore complete: {} tables, {} rows", restore_report.tables_restored, restore_report.rows_restored );
// Verify data let count: i64 = db2.query_one("SELECT COUNT(*) FROM users", &[])?; assert_eq!(count, 2);
Ok(())}Thread Safety Guarantees
All session management APIs are thread-safe and can be safely shared across threads using Arc:
use std::sync::Arc;
let manager = Arc::new(SessionManager::new(config));let lock_manager = Arc::new(LockManager::new(lock_config));
// Share across threadslet manager_clone = manager.clone();std::thread::spawn(move || { let session_id = manager_clone.create_session(&user, IsolationLevel::ReadCommitted).unwrap(); // ...});Internal Synchronization:
SessionManager: UsesDashMapfor lock-free concurrent accessLockManager: UsesDashMapand atomic operationsDumpManager: Uses read-write locks for safe concurrent dumpsTransaction: Isolated per-session, no cross-session contention
Concurrency Patterns
Pattern 1: Connection Pool Integration
use r2d2::{Pool, PooledConnection};use heliosdb_nano::EmbeddedDatabase;
type DbPool = Pool<EmbeddedDatabase>;
fn create_pool() -> Result<DbPool, Error> { let db = EmbeddedDatabase::new_in_memory()?; Ok(Pool::builder() .max_size(20) .build(db)?)}
fn get_connection(pool: &DbPool) -> Result<PooledConnection<EmbeddedDatabase>, Error> { pool.get().map_err(|e| Error::Other(e.to_string()))}Pattern 2: Async Session Management
use tokio::sync::Mutex;use std::sync::Arc;
#[tokio::main]async fn main() -> Result<(), Error> { let manager = Arc::new(Mutex::new(SessionManager::new(config)));
let handles: Vec<_> = (0..100).map(|i| { let manager = manager.clone(); tokio::spawn(async move { let manager = manager.lock().await; let user = User::new(format!("user{}", i), "pass"); manager.create_session(&user, IsolationLevel::ReadCommitted) }) }).collect();
for handle in handles { handle.await??; }
Ok(())}Integration Examples
Integration with Web Framework (Axum)
use axum::{ extract::{State, Json}, routing::post, Router,};use std::sync::Arc;
struct AppState { db: Arc<EmbeddedDatabase>, session_manager: Arc<SessionManager>,}
async fn create_user( State(state): State<Arc<AppState>>, Json(payload): Json<CreateUserRequest>,) -> Result<Json<CreateUserResponse>, AppError> { let user = User::new("api_user", "secret"); let session_id = state.session_manager.create_session( &user, IsolationLevel::ReadCommitted )?;
state.session_manager.begin_transaction(session_id)?;
state.db.execute(&format!( "INSERT INTO users (name, email) VALUES ('{}', '{}')", payload.name, payload.email ))?;
state.session_manager.commit_transaction(session_id)?; state.session_manager.destroy_session(session_id)?;
Ok(Json(CreateUserResponse { success: true }))}
#[tokio::main]async fn main() { let db = Arc::new(EmbeddedDatabase::new_in_memory().unwrap()); let session_manager = Arc::new(SessionManager::new(SessionConfig::default()));
let state = Arc::new(AppState { db, session_manager });
let app = Router::new() .route("/users", post(create_user)) .with_state(state);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap();}See Also
- In-Memory Mode Guide - User guide for in-memory operations
- Configuration Reference - Configuration options
- Multi-User Architecture - System design details
- Migration Guide - Migration planning and operational checks
Version: 3.1.0