|
|
|
@@ -1,39 +1,92 @@
|
|
|
|
|
# MAD
|
|
|
|
|
# MAD - Lifecycle Manager for Rust Applications
|
|
|
|
|
|
|
|
|
|
Mad is a life-cycle manager for long running rust operations.
|
|
|
|
|
[](https://crates.io/crates/notmad)
|
|
|
|
|
[](https://docs.rs/notmad)
|
|
|
|
|
[](https://opensource.org/licenses/MIT)
|
|
|
|
|
|
|
|
|
|
- Webservers
|
|
|
|
|
- Queue bindings
|
|
|
|
|
- gRPC servers etc
|
|
|
|
|
- Cron runners
|
|
|
|
|
## Overview
|
|
|
|
|
|
|
|
|
|
It is supposed to be the main thing the application runs, and everything from it is spawned and managed by it.
|
|
|
|
|
MAD is a robust lifecycle manager designed for long-running Rust operations. It provides a simple, composable way to manage multiple concurrent services within your application, handling graceful startup and shutdown automatically.
|
|
|
|
|
|
|
|
|
|
### Perfect for:
|
|
|
|
|
- 🌐 Web servers
|
|
|
|
|
- 📨 Queue consumers and message processors
|
|
|
|
|
- 🔌 gRPC servers
|
|
|
|
|
- ⏰ Cron job runners
|
|
|
|
|
- 🔄 Background workers
|
|
|
|
|
- 📡 Any long-running async operations
|
|
|
|
|
|
|
|
|
|
## Features
|
|
|
|
|
|
|
|
|
|
- **Component-based architecture** - Build your application from composable, reusable components
|
|
|
|
|
- **Graceful shutdown** - Automatic handling of shutdown signals with proper cleanup
|
|
|
|
|
- **Concurrent execution** - Run multiple components in parallel with tokio
|
|
|
|
|
- **Error handling** - Built-in error propagation and logging
|
|
|
|
|
- **Cancellation tokens** - Coordinate shutdown across all components
|
|
|
|
|
- **Minimal boilerplate** - Focus on your business logic, not lifecycle management
|
|
|
|
|
|
|
|
|
|
## Installation
|
|
|
|
|
|
|
|
|
|
Add MAD to your `Cargo.toml`:
|
|
|
|
|
|
|
|
|
|
```toml
|
|
|
|
|
[dependencies]
|
|
|
|
|
notmad = "0.7.5"
|
|
|
|
|
tokio = { version = "1", features = ["full"] }
|
|
|
|
|
async-trait = "0.1"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Quick Start
|
|
|
|
|
|
|
|
|
|
Here's a simple example of a component that simulates a long-running server:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
struct WaitServer {}
|
|
|
|
|
use mad::{Component, Mad};
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
use tokio_util::sync::CancellationToken;
|
|
|
|
|
|
|
|
|
|
// Define your component
|
|
|
|
|
struct WebServer {
|
|
|
|
|
port: u16,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl Component for WaitServer {
|
|
|
|
|
impl Component for WebServer {
|
|
|
|
|
fn name(&self) -> Option<String> {
|
|
|
|
|
Some("NeverEndingRun".into())
|
|
|
|
|
Some(format!("WebServer on port {}", self.port))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
|
|
|
|
|
let millis_wait = rand::thread_rng().gen_range(50..1000);
|
|
|
|
|
println!("Starting web server on port {}", self.port);
|
|
|
|
|
|
|
|
|
|
// Simulates a server running for some time. Is normally supposed to be futures blocking indefinitely
|
|
|
|
|
tokio::time::sleep(std::time::Duration::from_millis(millis_wait)).await;
|
|
|
|
|
// Your server logic here
|
|
|
|
|
// The cancellation token will be triggered on shutdown
|
|
|
|
|
tokio::select! {
|
|
|
|
|
_ = cancellation.cancelled() => {
|
|
|
|
|
println!("Shutting down web server");
|
|
|
|
|
}
|
|
|
|
|
_ = self.serve() => {
|
|
|
|
|
println!("Server stopped");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WebServer {
|
|
|
|
|
async fn serve(&self) {
|
|
|
|
|
// Simulate a running server
|
|
|
|
|
tokio::time::sleep(std::time::Duration::from_secs(3600)).await;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::main]
|
|
|
|
|
async fn main() -> anyhow::Result<()> {
|
|
|
|
|
// Build and run your application
|
|
|
|
|
Mad::builder()
|
|
|
|
|
.add(WaitServer {})
|
|
|
|
|
.add(WaitServer {})
|
|
|
|
|
.add(WaitServer {})
|
|
|
|
|
.add(WebServer { port: 8080 })
|
|
|
|
|
.add(WebServer { port: 8081 }) // You can add multiple instances
|
|
|
|
|
.run()
|
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
|
@@ -41,11 +94,75 @@ async fn main() -> anyhow::Result<()> {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Advanced Usage
|
|
|
|
|
|
|
|
|
|
### Custom Components
|
|
|
|
|
|
|
|
|
|
Components can be anything that implements the `Component` trait:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use mad::{Component, Mad};
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
|
|
|
|
|
struct QueueProcessor {
|
|
|
|
|
queue_name: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl Component for QueueProcessor {
|
|
|
|
|
fn name(&self) -> Option<String> {
|
|
|
|
|
Some(format!("QueueProcessor-{}", self.queue_name))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
|
|
|
|
|
while !cancellation.is_cancelled() {
|
|
|
|
|
// Process messages from queue
|
|
|
|
|
self.process_next_message().await?;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Error Handling
|
|
|
|
|
|
|
|
|
|
MAD provides comprehensive error handling through the `MadError` type with automatic conversion from `anyhow::Error`:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
async fn run(&self, cancellation: CancellationToken) -> Result<(), mad::MadError> {
|
|
|
|
|
// Errors automatically convert from anyhow::Error to MadError
|
|
|
|
|
database_operation().await?;
|
|
|
|
|
|
|
|
|
|
// Or return explicit errors
|
|
|
|
|
if some_condition {
|
|
|
|
|
return Err(anyhow::anyhow!("Something went wrong").into());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Examples
|
|
|
|
|
|
|
|
|
|
Can be found (here)[crates/mad/examples]
|
|
|
|
|
Check out the [examples directory](crates/mad/examples) for more detailed examples:
|
|
|
|
|
|
|
|
|
|
- basic
|
|
|
|
|
- fn
|
|
|
|
|
- signals
|
|
|
|
|
- error_log
|
|
|
|
|
- **basic** - Simple component lifecycle
|
|
|
|
|
- **fn** - Using functions as components
|
|
|
|
|
- **signals** - Handling system signals
|
|
|
|
|
- **error_log** - Error handling and logging
|
|
|
|
|
|
|
|
|
|
## Contributing
|
|
|
|
|
|
|
|
|
|
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
|
|
|
|
|
|
|
|
## Author
|
|
|
|
|
|
|
|
|
|
Created and maintained by [kjuulh](https://github.com/kjuulh)
|
|
|
|
|
|
|
|
|
|
## Repository
|
|
|
|
|
|
|
|
|
|
Find the source code at [https://github.com/kjuulh/mad](https://github.com/kjuulh/mad)
|
|
|
|
|