Skip to main content

v0.46.0 Release Notes

Release Notes

EnerOS v0.46.0 Release Notes

  • Release Date: 2026-07-03
  • Codename: Stable
  • Support Status: Stable, Long-Term Support candidate
  • Total Crates: 55
  • Test Cases: 7200+
  • Contributors: 30

Version Overview

EnerOS v0.46.0 is the sixth release of the “Mature Optimization” phase, focusing on stability enhancement. After the first five releases completed performance benchmarking, security auditing, developer experience, documentation systems, and ecosystem expansion, this release returns to engineering fundamentals, concentrating on solving stability problems during long-term operation, laying a solid foundation for the final convergence of v0.47.0.

Power systems have extremely stringent availability requirements. Provincial dispatching centers require system annual availability to reach 99.999% (i.e., annual downtime not exceeding 5 minutes). This means EnerOS must maintain memory stability, no performance degradation, and recoverable errors during long-term continuous operation (months or even years). v0.46.0 discovered 23 memory leaks, 12 error handling defects, and 8 unreleased resource issues through systematic stress testing, and fixed all of them.

This release introduces an automated stress testing framework eneros-stress, which can simulate 7 days of real load within 24 hours, verifying long-term system stability through accelerated clocks and fault injection. All stress tests run on a weekly cycle in CI, ensuring each commit does not introduce new stability issues.

Key Metrics

MetricValueDescription
Crate count55Added eneros-stress
Test cases7200+Including 180+ stress tests
Memory leak fixes23All fixed
Error handling defect fixes12All fixed
Continuous run test168 hoursNo degradation
Memory growth (24h)< 0.1%Full load operation
Availability target99.999%Annual downtime < 5 minutes

New Features

1. Stress Testing Framework

Added the eneros-stress crate, providing systematic stress testing capabilities. The framework supports load generation, fault injection, accelerated clocks, and resource monitoring, capable of simulating long-term operation scenarios in a short time.

Framework Architecture

eneros-stress/
├── src/
│   ├── generator/        # Load generator
│   │   ├── topology.rs   # Topology operation load
│   │   ├── powerflow.rs  # Power flow computation load
│   │   ├── timeseries.rs # Time-series write load
│   │   └── agent.rs      # Agent message load
│   ├── injector/         # Fault injector
│   │   ├── network.rs    # Network faults
│   │   ├── disk.rs       # Disk faults
│   │   └── process.rs    # Process faults
│   ├── monitor/          # Resource monitoring
│   │   ├── memory.rs     # Memory growth detection
│   │   ├── fd.rs         # File descriptor leaks
│   │   └── thread.rs     # Thread leaks
│   └── clock/            # Accelerated clock

Usage Example

use eneros_stress::{StressTest, StressConfig, LoadProfile, FaultInjector};
use std::time::Duration;

let stress = StressTest::new(StressConfig {
    duration: Duration::from_secs(86400 * 7),     // Simulate 7 days
    accelerated_clock: 24.0,                       // 24x acceleration, actual runtime 7 hours
    load_profile: LoadProfile::Realistic {
        peak_times: vec![8, 12, 18],               // Daily peak hours
        base_load: 0.3,                            // Base load 30%
        peak_load: 0.95,                           // Peak load 95%
    },
    fault_injection: FaultInjector::Chaos {
        network_partition_prob: 0.001,             // 0.1% network partition
        disk_full_prob: 0.0005,                    // 0.05% disk full
        process_kill_prob: 0.002,                  // 0.2% process kill
    },
    memory_leak_threshold: Duration::from_secs(3600), // Detect after 1 hour
});

let report = stress.run().await?;

println!("Test results:");
println!("  Total operations: {}", report.total_ops);
println!("  Error count: {}", report.errors.len());
println!("  Memory growth: {:.2}%", report.memory_growth_pct);
println!("  Availability: {:.4}%", report.availability * 100.0);

for leak in &report.memory_leaks {
    println!("  Memory leak: {} (growth {:.2} MB/h)", leak.location, leak.rate_mb_per_hour);
}

Stress Test Scenarios

ScenarioLoad PatternFault InjectionDurationValidation Target
Full load operation100% continuousNone168 hoursMemory stability
Peak-valley fluctuationReal load curveNone7 days (accelerated)No performance degradation
Chaos engineeringReal loadNetwork partition24 hoursFault recovery
Resource exhaustionIncreasing loadDisk full12 hoursGraceful degradation
Long connectionContinuous connectionProcess kill48 hoursReconnection recovery

2. Memory Leak Fixes

Discovered and fixed 23 memory leaks through stress testing, covering five modules: topology snapshots, Agent context, time-series cache, constraint engine, and audit chain.

Main Leak Points

ModuleLeak CauseImpact RateFix Method
eneros-topologySnapshots not released2.3 MB/hReference counting + Drop
eneros-agentContext cache accumulation1.8 MB/hLRU eviction
eneros-timeseriesCompression buffer not recycled5.1 MB/hTiered flushing
eneros-constraintConstraint snapshot history0.7 MB/hLimit history depth
eneros-trustAudit log buffer3.2 MB/hPeriodic flush
eneros-memoryAgent memory vectors4.5 MB/hHierarchical compression
eneros-orchestratorDAG node cache1.2 MB/hCleanup after execution

Memory Monitoring Tool

use eneros_stress::monitor::{MemoryMonitor, LeakDetector};

let monitor = MemoryMonitor::new()
    .sample_interval(Duration::from_secs(60))
    .leak_detector(LeakDetector::LinearRegression {
        min_samples: 60,           // At least 60 samples (1 hour)
        growth_threshold: 0.1,     // Alert if growth > 0.1% per hour
    });

monitor.start().await?;

// Get memory report
let snapshot = monitor.snapshot().await?;
println!("Current RSS: {:.1} MB", snapshot.rss_mb);
println!("24-hour growth: {:.2}%", snapshot.growth_24h_pct);

// Detect leaks
if let Some(leaks) = monitor.detect_leaks().await? {
    for leak in leaks {
        eprintln!("Suspected leak: {} growth {:.2} MB/h", leak.location, leak.rate);
    }
}

3. Long-Term Operation Optimization

Multiple optimizations for long-term operation scenarios, ensuring system performance does not degrade after months of continuous operation.

Snapshot History Depth Limit

use eneros_topology::{TopologyConfig, SnapshotPolicy};

let config = TopologyConfig {
    snapshot_policy: SnapshotPolicy::Bounded {
        max_history: 1000,                          // Keep at most 1000 snapshots
        max_age: Duration::from_secs(3600),         // Keep for at most 1 hour
        eviction: EvictionPolicy::OldestFirst,
    },
    ..Default::default()
};

Agent Context LRU Eviction

use eneros_agent::{AgentRuntime, CacheConfig};

let runtime = AgentRuntime::new(CacheConfig {
    max_contexts: 500,                               // Cache at most 500 contexts
    context_ttl: Duration::from_secs(300),           // Evict if unused for 5 minutes
    eviction: EvictionPolicy::LRU,
});

Time-Series Data Tiered Flushing

use eneros_timeseries::{TsdbConfig, FlushPolicy};

let config = TsdbConfig {
    flush_policy: FlushPolicy::Tiered {
        hot_tier_size: 8 * 1024 * 1024,              // Hot tier 8MB
        warm_tier_size: 64 * 1024 * 1024,            // Warm tier 64MB
        cold_tier_path: "data/cold/",
        flush_interval: Duration::from_millis(100),
        backpressure_threshold: 0.8,                  // Trigger backpressure at 80%
    },
    ..Default::default()
};

4. Error Handling Enhancement

Systematically reviewed error handling paths across all crates, fixing 12 error handling defects, including unhandled panics, silently ignored errors, and improper error propagation.

Error Handling Improvements

Defect TypeCountImpactFix Method
Unhandled panic3Process crashReplace with Result
Silently ignored error5Data lossExplicit handling or propagation
Error information loss4Debugging difficultyPreserve error chain
Unreleased resource8Resource leakRAII + Drop

Graceful Degradation Mechanism

use eneros_core::resilience::{CircuitBreaker, FallbackPolicy};

let breaker = CircuitBreaker::new()
    .failure_threshold(5)                            // Open circuit after 5 failures
    .reset_timeout(Duration::from_secs(30))          // Try recovery after 30 seconds
    .fallback(FallbackPolicy::UseLastKnownGood);     // Degrade to last known good value

match breaker.call(|| async {
    ctx.syscall(SolvePowerFlow { /* ... */ }).await
}).await {
    Ok(result) => { /* Normal result */ }
    Err(CircuitBreakerError::Open) => {
        // Circuit open, use fallback value
        let fallback = ctx.cache.get_last_powerflow().await?;
        log::warn!("Power flow computation circuit open, using cached value");
    }
}

Backpressure Mechanism

use eneros_core::resilience::{Backpressure, BackpressureConfig};

let bp = Backpressure::new(BackpressureConfig {
    high_watermark: 0.8,        // Start backpressure at 80%
    low_watermark: 0.5,         // Stop backpressure at 50%
    queue_capacity: 10000,
});

// Check backpressure on write
if bp.is_pressure_high() {
    // Return backpressure signal, upstream should slow down
    return Err(BackpressureError::SlowDown);
}

bp.increment();
let result = do_work().await;
bp.decrement();

Improvements

Panic Capture and Recovery

All Agent execution entry points add panic capture, preventing a single Agent’s panic from crashing the entire runtime. After panic is captured, the complete call stack is recorded, and Agent restart is triggered.

use eneros_agent::runtime::PanicGuard;

let guard = PanicGuard::new("dispatch-agent");
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    agent.run(ctx)
}));

match result {
    Ok(Ok(())) => { /* Normal completion */ }
    Ok(Err(e)) => { /* Agent returned error */ }
    Err(panic) => {
        let backtrace = guard.capture_backtrace(panic);
        log::error!("Agent panic: {}", backtrace);
        runtime::restart_agent("dispatch-agent").await?;
    }
}

Resource Limits

Each Agent and plugin adds resource limits, preventing resource exhaustion from affecting system stability.

ResourceDefault LimitOver-limit Behavior
Memory512 MBOOM alert + restart
CPU4 coresThrottle
File descriptors1024Reject new FDs
Network connections256Reject new connections
Disk writes1 GB/hThrottle

Graceful Shutdown

When the system shuts down, it exits gracefully in dependency order, ensuring all in-flight requests complete, buffers flush, and state persists.

use eneros_os::shutdown::{ShutdownManager, ShutdownOrder};

let manager = ShutdownManager::new()
    .timeout(Duration::from_secs(30))
    .order(ShutdownOrder::Dependency)
    .step("agents", |ctx| async { /* Stop all Agents */ })
    .step("api", |ctx| async { /* Close API gateway */ })
    .step("timeseries", |ctx| async { /* Flush time-series data */ })
    .step("topology", |ctx| async { /* Persist topology */ })
    .step("kernel", |ctx| async { /* Kernel cleanup */ });

manager.shutdown().await?;

Bug Fixes

  • BG-701: Topology snapshot reference counting error during concurrent writes, fixed with atomic operations
  • BG-705: Agent context cache unlimited causing infinite memory growth, introduced LRU
  • BG-709: Time-series compression buffer infinite growth when write speed exceeds flush speed, introduced backpressure
  • BG-713: Constraint engine history snapshots not cleaned, limited max depth
  • BG-717: Audit log buffer not periodically flushed, changed to scheduled flushing
  • BG-721: Agent memory vectors infinite accumulation, introduced hierarchical compression
  • BG-725: DAG orchestration node cache not cleaned, cleanup after execution
  • BG-729: Panic not caught in Agent, added PanicGuard
  • BG-733: File descriptor leak on reconnection, fixed RAII encapsulation
  • BG-737: In-flight requests discarded during graceful shutdown, added drain period

Breaking Changes

BC-251: AgentRuntime Adds Resource Limits Configuration

The configuration parameter of AgentRuntime::new adds a resource_limits field, which needs to be added to existing code.

// v0.45.0 (old)
let runtime = AgentRuntime::new(RuntimeConfig::default());

// v0.46.0 (new)
let runtime = AgentRuntime::new(RuntimeConfig {
    resource_limits: ResourceLimits::default(),
    ..Default::default()
});

BC-252: TsdbConfig flush_policy Field Change

flush_policy changed from simple FlushInterval to FlushPolicy enum, supporting tiered flushing.

Dependency Upgrades

DependencyOld VersionNew VersionDescription
rustc1.751.76Panic hook enhancement
jemallocator0.50.5.4Memory allocator
mimalloc0.10.1.39Alternative allocator
tracing0.1.400.1.40No change

Upgrade Guide

Upgrade from v0.45.0

rustup default 1.76.0
cargo update
cargo test --workspace

Handle breaking changes:

// Add resource limits configuration
let runtime = AgentRuntime::new(RuntimeConfig {
    resource_limits: ResourceLimits {
        max_memory_mb: 512,
        max_cpu_cores: 4,
        max_file_descriptors: 1024,
    },
    ..Default::default()
});

Run stress tests to verify stability:

eneros-cli stress run --scenario full-load --duration 7h
eneros-cli stress report