Skip to main content

noxu_engine/
lib.rs

1#![forbid(unsafe_code)]
2// Copyright (C) 2024-2025 Greg Burd.  Licensed under either of the
3// Apache License, Version 2.0 or the MIT license, at your option.
4// See LICENSE-APACHE and LICENSE-MIT at the root of this repository.
5// SPDX-License-Identifier: Apache-2.0 OR MIT
6#![allow(dead_code, clippy::type_complexity, clippy::too_many_arguments)]
7//! > **Internal component of the [`noxu`](https://crates.io/crates/noxu) database.**
8//! >
9//! > This crate is published only so the `noxu` umbrella crate can depend on it.
10//! > Use `noxu` (`noxu = "3"`) in applications; depend on this crate directly only
11//! > if you are extending the engine internals. Its API may change without a major
12//! > version bump.
13//!
14//! Engine orchestration for Noxu DB.
15//!
16//! Wires together all internal subsystems, manages daemon thread lifecycle,
17//! and coordinates environment open/close.
18//!
19//! # Overview
20//!
21//! The `noxu-engine` crate is the internal orchestration layer for Noxu DB.
22//! It brings together all the subsystems built in earlier phases:
23//!
24//! - **noxu-dbi**: EnvironmentImpl, DatabaseImpl, CursorImpl
25//! - **noxu-txn**: Transaction and lock management
26//! - **noxu-evictor**: Cache eviction
27//! - **noxu-cleaner**: Log garbage collection
28//! - **noxu-recovery**: Checkpointing and recovery
29//!
30//! # Architecture
31//!
32//! The [`Engine`] struct is the central coordination point. It:
33//! 1. Creates and owns all subsystems
34//! 2. Runs recovery on environment open
35//! 3. Starts background daemon threads (evictor, cleaner, checkpointer)
36//! 4. Coordinates orderly shutdown
37//! 5. Provides unified access to all subsystems
38//!
39//! # Usage
40//!
41//! ```rust,ignore
42//! use noxu_engine::{Engine, EngineConfig};
43//!
44//! // Open an environment
45//! let config = EngineConfig::new("/data/mydb")
46//!     .cache_size(128 * 1024 * 1024)
47//!     .transactional(true);
48//! let engine = Engine::open(config)?;
49//!
50//! // Use the engine...
51//! let stats = engine.get_stats();
52//! println!("Cache usage: {:.1}%", stats.cache_utilization_percent());
53//!
54//! // Explicitly close (or drop will close)
55//! engine.close()?;
56//! ```
57//!
58//! # Configuration
59//!
60//! Environment behavior is controlled via [`EngineConfig`]:
61//!
62//! - Cache size and eviction settings
63//! - Transaction and lock timeouts
64//! - Daemon thread intervals
65//! - Read-only mode
66//! - Checkpoint and cleaning thresholds
67//!
68//! # Background Daemons
69//!
70//! Three daemon threads run in the background:
71//!
72//! 1. **Evictor**: Evicts nodes from cache when memory budget is exceeded
73//! 2. **Cleaner**: Garbage collects log files when utilization is low
74//! 3. **Checkpointer**: Flushes dirty nodes to bound recovery time
75//!
76//! All daemons can be individually enabled/disabled via configuration.
77//!
78//! # Statistics
79//!
80//! [`EnvironmentStats`] provides a unified view of all subsystem statistics:
81//!
82//! - Cache usage and eviction metrics
83//! - Cleaning progress and file statistics
84//! - Checkpoint frequency and flush counts
85//! - Database and transaction counts
86
87pub mod daemon_manager;
88pub mod engine;
89pub mod engine_config;
90pub mod env_stats;
91pub mod error;
92pub mod verify;
93
94// Re-export main types at crate root
95pub use daemon_manager::DaemonManager;
96pub use engine::Engine;
97pub use engine_config::EngineConfig;
98pub use env_stats::EnvironmentStats;
99pub use error::{EngineError, Result};
100pub use verify::{
101    VerifyConfig, VerifyError, VerifyResult, verify_database,
102    verify_database_impl, verify_environment, verify_tree,
103};
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use tempfile::TempDir;
109
110    #[test]
111    fn test_crate_public_api() {
112        // Verify all main types are re-exported
113        let _: EngineConfig;
114        let _: Engine;
115        let _: EnvironmentStats;
116        let _: EngineError;
117        let _: Result<()>;
118    }
119
120    #[test]
121    fn test_basic_engine_lifecycle() {
122        let dir = TempDir::new().unwrap();
123        let config = EngineConfig::new(dir.path())
124            .cache_size(10 * 1024 * 1024)
125            .evictor_wakeup_interval_ms(100)
126            .cleaner_wakeup_interval_ms(100)
127            .checkpointer_wakeup_interval_ms(100);
128
129        let engine = Engine::open(config).unwrap();
130        assert!(engine.is_open());
131
132        let stats = engine.get_stats();
133        assert_eq!(stats.cache_size, 10 * 1024 * 1024);
134
135        engine.close().unwrap();
136        assert!(!engine.is_open());
137    }
138
139    #[test]
140    fn test_config_validation() {
141        let dir = TempDir::new().unwrap();
142
143        // Valid config
144        let config = EngineConfig::new(dir.path());
145        assert!(config.validate().is_ok());
146
147        // Invalid: cache too small
148        let config = EngineConfig::new(dir.path()).cache_size(1024);
149        assert!(config.validate().is_err());
150
151        // Invalid: zero lock tables
152        let config = EngineConfig::new(dir.path()).lock_table_count(0);
153        assert!(config.validate().is_err());
154    }
155
156    #[test]
157    fn test_readonly_mode() {
158        let dir = TempDir::new().unwrap();
159        let config = EngineConfig::new(dir.path())
160            .read_only(true)
161            .cleaner_enabled(false)
162            .checkpointer_enabled(false);
163
164        let engine = Engine::open(config).unwrap();
165        assert!(engine.get_config().read_only);
166
167        // Write operations should fail
168        assert!(engine.checkpoint("test").is_err());
169        assert!(engine.clean(5).is_err());
170    }
171
172    #[test]
173    fn test_subsystem_access() {
174        let dir = TempDir::new().unwrap();
175        let config = EngineConfig::new(dir.path()).cache_size(10 * 1024 * 1024);
176
177        let engine = Engine::open(config).unwrap();
178
179        // Verify subsystem access
180        let env_impl = engine.get_env_impl();
181        assert!(env_impl.lock().is_open());
182
183        let evictor = engine.get_evictor();
184        let _ = evictor.get_stats();
185
186        let cleaner = engine.get_cleaner();
187        let _ = cleaner.get_stats();
188
189        let checkpointer = engine.get_checkpointer();
190        let _ = checkpointer.get_stats();
191    }
192}