port ourdb from vlang

This commit is contained in:
Timur Gordon
2025-04-09 11:37:11 +02:00
parent 5c5225c8f7
commit 0eedec9ed0
14 changed files with 3423 additions and 0 deletions

View File

@@ -0,0 +1,199 @@
use ourdb::{OurDB, OurDBConfig, OurDBSetArgs};
use std::path::PathBuf;
use std::time::{Duration, Instant};
fn main() -> Result<(), ourdb::Error> {
// Create a temporary directory for the database
let db_path = std::env::temp_dir().join("ourdb_advanced_example");
std::fs::create_dir_all(&db_path)?;
println!("Creating database at: {}", db_path.display());
// Demonstrate key-value mode (non-incremental)
key_value_mode_example(&db_path)?;
// Demonstrate incremental mode
incremental_mode_example(&db_path)?;
// Demonstrate performance benchmarking
performance_benchmark(&db_path)?;
// Clean up (optional)
if std::env::var("KEEP_DB").is_err() {
std::fs::remove_dir_all(&db_path)?;
println!("Cleaned up database directory");
} else {
println!("Database kept at: {}", db_path.display());
}
Ok(())
}
fn key_value_mode_example(base_path: &PathBuf) -> Result<(), ourdb::Error> {
println!("\n=== Key-Value Mode Example ===");
let db_path = base_path.join("key_value");
std::fs::create_dir_all(&db_path)?;
// Create a new database with key-value mode (non-incremental)
let config = OurDBConfig {
path: db_path,
incremental_mode: false,
file_size: Some(1024 * 1024), // 1MB for testing
keysize: Some(2), // Small key size for demonstration
};
let mut db = OurDB::new(config)?;
// In key-value mode, we must provide IDs explicitly
let custom_ids = [100, 200, 300, 400, 500];
// Store data with custom IDs
for (i, &id) in custom_ids.iter().enumerate() {
let data = format!("Record with custom ID {}", id);
db.set(OurDBSetArgs { id: Some(id), data: data.as_bytes() })?;
println!("Stored record {} with custom ID: {}", i+1, id);
}
// Retrieve data by custom IDs
for &id in &custom_ids {
let retrieved = db.get(id)?;
println!("Retrieved ID {}: {}", id, String::from_utf8_lossy(&retrieved));
}
// Update and track history
let id_to_update = custom_ids[2]; // ID 300
for i in 1..=3 {
let updated_data = format!("Updated record {} (version {})", id_to_update, i);
db.set(OurDBSetArgs { id: Some(id_to_update), data: updated_data.as_bytes() })?;
println!("Updated ID {} (version {})", id_to_update, i);
}
// Get history for the updated record
let history = db.get_history(id_to_update, 5)?;
println!("History for ID {} (most recent first):", id_to_update);
for (i, entry) in history.iter().enumerate() {
println!(" Version {}: {}", i, String::from_utf8_lossy(entry));
}
db.close()?;
println!("Key-value mode example completed");
Ok(())
}
fn incremental_mode_example(base_path: &PathBuf) -> Result<(), ourdb::Error> {
println!("\n=== Incremental Mode Example ===");
let db_path = base_path.join("incremental");
std::fs::create_dir_all(&db_path)?;
// Create a new database with incremental mode
let config = OurDBConfig {
path: db_path,
incremental_mode: true,
file_size: Some(1024 * 1024), // 1MB for testing
keysize: Some(3), // 3-byte keys
};
let mut db = OurDB::new(config)?;
// In incremental mode, IDs are auto-generated
let mut assigned_ids = Vec::new();
// Store multiple records and collect assigned IDs
for i in 1..=5 {
let data = format!("Auto-increment record {}", i);
let id = db.set(OurDBSetArgs { id: None, data: data.as_bytes() })?;
assigned_ids.push(id);
println!("Stored record {} with auto-assigned ID: {}", i, id);
}
// Check next ID
let next_id = db.get_next_id()?;
println!("Next ID to be assigned: {}", next_id);
// Retrieve all records
for &id in &assigned_ids {
let retrieved = db.get(id)?;
println!("Retrieved ID {}: {}", id, String::from_utf8_lossy(&retrieved));
}
db.close()?;
println!("Incremental mode example completed");
Ok(())
}
fn performance_benchmark(base_path: &PathBuf) -> Result<(), ourdb::Error> {
println!("\n=== Performance Benchmark ===");
let db_path = base_path.join("benchmark");
std::fs::create_dir_all(&db_path)?;
// Create a new database
let config = OurDBConfig {
path: db_path,
incremental_mode: true,
file_size: Some(10 * 1024 * 1024), // 10MB
keysize: Some(4), // 4-byte keys
};
let mut db = OurDB::new(config)?;
// Number of operations for the benchmark
let num_operations = 1000;
let data_size = 100; // bytes per record
// Prepare test data
let test_data = vec![b'A'; data_size];
// Benchmark write operations
println!("Benchmarking {} write operations...", num_operations);
let start = Instant::now();
let mut ids = Vec::with_capacity(num_operations);
for _ in 0..num_operations {
let id = db.set(OurDBSetArgs { id: None, data: &test_data })?;
ids.push(id);
}
let write_duration = start.elapsed();
let writes_per_second = num_operations as f64 / write_duration.as_secs_f64();
println!("Write performance: {:.2} ops/sec ({:.2} ms/op)",
writes_per_second,
write_duration.as_secs_f64() * 1000.0 / num_operations as f64);
// Benchmark read operations
println!("Benchmarking {} read operations...", num_operations);
let start = Instant::now();
for &id in &ids {
let _ = db.get(id)?;
}
let read_duration = start.elapsed();
let reads_per_second = num_operations as f64 / read_duration.as_secs_f64();
println!("Read performance: {:.2} ops/sec ({:.2} ms/op)",
reads_per_second,
read_duration.as_secs_f64() * 1000.0 / num_operations as f64);
// Benchmark update operations
println!("Benchmarking {} update operations...", num_operations);
let start = Instant::now();
for &id in &ids {
db.set(OurDBSetArgs { id: Some(id), data: &test_data })?;
}
let update_duration = start.elapsed();
let updates_per_second = num_operations as f64 / update_duration.as_secs_f64();
println!("Update performance: {:.2} ops/sec ({:.2} ms/op)",
updates_per_second,
update_duration.as_secs_f64() * 1000.0 / num_operations as f64);
db.close()?;
println!("Performance benchmark completed");
Ok(())
}

View File

@@ -0,0 +1,72 @@
use ourdb::{OurDB, OurDBConfig, OurDBSetArgs};
use std::path::PathBuf;
fn main() -> Result<(), ourdb::Error> {
// Create a temporary directory for the database
let db_path = std::env::temp_dir().join("ourdb_example");
std::fs::create_dir_all(&db_path)?;
println!("Creating database at: {}", db_path.display());
// Create a new database with incremental mode enabled
let config = OurDBConfig {
path: db_path.clone(),
incremental_mode: true,
file_size: None, // Use default (500MB)
keysize: None, // Use default (4 bytes)
};
let mut db = OurDB::new(config)?;
// Store some data with auto-generated IDs
let data1 = b"First record";
let id1 = db.set(OurDBSetArgs { id: None, data: data1 })?;
println!("Stored first record with ID: {}", id1);
let data2 = b"Second record";
let id2 = db.set(OurDBSetArgs { id: None, data: data2 })?;
println!("Stored second record with ID: {}", id2);
// Retrieve and print the data
let retrieved1 = db.get(id1)?;
println!("Retrieved ID {}: {}", id1, String::from_utf8_lossy(&retrieved1));
let retrieved2 = db.get(id2)?;
println!("Retrieved ID {}: {}", id2, String::from_utf8_lossy(&retrieved2));
// Update a record to demonstrate history tracking
let updated_data = b"Updated first record";
db.set(OurDBSetArgs { id: Some(id1), data: updated_data })?;
println!("Updated record with ID: {}", id1);
// Get history for the updated record
let history = db.get_history(id1, 2)?;
println!("History for ID {}:", id1);
for (i, entry) in history.iter().enumerate() {
println!(" Version {}: {}", i, String::from_utf8_lossy(entry));
}
// Delete a record
db.delete(id2)?;
println!("Deleted record with ID: {}", id2);
// Verify deletion
match db.get(id2) {
Ok(_) => println!("Record still exists (unexpected)"),
Err(e) => println!("Verified deletion: {}", e),
}
// Close the database
db.close()?;
println!("Database closed successfully");
// Clean up (optional)
if std::env::var("KEEP_DB").is_err() {
std::fs::remove_dir_all(&db_path)?;
println!("Cleaned up database directory");
} else {
println!("Database kept at: {}", db_path.display());
}
Ok(())
}

186
ourdb/examples/benchmark.rs Normal file
View File

@@ -0,0 +1,186 @@
use ourdb::{OurDB, OurDBConfig, OurDBSetArgs};
use std::path::PathBuf;
use std::time::{Duration, Instant};
fn main() -> Result<(), ourdb::Error> {
// Parse command line arguments
let args: Vec<String> = std::env::args().collect();
let (num_operations, record_size, incremental_mode, keysize) = parse_args(&args);
println!("OurDB Benchmark");
println!("===============");
println!("Operations: {}", num_operations);
println!("Record size: {} bytes", record_size);
println!("Mode: {}", if incremental_mode { "Incremental" } else { "Key-Value" });
println!("Key size: {} bytes", keysize);
println!();
// Create a temporary directory for the database
let db_path = std::env::temp_dir().join(format!("ourdb_benchmark_{}", std::process::id()));
std::fs::create_dir_all(&db_path)?;
println!("Database path: {}", db_path.display());
// Create a new database
let config = OurDBConfig {
path: db_path.clone(),
incremental_mode,
file_size: Some(50 * 1024 * 1024), // 50MB
keysize: Some(keysize),
};
let mut db = OurDB::new(config)?;
// Prepare test data
let test_data = vec![b'X'; record_size];
// Benchmark write operations
println!("\nBenchmarking writes...");
let start = Instant::now();
let mut ids = Vec::with_capacity(num_operations);
for i in 0..num_operations {
let id = if incremental_mode {
db.set(OurDBSetArgs { id: None, data: &test_data })?
} else {
// In key-value mode, we provide explicit IDs
let id = i as u32 + 1;
db.set(OurDBSetArgs { id: Some(id), data: &test_data })?;
id
};
ids.push(id);
}
let write_duration = start.elapsed();
print_performance_stats("Write", num_operations, write_duration);
// Benchmark read operations (sequential)
println!("\nBenchmarking sequential reads...");
let start = Instant::now();
for &id in &ids {
let _ = db.get(id)?;
}
let read_duration = start.elapsed();
print_performance_stats("Sequential read", num_operations, read_duration);
// Benchmark random reads
println!("\nBenchmarking random reads...");
let start = Instant::now();
use std::collections::HashSet;
let mut rng = rand::thread_rng();
let mut random_indices = HashSet::new();
// Select 20% of the IDs randomly for testing
let sample_size = num_operations / 5;
while random_indices.len() < sample_size {
let idx = rand::Rng::gen_range(&mut rng, 0..ids.len());
random_indices.insert(idx);
}
for idx in random_indices {
let _ = db.get(ids[idx])?;
}
let random_read_duration = start.elapsed();
print_performance_stats("Random read", sample_size, random_read_duration);
// Benchmark update operations
println!("\nBenchmarking updates...");
let start = Instant::now();
for &id in &ids[0..num_operations/2] {
db.set(OurDBSetArgs { id: Some(id), data: &test_data })?;
}
let update_duration = start.elapsed();
print_performance_stats("Update", num_operations/2, update_duration);
// Benchmark history retrieval
println!("\nBenchmarking history retrieval...");
let start = Instant::now();
for &id in &ids[0..num_operations/10] {
let _ = db.get_history(id, 2)?;
}
let history_duration = start.elapsed();
print_performance_stats("History retrieval", num_operations/10, history_duration);
// Benchmark delete operations
println!("\nBenchmarking deletes...");
let start = Instant::now();
for &id in &ids[0..num_operations/4] {
db.delete(id)?;
}
let delete_duration = start.elapsed();
print_performance_stats("Delete", num_operations/4, delete_duration);
// Close and clean up
db.close()?;
std::fs::remove_dir_all(&db_path)?;
println!("\nBenchmark completed successfully");
Ok(())
}
fn parse_args(args: &[String]) -> (usize, usize, bool, u8) {
let mut num_operations = 10000;
let mut record_size = 100;
let mut incremental_mode = true;
let mut keysize = 4;
for i in 1..args.len() {
if args[i] == "--ops" && i + 1 < args.len() {
if let Ok(n) = args[i + 1].parse() {
num_operations = n;
}
} else if args[i] == "--size" && i + 1 < args.len() {
if let Ok(n) = args[i + 1].parse() {
record_size = n;
}
} else if args[i] == "--keyvalue" {
incremental_mode = false;
} else if args[i] == "--keysize" && i + 1 < args.len() {
if let Ok(n) = args[i + 1].parse() {
if [2, 3, 4, 6].contains(&n) {
keysize = n;
}
}
} else if args[i] == "--help" {
print_usage();
std::process::exit(0);
}
}
(num_operations, record_size, incremental_mode, keysize)
}
fn print_usage() {
println!("OurDB Benchmark Tool");
println!("Usage: cargo run --example benchmark [OPTIONS]");
println!();
println!("Options:");
println!(" --ops N Number of operations to perform (default: 10000)");
println!(" --size N Size of each record in bytes (default: 100)");
println!(" --keyvalue Use key-value mode instead of incremental mode");
println!(" --keysize N Key size in bytes (2, 3, 4, or 6) (default: 4)");
println!(" --help Print this help message");
}
fn print_performance_stats(operation: &str, count: usize, duration: Duration) {
let ops_per_second = count as f64 / duration.as_secs_f64();
let ms_per_op = duration.as_secs_f64() * 1000.0 / count as f64;
println!("{} performance:", operation);
println!(" Total time: {:.2} seconds", duration.as_secs_f64());
println!(" Operations: {}", count);
println!(" Speed: {:.2} ops/sec", ops_per_second);
println!(" Average: {:.3} ms/op", ms_per_op);
}