circuit-breaker: An implementation of the "circuit breaker" pattern to disable repeated calls to a failing system

[ bsd3, library, program, system ] [ Propose Tags ]

Please see the README on GitHub at

[Skip to Readme]
Versions [faq]
Change log
Dependencies base (>=4.7 && <5), circuit-breaker, mtl, random, text, time, transformers, unliftio, unliftio-core, unordered-containers [details]
License BSD-3-Clause
Copyright 2019 Chris Coffey
Author Chris Coffey
Category System
Home page
Bug tracker
Source repo head: git clone
Uploaded by ChrisCoffey at 2019-03-24T22:19:04Z
Distributions NixOS:
Executables circuit-breaker-exe
Downloads 432 total (3 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Hackage Matrix CI
Docs available [build log]
Last success reported on 2019-03-24 [all 1 reports]


[Index] [Quick Jump]


Maintainer's Corner

For package maintainers and hackage trustees

Readme for circuit-breaker-

[back to package description]


Circuit breakers are an error handling machine inspired by the circuit breakers used in electrical systems. Just like their namesake, software circuit breakers prevent pushing traffic through a failing component until it has recovered.

As of the current version, all CircuitBreakers use a "leaky bucket" approach to backoffs. In the definition of a circuit breaker, the first Natural argument is the number of milliseconds that need to pass before an error is expunged. Coupled with an error threshold argument, this provides a surprising amount of flexibility for cirucit breakers in the wild, although you'll want to monitor and tune them over time.

Using a circuit breaker

The following short example illustrates how to define a circuit breaker & use it.

testBreaker :: CircuitBreaker "Test" 1000 4
testBreaker = undefined

main :: IO ()
main = do
    -- Initializes the empty storage for all circuit breakers
    cbConf <- initialBreakerState
    -- Perform a bunch of "work". Because we have a 50% failure rate and trigger the breaker after four
    -- errors, this will cause the breaker to disable additional calls.
    forM_ [1..30] $ const . noteError . flip runReaderT cbConf $ withBreaker testBreaker computation

    -- simulating a backoff long enough to decrement the accumulated errors.
    threadDelay 5000000

    noteError . flip runReaderT cbConf $ withBreaker testBreaker computation
        noteError action = print =<< catchAny action (const $ pure (Left Failed))

-- | This computation fails 50% of the time
computation :: ReaderT CircuitBreakerConf IO Int
computation = do
    shouldFail <- liftIO randomIO
    when shouldFail $ error "Failed"
    pure 42

The first thing to notice is that the CircuitBreaker definition exists entirely at the type level. This guarantees that the definition of a particular CircuitBreaker can't somehow change at runtime.

All calls to withBreaker require a CircuitBreakerConf to be present in a reader environment. initialBreakerState simply initializes an empty CircuitBreakerConf to save you the trouble of creating one yourself.


PRs and issues are welcome! Please let me know what you think could be improved or submit the patch yourself.