# Request Coalescer
A Rust library for coalescing identical asynchronous operations to prevent redundant work in concurrent systems.
## Overview
`request_coalescer` provides a `CoalescingService` that prevents redundant work in concurrent asynchronous systems by ensuring that the underlying expensive operation is executed only once when multiple requests for the same "key" arrive simultaneously.
This is particularly useful for:
- Deduplicating database reads or API calls for identical resources
- Throttling access to rate-limited external services
- Optimizing expensive computations
- Reducing load on downstream services
## Features
- **Asynchronous**: Built on Tokio for efficient async operation
- **Key-based Coalescing**: Deduplicate operations based on any hashable key
- **Optional Timeouts**: Set global or per-operation timeouts
- **Detailed Statistics**: Track coalesced, failed, and timed-out operations
- **Error Handling**: Propagate errors to all waiting clients
- **Configurable**: Customize behavior with various configuration options
## Installation
Add the following to your `Cargo.toml`:
```toml
[dependencies]
request_coalescer = "0.1.0"
tokio = { version = "1", features = ["full"] } # Or specific features
anyhow = "1"
# Optional for logging/tracing:
# tracing = "0.1"
# tracing-subscriber = "0.3" # For development/examples only
```
## Basic Usage
Here's a simple example of how to use the `CoalescingService`:
```rust
use request_coalescer::CoalescingService;
use std::sync::Arc;
use anyhow::Result;
async fn fetch_data(id: &str) -> Result<String> {
// Simulate expensive operation
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
Ok(format!("Data for {}", id))
}
#[tokio::main]
async fn main() -> Result<()> {
// Create a new coalescing service
let service: CoalescingService<String, String> = CoalescingService::new();
// Execute multiple requests with the same key
let key = "user:123".to_string();
// These will be coalesced into a single operation
let handle1 = {
let svc = service.clone();
let k = key.clone();
tokio::spawn(async move {
svc.execute(k, || fetch_data("user:123")).await
})
};
let handle2 = {
let svc = service.clone();
let k = key.clone();
tokio::spawn(async move {
svc.execute(k, || fetch_data("user:123")).await
})
};
// Both will get the same result, but the operation runs only once
let result1 = handle1.await??;
let result2 = handle2.await??;
assert_eq!(result1, result2);
Ok(())
}
```
## Advanced Usage
### Custom Configuration
You can customize the `CoalescingService` with various configuration options:
```rust
use request_coalescer::{CoalescingService, service::CoalescingConfig};
use std::time::Duration;
async fn main() -> Result<()> {
let config = CoalescingConfig {
timeout: Some(Duration::from_millis(200)),
max_concurrent_ops: Some(2),
auto_cleanup: false,
};
let service: CoalescingService<String, String> = CoalescingService::with_config(config);
Ok(())
}
```
### Timeout Handling
The library provides built-in timeout handling:
```rust
use request_coalescer::CoalescingService;
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<()> {
// Create a service with a global timeout of 1 second
let service: CoalescingService<String, String> =
CoalescingService::with_timeout(Duration::from_secs(1));
// Execute with a specific timeout
let result = service.execute_with_timeout(
"resource".to_string(),
Duration::from_millis(500),
|| slow_api_call("resource")
).await;
Ok(())
}
```
## API Documentation
For detailed API documentation, please run `cargo doc --open` or visit [docs.rs](https://docs.rs/request_coalescer) (once published).
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is licensed under the MIT License or Apache License 2.0, at your option.
## Acknowledgments
- This library is built on top of the Tokio async runtime.
- Thanks to the Rust community for providing excellent tools and libraries that made this project possible.