A circuit breaker implementation in Moonbit
Dependencies
///|
test "basic circuit breaker usage" {
// Create a circuit breaker with default settings
let settings : @ryota0624/circuit_breaker.Settings[Int, String] = @ryota0624/circuit_breaker.Settings::default()
let cb = @ryota0624/circuit_breaker.CircuitBreaker::new(settings)
// Execute a function through the circuit breaker
let result = cb.run_sync(fn() -> Result[Int, String] { Ok(42) })
inspect(result, content="Success(42)")
}///|
test "custom circuit breaker configuration" {
let mut current_time = 0L
let settings : @ryota0624/circuit_breaker.Settings[Int, String] = @ryota0624/circuit_breaker.Settings::default()
.with_name("MyService")
.with_timeout(5000L) // 5 seconds timeout in Open state
.with_max_requests(3L) // Allow 3 requests in HalfOpen state
.with_get_now(fn() { current_time }) // Custom time source for testing
let cb = @ryota0624/circuit_breaker.CircuitBreaker::new(settings)
// Simulate failures to trip the circuit
for i = 0; i < 6; i = i + 1 {
let _ = cb.run_sync(fn() -> Result[Int, String] {
Err("service unavailable")
})
}
// Circuit should be open now
inspect(cb.state(), content="Open")
// Requests are rejected in Open state
let result = cb.run_sync(fn() -> Result[Int, String] { Ok(1) })
inspect(result, content="Rejected(OpenCircuit)")
// Advance time past timeout
current_time = 5000L
// Circuit transitions to HalfOpen and allows request
let result2 = cb.run_sync(fn() -> Result[Int, String] { Ok(1) })
inspect(result2, content="Success(1)")
}///|
test "error handling with circuit breaker" {
let settings : @ryota0624/circuit_breaker.Settings[String, String] = @ryota0624/circuit_breaker.Settings::default()
let cb = @ryota0624/circuit_breaker.CircuitBreaker::new(settings)
// Handle different result types
let result = cb.run_sync(fn() -> Result[String, String] { Err("error") })
inspect(result, content="Failure(\"error\")")
}pub suberror CircuitBreakerError {
TooManyRequests
OpenCircuit
}impl Show for CircuitBreakerErrorasync fn[T, E] CircuitBreaker::run_async(self : CircuitBreaker[T, E], f : async () -> Result[T, E] noraise) -> CircuitBreakerRunResult[T, E] noraisefn[T, E] CircuitBreaker::run_sync(self : CircuitBreaker[T, E], f : () -> Result[T, E]) -> CircuitBreakerRunResult[T, E]async fn[T, E : Error] CircuitBreaker::try_async(self : CircuitBreaker[T, E], f : async () -> T raise E) -> Tfn[T, E : Error] CircuitBreaker::try_sync(self : CircuitBreaker[T, E], f : () -> T raise E) -> T raiseimpl Show for CircuitBreakerRunResult[T, E]fn[T, E, U] CircuitBreakerRunResult::map(self : CircuitBreakerRunResult[T, E], f : (T) -> U) -> CircuitBreakerRunResult[U, E]fn[T, E, U] CircuitBreakerRunResult::map_errors(self : CircuitBreakerRunResult[T, E], map_failure : (E) -> U, map_rejected : (CircuitBreakerError) -> U) -> Result[T, U]fn[T, E : Error] CircuitBreakerRunResult::unwrap_or_error(self : CircuitBreakerRunResult[T, E]) -> T raisepub enum ConsecutiveResult {
Successes(Int64)
Failures(Int64)
}impl Eq for ConsecutiveResultimpl Show for ConsecutiveResultpub struct Counts {
requests : Int64
total_successes : Int64
total_failures : Int64
consecutive : ConsecutiveResult
}