Created
October 7, 2015 15:29
-
-
Save Vengarioth/6a382ae95c79d8d0aae9 to your computer and use it in GitHub Desktop.
Lightweight, locking implementation of the Circuit Breaker pattern.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
namespace Integrations | |
{ | |
/// <summary> | |
/// Circuit Breaker stability pattern. | |
/// | |
/// The circuit breaker will reduce stress on external systems upon their failure | |
/// by limiting the calls made to it once a threshold on errors is reached | |
/// </summary> | |
public class CircuitBreaker | |
{ | |
private enum CircuitBreakerState | |
{ | |
//Closed means operations are carried out | |
Closed, | |
//Open means operations will fail automatically | |
Open, | |
//During HalfOpen, one operation is allowed, if it succeds the CircuitBreaker goes back to Closed | |
HalfOpen | |
} | |
private readonly string _name; | |
private readonly int _threshold; | |
private readonly TimeSpan _timeout; | |
private readonly object _lock; | |
private DateTime _timeoutEnd; | |
private int _failures; | |
private CircuitBreakerState _state; | |
/// <summary> | |
/// Constructor | |
/// </summary> | |
/// <param name="name">Name of the protected system</param> | |
/// <param name="threshold">Threshold at which the Circuit Breaker blocks operations to the protected system</param> | |
/// <param name="timeout">Timeout after which the Circuit Breaker will retry to operate with the protected system</param> | |
public CircuitBreaker(string name, int threshold, TimeSpan timeout) | |
{ | |
_name = name; | |
_threshold = threshold; | |
_timeout = timeout; | |
_lock = new object(); | |
} | |
/// <summary> | |
/// Attempt an operation on the protected system. | |
/// Exceptions thrown by the given action will be used | |
/// to measure the protected systems health | |
/// </summary> | |
/// <param name="protectedAction">The action on the protected System, monitored by the Circuit Breaker</param> | |
public void Attempt(Action protectedAction) | |
{ | |
switch (_state) | |
{ | |
case CircuitBreakerState.Open: | |
lock (_lock) | |
{ | |
if (DateTime.Now > _timeoutEnd) | |
{ | |
_state = CircuitBreakerState.HalfOpen; | |
} | |
else | |
{ | |
throw new IntegrationPointUnavailableException(_name); | |
} | |
} | |
break; | |
case CircuitBreakerState.HalfOpen: | |
throw new IntegrationPointUnavailableException(_name); | |
} | |
try | |
{ | |
protectedAction(); | |
} | |
catch (Exception e) | |
{ | |
HandleException(e); | |
throw new IntegrationPointUnavailableException(_name, e); | |
} | |
switch (_state) | |
{ | |
case CircuitBreakerState.Closed: | |
_failures = 0; | |
break; | |
case CircuitBreakerState.HalfOpen: | |
_state = CircuitBreakerState.Closed; | |
_failures = 0; | |
break; | |
} | |
} | |
private void HandleException(Exception e) | |
{ | |
switch (_state) | |
{ | |
case CircuitBreakerState.Closed: | |
if (++_failures > _threshold) | |
{ | |
lock (_lock) | |
{ | |
_state = CircuitBreakerState.Open; | |
_timeoutEnd = DateTime.Now.Add(_timeout); | |
} | |
} | |
break; | |
case CircuitBreakerState.HalfOpen: | |
lock (_lock) | |
{ | |
_state = CircuitBreakerState.Open; | |
_timeoutEnd = DateTime.Now.Add(_timeout); | |
} | |
break; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment