use tower_layer::{Layer, Stack};
use crate::{
backend::Backend,
worker::{
builder::WorkerBuilder,
ext::circuit_breaker::{config::CircuitBreakerConfig, service::CircuitBreakerLayer},
},
};
pub mod config;
mod service;
pub trait CircuitBreaker<Args, Source, Middleware>: Sized {
fn break_circuit(self) -> WorkerBuilder<Args, Source, Stack<CircuitBreakerLayer, Middleware>> {
self.break_circuit_with(CircuitBreakerConfig::default())
}
fn break_circuit_with(
self,
cfg: CircuitBreakerConfig,
) -> WorkerBuilder<Args, Source, Stack<CircuitBreakerLayer, Middleware>>;
}
impl<Args, P, M> CircuitBreaker<Args, P, M> for WorkerBuilder<Args, P, M>
where
P: Backend,
M: Layer<CircuitBreakerLayer>,
{
fn break_circuit_with(
self,
config: CircuitBreakerConfig,
) -> WorkerBuilder<Args, P, Stack<CircuitBreakerLayer, M>> {
let this = self.layer(CircuitBreakerLayer::new(config));
WorkerBuilder {
context: this.context,
request: this.request,
layer: this.layer,
source: this.source,
shutdown: this.shutdown,
event_handler: this.event_handler,
}
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use tower::limit::ConcurrencyLimitLayer;
use crate::{
backend::{TaskSink, memory::MemoryStorage},
error::BoxDynError,
worker::{
builder::WorkerBuilder,
context::WorkerContext,
ext::{circuit_breaker::CircuitBreaker, event_listener::EventListenerExt},
},
};
use super::*;
const ITEMS: u32 = 10;
#[tokio::test]
async fn basic_worker() {
let mut in_memory = MemoryStorage::new();
for i in 0..ITEMS {
in_memory.push(i).await.unwrap();
}
async fn task(task: u32, worker: WorkerContext) -> Result<(), BoxDynError> {
tokio::time::sleep(Duration::from_secs(1)).await;
if task == ITEMS - 1 {
return Err("Worker stopped!")?;
}
if task == 8 {
worker.stop().unwrap();
}
if task % 3 == 0 {
Ok(())
} else {
return Err("Expected Error stopped!")?;
}
}
let config = CircuitBreakerConfig::default()
.failure_threshold(1)
.recovery_timeout(Duration::from_secs(1))
.success_threshold(0.5)
.half_open_max_calls(1);
let worker = WorkerBuilder::new("rango-tango")
.backend(in_memory)
.break_circuit_with(config)
.layer(ConcurrencyLimitLayer::new(3))
.on_event(|_worker, ev| {
println!("On Event = {ev:?}");
})
.build(task);
worker.run().await.unwrap();
}
}