graph_d 1.3.2

A native graph database implementation in Rust with built-in JSON support and SQLite-like simplicity
Documentation
//! Example demonstrating advanced memory management features.
//!
//! This example shows how to use the memory management system to optimize
//! graph operations for memory efficiency and performance.

use graph_d::memory::allocator_safe::SafeMemoryManager;
use graph_d::memory::cache::{CacheConfig, NodeCache, RelationshipCache};
use graph_d::memory::interning::GraphStringInterner;
use graph_d::memory::{MemoryConfig, MemoryManager};
use graph_d::{Graph, Result};
use serde_json::json;

fn main() -> Result<()> {
    println!("Graph Database Memory Management Example");
    println!("========================================");

    // Example 1: Basic Memory Management
    basic_memory_management()?;

    // Example 2: Safe Memory Allocators
    safe_memory_allocators()?;

    // Example 3: Intelligent Caching
    intelligent_caching()?;

    // Example 4: String Interning
    string_interning_example()?;

    Ok(())
}

/// Demonstrates basic memory management with configuration.
fn basic_memory_management() -> Result<()> {
    println!("\n1. Basic Memory Management");
    println!("--------------------------");

    // Configure memory management for 1M nodes target
    let config = MemoryConfig {
        max_memory_bytes: 1024 * 1024 * 1024, // 1GB limit
        arena_size_bytes: 64 * 1024 * 1024,   // 64MB arena
        node_cache_capacity: 100_000,         // Cache 100K nodes
        relationship_cache_capacity: 400_000, // Cache 400K relationships
        property_cache_capacity: 200_000,     // Cache 200K properties
        string_table_capacity: 50_000,        // Intern 50K strings
    };

    let memory_manager = MemoryManager::new(config)?;

    // Create a graph and demonstrate memory-aware operations
    let mut graph = Graph::new()?;

    // Create nodes with memory tracking
    println!("Creating nodes with memory tracking...");
    let mut node_ids = Vec::new();

    for i in 0..1000 {
        let properties = [
            ("id".to_string(), json!(i)),
            ("name".to_string(), json!(format!("Node{}", i))),
            ("category".to_string(), json!(format!("cat_{}", i % 10))),
        ]
        .into();

        let node_id = graph.create_node(properties)?;
        node_ids.push(node_id);

        // Cache frequently accessed nodes
        if i % 10 == 0 {
            let node_data = format!("node_data_{i}").into_bytes();
            memory_manager.cache_node(node_id, node_data);
        }
    }

    // Check memory statistics
    let stats = memory_manager.memory_stats();
    println!("Memory Statistics:");
    println!("  Total allocated: {} bytes", stats.total_allocated);
    println!("  Node cache: {} bytes", stats.node_cache_bytes);
    println!("  Cache hit rate: {:.2}%", stats.cache_hit_rate * 100.0);

    // Demonstrate arena allocation for temporary data
    let handle = memory_manager.arena_alloc(vec![1, 2, 3, 4, 5]);
    println!("Arena allocated data: {:?}", handle.get());

    // Check memory pressure and cleanup
    memory_manager.check_memory_pressure()?;
    println!("Memory pressure check completed");

    Ok(())
}

/// Demonstrates safe memory allocators without unsafe code.
fn safe_memory_allocators() -> Result<()> {
    println!("\n2. Safe Memory Allocators");
    println!("-------------------------");

    let memory_manager = SafeMemoryManager::new(1024 * 1024); // 1MB arena

    // Demonstrate pooled buffer allocation
    {
        let mut node_buffer = memory_manager.acquire_node_buffer();
        node_buffer
            .get_mut()
            .extend_from_slice(b"node_data_example");
        println!("Node buffer contains: {} bytes", node_buffer.get().len());

        let mut rel_buffer = memory_manager.acquire_relationship_buffer();
        rel_buffer.get_mut().extend_from_slice(b"relationship_data");
        println!(
            "Relationship buffer contains: {} bytes",
            rel_buffer.get().len()
        );
    } // Buffers automatically returned to pool here

    // Get statistics after buffer usage
    let stats = memory_manager.stats();
    println!("Safe Memory Statistics:");
    println!("  Arena usage: {} bytes", stats.arena_usage);
    println!(
        "  Node pool available: {}",
        stats.node_pool.available_objects
    );
    println!(
        "  Relationship pool available: {}",
        stats.relationship_pool.available_objects
    );

    // Demonstrate scoped arena allocation
    memory_manager.arena().scoped(|| {
        println!("Performing scoped operations...");
        // Temporary allocations happen here and are automatically cleaned up
        42
    });

    Ok(())
}

/// Demonstrates intelligent caching with hot/cold separation.
fn intelligent_caching() -> Result<()> {
    println!("\n3. Intelligent Caching");
    println!("----------------------");

    let cache_config = CacheConfig {
        hot_cache_size: 1000,
        cold_cache_size: 10000,
        promotion_threshold: 3,
        ..Default::default()
    };

    let node_cache = NodeCache::new(cache_config.clone())?;
    let _rel_cache = RelationshipCache::new(cache_config)?;

    // Simulate node access patterns
    println!("Simulating access patterns...");

    // Create some test nodes
    for i in 0..100 {
        let node_data = format!("node_{i}").into_bytes();
        node_cache.put_node(i, node_data);

        // Cache some properties
        node_cache.put_property(i, "name".to_string(), json!(format!("Node{}", i)));
        node_cache.put_property(i, "type".to_string(), json!("test"));
    }

    // Access some nodes multiple times (simulating hot access)
    for _ in 0..5 {
        for i in 0..10 {
            if let Some(data) = node_cache.get_node(i) {
                println!("Hot access: Node {} has {} bytes", i, data.len());
            }

            // Access properties
            if let Some(name) = node_cache.get_property(i, "name") {
                println!("Property access: name = {name}");
            }
        }
    }

    // Get cache statistics
    let (cache_stats, property_cache_size) = node_cache.stats();
    println!("Node Cache Statistics:");
    println!("  Hit rate: {:.2}%", cache_stats.hit_rate() * 100.0);
    println!("  Hot hit rate: {:.2}%", cache_stats.hot_hit_rate() * 100.0);
    println!("  Promotions: {}", cache_stats.promotions);
    println!("  Property cache size: {property_cache_size}");

    Ok(())
}

/// Demonstrates string interning for memory deduplication.
fn string_interning_example() -> Result<()> {
    println!("\n4. String Interning");
    println!("-------------------");

    let interner = GraphStringInterner::new(10000);

    // Intern common graph property names (these are pre-loaded)
    let name_id = interner.intern("name");
    let type_id = interner.intern("type");
    let id_id = interner.intern("id");

    println!("Interned common properties:");
    println!("  'name' -> ID {name_id}");
    println!("  'type' -> ID {type_id}");
    println!("  'id' -> ID {id_id}");

    // Intern the same strings again (should get same IDs)
    let name_id2 = interner.intern("name");
    println!(
        "  'name' again -> ID {} (same: {})",
        name_id2,
        name_id == name_id2
    );

    // Check if strings are common/pre-loaded
    println!("  'name' is common: {}", interner.is_common_string("name"));
    println!(
        "  'custom_prop' is common: {}",
        interner.is_common_string("custom_prop")
    );

    // Intern some unique values
    for i in 0..100 {
        let unique_value = format!("unique_value_{i}");
        interner.intern(&unique_value);
    }

    // Get statistics
    let stats = interner.stats();
    let savings = interner.savings();

    println!("String Interning Statistics:");
    println!("  Unique strings: {}", stats.interned_strings);
    println!("  Utilization: {:.2}%", stats.utilization() * 100.0);
    println!("  Total string bytes: {}", stats.total_string_bytes);
    println!("  Memory savings: {} bytes", savings.bytes_saved);
    println!("  Net beneficial: {}", savings.is_beneficial());
    println!("  Compression ratio: {:.2}x", savings.compression_ratio());

    Ok(())
}