[Update Jan 1., 2010: A couple of people have been linking and twitting this, so I've made the blog entry match the current Clojure version at github. Code works in the 'new' branch of Clojure: tested with commit 3ae9e8874d43f9fd37e59bb7ea8cce0f85bac101.
There is support for creating several circuit breakers wrapping given functions].
As an exercise in Clojure, I've implemented a mostly functional version of Michael Nygaard's stability pattern "Circuit breaker" (http://www.pragprog.com/titles/mnee/release-it
The implementation uses the new constructs deftype
(which are looking really interesting to me!). The implementation maintains a single identity named "state" which is an atom holding the current state (open, closed, initial-half-open or pending-half-open).
More on deftype and defprotocol can be found here
). Notice how the events 'on-before-call', 'on-success' and 'on-error' are implemented as a protocol defining state-transition functions. I really like the features Rich is adding to Clojure - to me it makes modeling so much more natural (with an OO-background), while retaining the benefits of functional programming.
Notice how this snipplet defines the transition functions, the four states as well as an "abstract" implementation of the state transitions. The abs-transitions can be used as default implementations when extending the protocol to the state-types. In Java, this would correspond to an interface (CircuitBreakerTransitions), an abstract super-class implementing the interface, and four immutable classes (the states). However, Clojures deftype and defprotocol does not create a type-hierarchy, and is much more dynamic (you can always define how a given type extends to a protocol - it is not fixed at type definition time). Here is how I extended my types to the protocol:
This defines a completely functional transition-system between the four states. I think this reads really well, for example consider the on-error event in the closed state: this checks to see if the states fail-count is equal to the threshold defined in the policy, if so it transitions to the open state, recording the current time (OpenState p (System/currentTimeMillis))
(preserving the policy); otherwise it transitions to a closed state with a higher fail-count.
The actual circuit breaker uses these transition functions:
The state of a circuit breaker is an atom (the only mutable construct in this example). The function wrap takes a function f and returns a "wrapped" version of f, which implements the circuit breaker functionality around f.
Notice how dead-simple this is to test: