# Memlink Runtime
**Dynamic module loading and execution framework for Rust**
<table>
<tr>
<td><a href="https://crates.io/crates/memlink-runtime"><img src="https://img.shields.io/crates/v/memlink-runtime.svg" alt="Crates.io"/></a></td>
<td><a href="https://docs.rs/memlink-runtime"><img src="https://docs.rs/memlink-runtime/badge.svg" alt="Docs"/></a></td>
<td><a href="../LICENSE-APACHE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License"/></a></td>
<td><a href="https://www.rust-lang.org"><img src="https://img.shields.io/badge/rust-1.70%2B-orange.svg" alt="Rust"/></a></td>
</tr>
<tr>
<td><a href="docs/PERFORMANCE.md"><img src="https://img.shields.io/badge/benchmarks-included-purple" alt="Benchmarks"/></a></td>
<td><a href="../shm/README.md"><img src="https://img.shields.io/badge/sibling-shm-blue" alt="SHM"/></a></td>
<td><a href="examples/README.md"><img src="https://img.shields.io/badge/examples-included-green" alt="Examples"/></a></td>
</tr>
</table>
---
## Overview
Memlink Runtime is a high-performance dynamic module loading system that enables **plugin architectures**, **hot-reloadable code**, and **safe FFI execution**. Load shared libraries at runtime, call methods on them, and unload them without restarting your application.
### Key Features
- ð **Dynamic Loading** - Load `.so`, `.dll`, `.dylib` files at runtime
- ðĄïļ **Panic Isolation** - Module panics don't crash your application
- ðĨ **Hot Reload** - Replace modules with zero downtime
- ð **Prometheus Metrics** - Built-in observability
- ð§ĩ **Thread-Safe** - Concurrent module calls from multiple threads
- ðĶ **Multi-Module** - Load and manage multiple modules simultaneously
- ⥠**Circuit Breaker** - Prevent cascading failures with automatic recovery
- ðïļ **Request Caching** - Cache responses for improved performance
- ð **Module Pooling** - Multiple instances with load balancing
- âĪïļ **Health Checks** - Liveness and readiness probes
- ð **Dependency Management** - Track module dependencies and load order
- ðĶ **Version Management** - Side-by-side versions with gradual migration
- ð **Request Batching** - Combine requests for better throughput
- ð **Sandboxing** - Resource limits and permission control
- ðĄ **Event System** - Lifecycle hooks and middleware
- ð **Auto-Discovery** - Watch directories and auto-load modules
---
## Quick Start
### Basic Example
```rust
use memlink_runtime::runtime::{Runtime, ModuleRuntime};
use memlink_runtime::resolver::ModuleRef;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create runtime
let runtime = Runtime::with_local_resolver();
// Load a module
let handle = runtime.load(
ModuleRef::parse("./my_module.so")?
)?;
// Call a method
let result = runtime.call(handle, "process", b"input data")?;
println!("Result: {:?}", String::from_utf8_lossy(&result));
// Unload when done
runtime.unload(handle)?;
Ok(())
}
```
---
## Creating Modules
Modules are shared libraries that export three required functions:
### Minimal Module (C)
```c
#include <stdint.h>
#include <string.h>
__attribute__((visibility("default")))
int memlink_init(const unsigned char* config, unsigned long config_len) {
(void)config;
(void)config_len;
return 0;
}
__attribute__((visibility("default")))
int memlink_call(unsigned int method_id, const unsigned char* args,
unsigned long args_len, unsigned char* output) {
(void)method_id;
// Echo input back
if (args_len > 0 && args != NULL) {
memcpy(output, args, args_len);
}
return 0;
}
__attribute__((visibility("default")))
int memlink_shutdown(void) {
return 0;
}
```
### Build Commands
| Linux | `cc -shared -fPIC -O2 -o my_module.so my_module.c` |
| Windows | `cl /LD my_module.c /Fe:my_module.dll` |
| macOS | `cc -shared -fPIC -O2 -o my_module.dylib my_module.c` |
See [ABI Documentation](docs/abi.md) for full specification.
---
## Advanced Usage
### Loading Multiple Modules
```rust
use memlink_runtime::runtime::{Runtime, ModuleRuntime};
use memlink_runtime::resolver::ModuleRef;
use std::sync::Arc;
use std::thread;
let runtime = Arc::new(Runtime::with_local_resolver());
// Load multiple modules
let math = runtime.load(ModuleRef::parse("./math.so")?)?;
let string = runtime.load(ModuleRef::parse("./string.so")?)?;
let crypto = runtime.load(ModuleRef::parse("./crypto.so")?)?;
// Call concurrently from different threads
let mut handles = vec![];
for (name, module) in [("math", math), ("string", string), ("crypto", crypto)] {
let rt = Arc::clone(&runtime);
let h = thread::spawn(move || {
for i in 0..100 {
rt.call(module, "process", format!("{}_{}", name, i).as_bytes()).unwrap();
}
});
handles.push(h);
}
for h in handles {
h.join().unwrap();
}
```
### Hot Reload
```rust
use memlink_runtime::reload::ReloadConfig;
use std::time::Duration;
// Reload a module with new version
let config = ReloadConfig::default()
.with_drain_timeout(Duration::from_secs(30))
.with_state_preservation();
let reload_state = runtime.reload_with_config(
old_handle,
ModuleRef::parse("./my_module_v2.so")?,
config
)?;
// Old module drains in-flight calls before unloading
```
### Metrics and Monitoring
```rust
// Get usage statistics
let usage = runtime.get_usage(handle).unwrap();
println!("Calls: {}", usage.call_count);
println!("Arena: {} bytes ({:.2}%)",
usage.arena_bytes,
usage.arena_usage * 100.0
);
// Export Prometheus metrics
let metrics = RuntimeMetrics::new();
// ... after operations ...
println!("{}", metrics.prometheus_export());
```
### Circuit Breaker Pattern
```rust
use memlink_runtime::{CircuitRegistry, CircuitConfig};
let registry = CircuitRegistry::new();
// Configure circuit breaker
let config = CircuitConfig {
failure_threshold: 5,
success_threshold: 3,
open_timeout: std::time::Duration::from_secs(30),
..Default::default()
};
registry.register("risky_module", config);
// Check before calling
if registry.can_execute("risky_module") {
match runtime.call(handle, "risky_op", data) {
Ok(result) => registry.record_success("risky_module"),
Err(_) => registry.record_failure("risky_module"),
}
} else {
// Circuit is open, reject immediately
eprintln!("Request rejected due to circuit open");
}
```
### Request Caching
```rust
use memlink_runtime::{RequestCache, CacheConfig};
let config = CacheConfig {
default_ttl: std::time::Duration::from_secs(60),
max_entries: 10000,
max_memory_bytes: 256 * 1024 * 1024,
};
let cache = RequestCache::new(config);
let key = RequestCache::generate_key("expensive_method", &input);
// Check cache first
if let Some(cached) = cache.get(key) {
return Ok(cached);
}
// Cache miss - compute and cache
let result = runtime.call(handle, "expensive_method", &input)?;
cache.set(key, result.clone(), None);
Ok(result)
```
### Module Pooling & Load Balancing
```rust
use memlink_runtime::{ModulePool, PoolConfig, LoadBalanceStrategy};
let config = PoolConfig {
min_instances: 2,
max_instances: 8,
target_load: 0.6,
scale_up_threshold: 0.8,
..Default::default()
};
let pool = ModulePool::new("worker".to_string(), config);
// Add multiple instances
for path in &["worker1.so", "worker2.so", "worker3.so"] {
let instance = runtime.load(ModuleRef::parse(*path)?)?;
pool.add_instance(instance);
}
// Select instance based on load balancing strategy
if let Some(instance) = pool.select_instance() {
let result = runtime.call(instance, "work", input)?;
}
```
### Health Checks
```rust
use memlink_runtime::{HealthRegistry, HealthConfig};
let config = HealthConfig {
check_interval: std::time::Duration::from_secs(10),
timeout: std::time::Duration::from_secs(5),
failure_threshold: 3,
..Default::default()
};
let registry = HealthRegistry::new();
let tracker = registry.get_or_create("database_module");
// Record health check results
tracker.record_success(latency_us).await;
// or
tracker.record_failure(Some("connection timeout".to_string())).await;
// Check health status
match tracker.status().await {
HealthStatus::Healthy => println!("Module is healthy"),
HealthStatus::Unhealthy => println!("Module is unhealthy!"),
HealthStatus::Unknown => println!("Health unknown"),
}
```
### Dependency Management
```rust
use memlink_runtime::{DependencyGraph, ModuleDependency};
let graph = DependencyGraph::new();
// Define dependencies
graph.add_module("database", vec![]);
graph.add_module("cache", vec![
ModuleDependency { name: "database".to_string(), min_version: None, optional: false }
]);
graph.add_module("api", vec![
ModuleDependency { name: "cache".to_string(), min_version: None, optional: false },
ModuleDependency { name: "database".to_string(), min_version: None, optional: true }
]);
// Get correct load order
let order = graph.load_order()?; // ["database", "cache", "api"]
// Load in order
for module_name in order {
runtime.load(ModuleRef::parse(&format!("{}.so", module_name))?)?;
}
```
### Version Management
```rust
use memlink_runtime::{VersionManager, ModuleVersion, TrafficRouting};
let manager = VersionManager::new("payment_processor".to_string());
// Register multiple versions
manager.register_version(ModuleVersion::new(1, 0, 0), "/path/v1.so");
manager.register_version(ModuleVersion::new(2, 0, 0), "/path/v2.so");
// Activate v1
manager.activate_version(&ModuleVersion::new(1, 0, 0));
// Configure gradual migration (10% to v2)
let routing = TrafficRouting {
new_version_percentage: 10,
min_requests: 100,
error_threshold: 0.01,
};
manager.set_routing(routing);
// Select version for each request
if let Some(version) = manager.select_version() {
// Route to appropriate version
}
```
### Event System
```rust
use memlink_runtime::{EventRegistry, ModuleEvent, EventListener};
struct MyListener;
impl EventListener for MyListener {
fn on_event(&self, event: &ModuleEvent) {
match event {
ModuleEvent::Loaded { name, duration_ms } => {
println!("Module {} loaded in {}ms", name, duration_ms);
}
ModuleEvent::CallCompleted { module, method, duration_us, success } => {
println!("{}.{} completed in {}Ξs (success: {})",
module, method, duration_us, success);
}
_ => {}
}
}
}
let registry = EventRegistry::new();
registry.register("my_listener", Arc::new(MyListener));
// Events are automatically emitted during module operations
```
---
## Architecture
```
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Your Application â
â (Memlink Runtime) â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââĪ
â Runtime Components â
â ââ Resolver (locate modules) â
â ââ Loader (load shared libs) â
â ââ Instance Manager (track loaded modules) â
â ââ Panic Handler (catch module panics) â
â ââ Circuit Breaker (fault tolerance) â
â ââ Request Cache (performance) â
â ââ Module Pool (load balancing) â
â ââ Health Tracker (liveness/readiness) â
â ââ Dependency Graph (load order) â
â ââ Version Manager (gradual migration) â
â ââ Request Batcher (throughput) â
â ââ Sandbox (resource limits) â
â ââ Event Registry (lifecycle hooks) â
â ââ Discovery (auto-loading) â
â ââ Metrics (Prometheus-compatible) â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â
âââ [module_a.so] ââ memlink_init/call/shutdown
âââ [module_b.dll] ââ memlink_init/call/shutdown
âââ [module_c.dylib] ââ memlink_init/call/shutdown
```
### Components
| **Runtime** | High-level API for module management |
| **Resolver** | Locates and validates module files |
| **Loader** | Loads shared libraries and resolves symbols |
| **Instance** | Represents a loaded module |
| **Arena** | Fast bump allocator for module memory |
| **Metrics** | Collects and exports runtime statistics |
| **CircuitBreaker** | Fault tolerance with automatic recovery |
| **RequestCache** | Response caching with TTL |
| **ModulePool** | Instance pooling with load balancing |
| **HealthTracker** | Liveness and readiness probes |
| **DependencyGraph** | Module dependency tracking |
| **VersionManager** | Side-by-side version management |
| **RequestBatcher** | Request batching for throughput |
| **ResourceTracker** | Resource usage monitoring |
| **EventRegistry** | Lifecycle event emission |
| **ModuleDiscovery** | Directory watching and auto-loading |
---
## Performance
| Module load | 92 Ξs | - |
| Method call (64 bytes) | 210 ns | 4.7M calls/sec (single thread) |
| Module unload | 52 Ξs | - |
| Hot reload | 237 Ξs | - |
| Concurrent calls (8 threads) | - | 2.5M calls/sec |
| Memory overhead | 0.7 MB/module | - |
**Key Highlights:**
- â
Sub-microsecond call latency
- â
Linear scalability up to 8 threads
- â
Fast hot reload with zero downtime
- â
Low memory footprint
See [Performance Benchmarks](docs/perf.md) for detailed methodology, full results, and optimization recommendations.
---
## Use Cases
### Plugin Systems
Load user-created plugins without recompiling your application:
```rust
// Load all plugins from a directory
for entry in std::fs::read_dir("./plugins")? {
let path = entry?.path();
if path.extension() == Some("so".as_ref()) {
runtime.load(ModuleRef::parse(path.to_str().unwrap())?)?;
}
}
```
### Hot-Reloadable Business Logic
Update logic in production without downtime:
```rust
// Watch for file changes
runtime.reload(handle, ModuleRef::parse("./business_logic.so")?)?;
}
})?;
```
### Sandboxed Execution
Isolate risky code that might panic:
```rust
// Module panics are caught and converted to errors
match runtime.call(handle, "risky_operation", data) {
Ok(result) => println!("Success"),
Err(Error::ModulePanicked(msg)) => eprintln!("Module panicked: {}", msg),
Err(e) => eprintln!("Error: {}", e),
}
```
---
## API Reference
### Core Traits
| `ModuleRuntime` | Main interface for module operations |
| `ModuleResolver` | Resolve module references to artifacts |
### Key Types
| `Runtime` | Default runtime implementation |
| `ModuleHandle` | Opaque handle to loaded module |
| `ModuleRef` | Module reference (path or registry) |
| `ModuleUsage` | Usage statistics per module |
| `ReloadState` | Tracks hot-reload operations |
| `CircuitBreaker` | Circuit breaker for fault tolerance |
| `CircuitRegistry` | Registry of circuit breakers |
| `RequestCache` | Response cache with TTL |
| `ModulePool` | Pool of module instances |
| `HealthTracker` | Health check tracker |
| `HealthRegistry` | Registry of health trackers |
| `DependencyGraph` | Module dependency graph |
| `VersionManager` | Version management |
| `TrafficRouting` | Gradual migration config |
| `RequestBatcher` | Request batching |
| `ResourceTracker` | Resource usage tracking |
| `EventRegistry` | Event emission |
| `ModuleDiscovery` | Module auto-discovery |
[Full API Documentation](https://docs.rs/memlink-runtime)
---
## Examples
Run the included examples:
```bash
# Multi-module concurrent calls
cargo run --example m2
# Build test modules first (Linux/WSL)
cd examples/modules/build
node build.js --linux
```
See [examples/modules/README.md](examples/modules/README.md) for module documentation.
---
## License
Memlink Runtime is licensed under the Apache License 2.0 ([LICENSE](../LICENSE).
---
## Contributing
Contributions are welcome! Please:
1. Fork the repository
2. Create a feature branch
3. Run `cargo test` and `cargo clippy`
4. Submit a pull request
### Development
```bash
# Build
cargo build
# Test
cargo test
# Lint
cargo clippy --all-targets
# Format
cargo fmt --all
# Build docs
cargo doc --open
# Run integration tests
cargo test --test integration
```
### Testing
The runtime includes comprehensive test coverage:
- **41 unit tests** - Testing individual components
- **13 integration tests** - Testing all 10 enterprise features
- **6 platform-specific tests** - Linux-only module tests
```bash
# Run all tests
cargo test -p memlink-runtime
# Run only integration tests
cargo test --test integration
# Run with output
cargo test -- --nocapture
```
---
## Related Crates
- [memlink-shm](../shm/README.md) - Shared memory IPC
- [libloading](https://crates.io/crates/libloading) - Underlying library loading
- [dashmap](https://crates.io/crates/dashmap) - Concurrent hash map
---
## Support
- **Issues**: [GitHub Issues](https://github.com/memlink/memlink/issues)
- **Documentation**: [docs.rs](https://docs.rs/memlink-runtime)
- **ABI Spec**: [docs/abi.md](docs/abi.md)