# Memory Management & Resource Limits
**Status**: ✅ Implemented
**Version**: Added in v0.6.0
**Date**: September 2025
## Overview
The CSP Solver now includes comprehensive memory management and resource limiting to prevent system memory exhaustion during model building and solving.
## 🚨 Problem Solved
**Before**: Large models could consume 28GB+ of system RAM and crash the IDE/system during variable creation.
**After**: Memory limits are enforced during variable creation with early failure and clear error messages.
## 🎛️ Default Configuration
Starting with version 0.6.0, all models have sensible default limits:
```rust
// Default limits applied automatically
let m = Model::default();
// ↳ Memory limit: 2GB
// ↳ Timeout: 60000 milliseconds (60 seconds)
```
### Default Values
| **Memory** | **2GB** | Prevents system memory exhaustion |
| **Timeout** | **60000 ms (60 sec)** | Prevents infinite solver runs |
## 🔧 Configuration
### Custom Limits
```rust
use selen::prelude::*;
// Configure custom limits
let config = SolverConfig::default()
.with_max_memory_mb(512) // 512MB memory limit
.with_timeout_ms(30000); // 30000ms = 30 second timeout
let mut m = Model::with_config(config);
```
### No Limits (Use with Caution)
```rust
// Remove all limits - USE CAREFULLY!
let config = SolverConfig::unlimited();
let mut m = Model::with_config(config);
```
### Production Recommended Limits
```rust
// Conservative for shared environments
let config = SolverConfig::default()
.with_max_memory_mb(1024) // 1GB limit
.with_timeout_ms(120000); // 120000ms = 2 minute timeout
// Generous for dedicated systems
let config = SolverConfig::default()
.with_max_memory_mb(4096) // 4GB limit
.with_timeout_ms(300000); // 300000ms = 5 minute timeout
```
## 📊 Memory Estimation
### How It Works
The solver estimates memory usage during variable creation:
- **Float variables**: ~64 bytes each (interval storage)
- **Small integer domains** (<1000 values): ~96 + domain_size×8 bytes
- **Large integer domains** (≥1000 values): ~96 + domain_size×4 bytes (sparse)
- **Custom domains**: Based on number of specific values
### Memory Tracking
```rust
let mut m = Model::with_config(
SolverConfig::default().with_max_memory_mb(100)
);
// Create variables - memory is tracked automatically
for i in 0..1000 {
let var = m.float(0.0, 100.0);
// Check current usage
if i % 100 == 0 {
println!("Memory usage: {:.2} MB", m.estimated_memory_mb());
}
}
// Get detailed breakdown
println!("{}", m.memory_breakdown());
```
## 🛡️ Failure Behavior
### Memory Limit Exceeded
When memory limit is exceeded during variable creation:
```
thread 'main' panicked at src/model/core.rs:460:17:
Memory limit exceeded during variable creation: Memory limit exceeded (used: 1MB, limit: 1MB).
Configured limit: Some(1) MB.
Current usage: 1.0001220703125 MB.
This prevents system memory exhaustion.
```
### Solve-Time Checks
If memory limit was exceeded during model building:
```rust
match m.solve() {
Err(SolverError::MemoryLimit { usage_mb, limit_mb }) => {
println!("Memory limit exceeded: used {}MB, limit {}MB",
usage_mb.unwrap_or(0), limit_mb.unwrap_or(0));
}
// ... other cases
}
```
## 🎯 Best Practices
### 1. Choose Appropriate Limits
```rust
// For testing/development
let config = SolverConfig::default().with_max_memory_mb(100);
// For production batch processing
let config = SolverConfig::default()
.with_max_memory_mb(4096)
.with_timeout_ms(600000); // 600000ms = 10 minutes
// For real-time applications
let config = SolverConfig::default()
.with_max_memory_mb(256)
.with_timeout_ms(5000); // 5000ms = 5 seconds
```
### 2. Monitor Memory Usage
```rust
let mut m = Model::with_config(config);
// Check memory periodically during model building
if m.estimated_memory_mb() > 100.0 {
println!("Warning: High memory usage detected");
println!("{}", m.memory_breakdown());
}
```
### 3. Handle Failures Gracefully
```rust
use std::panic;
let result = panic::catch_unwind(|| {
let mut m = Model::with_config(config);
// This might panic if memory limit exceeded
for i in 0..10000 {
let var = m.float(0.0, 1000.0);
}
m.solve()
});
match result {
Ok(solve_result) => { /* Handle solve result */ }
Err(_) => {
println!("Model building failed - likely memory limit exceeded");
// Fallback strategy or reduce problem size
}
}
```
## 🔍 Accuracy & Performance
### Memory Estimation Accuracy
- **Float variables**: Very accurate (~64 bytes actual vs ~64 bytes estimated)
- **Integer variables**: Good accuracy for typical domains
- **Large sparse domains**: Conservative estimation (usually under-estimates by 20-30%)
### Performance Impact
- **Variable creation**: <1% overhead for memory tracking
- **Memory checking**: O(1) per variable creation
- **No runtime overhead**: Zero impact during constraint propagation/solving
### Calibration
Based on testing with real workloads:
- **1MB limit**: ~8,000 float variables or ~1,000 integer variables (domain size 100)
- **100MB limit**: ~800,000 float variables
- **1GB limit**: ~8,000,000 float variables
## ⚙️ Advanced Configuration
### Environment-Specific Configs
```rust
fn create_solver_config() -> SolverConfig {
// Detect environment and adjust limits
match std::env::var("ENVIRONMENT") {
Ok(env) if env == "production" => {
SolverConfig::default()
.with_max_memory_mb(2048)
.with_timeout_ms(300000) // 300000ms = 5 minutes
}
Ok(env) if env == "testing" => {
SolverConfig::default()
.with_max_memory_mb(256)
.with_timeout_ms(10000) // 10000ms = 10 seconds
}
_ => SolverConfig::default() // Development defaults
}
}
```
### Integration with Monitoring
```rust
struct ModelMetrics {
memory_used: f64,
variables_created: usize,
constraints_added: usize,
}
impl ModelMetrics {
fn collect(model: &Model) -> Self {
Self {
memory_used: model.estimated_memory_mb(),
variables_created: model.var_count(),
constraints_added: model.constraint_count(),
}
}
}
```
## 🏗️ Architecture Details
### Memory Tracking Location
Memory limits are enforced at the lowest level during variable creation:
1. **Variable Creation** (`Model::float`, `Model::int`, etc.)
2. **Memory Estimation** (based on domain type/size)
3. **Limit Checking** (cumulative usage vs configured limit)
4. **Early Failure** (panic with clear message if exceeded)
### Design Principles
- **Fail Fast**: Detect memory issues during model building, not solving
- **Clear Errors**: Descriptive messages with actual vs limit usage
- **Zero Overhead**: No performance impact during normal operation
- **Conservative**: Better to under-estimate than over-estimate memory needs
### Alternative Approaches Considered
1. **OS-level monitoring**: Too slow and platform-dependent
2. **Periodic checks**: Could miss rapid memory growth
3. **Lazy evaluation**: Would allow memory buildup before detection
4. **Real memory tracking**: Significant performance overhead
## 📚 Related Documentation
- **[SolverConfig API](../src/utils/config.rs)** - Complete configuration options
- **[Error Handling](../src/core/error.rs)** - Memory limit error types
- **[Performance Guide](performance.md)** - Performance optimization strategies
- **[Production Deployment](production.md)** - Production environment setup