Created
May 12, 2015 14:36
-
-
Save loosechainsaw/edf6388e5f1c20d93aa0 to your computer and use it in GitHub Desktop.
Circuit Breaker
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
internal class ScopedAquiredWriteLock : IDisposable | |
{ | |
internal ScopedAquiredWriteLock(ReaderWriterLockSlim locker) | |
{ | |
this.locker = locker; | |
this.locker.EnterWriteLock(); | |
} | |
public void Dispose() | |
{ | |
this.locker.ExitWriteLock(); | |
} | |
private ReaderWriterLockSlim locker; | |
} | |
internal class ScopedAquiredReadLock : IDisposable | |
{ | |
internal ScopedAquiredReadLock(ReaderWriterLockSlim locker) | |
{ | |
this.locker = locker; | |
this.locker.EnterReadLock(); | |
} | |
public void Dispose() | |
{ | |
this.locker.ExitReadLock(); | |
} | |
private ReaderWriterLockSlim locker; | |
} | |
internal class ScopedAquiredUpgradableLock : IDisposable | |
{ | |
internal ScopedAquiredUpgradableLock(ReaderWriterLockSlim locker) | |
{ | |
this.locker = locker; | |
this.locker.EnterUpgradeableReadLock(); | |
} | |
public void Dispose() | |
{ | |
this.locker.ExitUpgradeableReadLock(); | |
} | |
private ReaderWriterLockSlim locker; | |
} | |
internal enum State | |
{ | |
Closed, | |
HalfOpen, | |
Open | |
} | |
public enum Notification | |
{ | |
Success, | |
Failure | |
} | |
public interface IEnvironmentProvider | |
{ | |
DateTime GetCurrentTime(); | |
int GetMaximumFailuresForClosedContext(); | |
TimeSpan GetDurationForClosedContext(); | |
TimeSpan GetDurationForOpenContext(); | |
int GetSuccessResetCountForHalfOpenContext(); | |
} | |
public class EnvironmentProvider : IEnvironmentProvider | |
{ | |
public int GetMaximumFailuresForClosedContext() | |
{ | |
return 10; | |
} | |
public TimeSpan GetDurationForClosedContext() | |
{ | |
return TimeSpan.FromMinutes(10); | |
} | |
public TimeSpan GetDurationForOpenContext() | |
{ | |
return TimeSpan.FromMinutes(3); | |
} | |
public int GetSuccessResetCountForHalfOpenContext() | |
{ | |
return 2; | |
} | |
public DateTime GetCurrentTime() | |
{ | |
return DateTime.UtcNow; | |
} | |
} | |
public class CircuitBreaker | |
{ | |
public CircuitBreaker(IEnvironmentProvider environmentProvider, string name) | |
{ | |
locker = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); | |
this.environmentProvider = environmentProvider; | |
this.name = name; | |
context = new ClosedCircuitBreakerContext(this); | |
} | |
internal void ChangeContext(CircuitBreakerContext ctx) | |
{ | |
context = ctx; | |
} | |
public void ChangeEnvironment(IEnvironmentProvider provider) | |
{ | |
environmentProvider = provider; | |
} | |
public IEnvironmentProvider GetEnvironment() | |
{ | |
return environmentProvider; | |
} | |
internal ReaderWriterLockSlim GetLock() | |
{ | |
return locker; | |
} | |
public bool Accepting() | |
{ | |
using(var lk = new ScopedAquiredReadLock(locker)) | |
return context.Accepting(environmentProvider.GetCurrentTime()); | |
} | |
public void Notify(Notification notification) | |
{ | |
using(var lk = new ScopedAquiredUpgradableLock(locker)) { | |
if(!context.Accepting(environmentProvider.GetCurrentTime())) | |
return; | |
context.Notify(notification); | |
} | |
} | |
private IEnvironmentProvider environmentProvider; | |
private readonly string name; | |
private CircuitBreakerContext context; | |
private readonly ReaderWriterLockSlim locker; | |
} | |
internal abstract class CircuitBreakerContext | |
{ | |
internal CircuitBreakerContext(CircuitBreaker circuitbreaker) | |
{ | |
this.circuitbreaker = circuitbreaker; | |
} | |
internal abstract void Notify(Notification notification); | |
internal abstract bool Accepting(DateTime currentTime); | |
internal void ChangeContext(CircuitBreakerContext context) | |
{ | |
circuitbreaker.ChangeContext(context); | |
} | |
internal IEnvironmentProvider GetEnvironment() | |
{ | |
return circuitbreaker.GetEnvironment(); | |
} | |
protected internal readonly CircuitBreaker circuitbreaker; | |
} | |
internal class ClosedCircuitBreakerContext : CircuitBreakerContext | |
{ | |
internal ClosedCircuitBreakerContext(CircuitBreaker circuitbreaker) | |
: base(circuitbreaker) | |
{ | |
var environment = GetEnvironment(); | |
windowEnd = environment.GetCurrentTime().Add(environment.GetDurationForClosedContext()); | |
maxFailures = environment.GetMaximumFailuresForClosedContext(); | |
} | |
internal override void Notify(Notification notification) | |
{ | |
if(notification == Notification.Success) | |
return; | |
var environment = GetEnvironment(); | |
var locker = circuitbreaker.GetLock(); | |
var time = environment.GetCurrentTime().Add(environment.GetDurationForClosedContext()); | |
if(time >= windowEnd) { | |
using(new ScopedAquiredWriteLock(locker)) { | |
failures = 0; | |
windowEnd = time; | |
return; | |
} | |
} | |
if(++failures == maxFailures) { | |
using(new ScopedAquiredWriteLock(locker)) { | |
ChangeContext(new OpenCircuitBreakerContext(circuitbreaker)); | |
} | |
} | |
} | |
internal override bool Accepting(DateTime currentTime) | |
{ | |
return true; | |
} | |
private DateTime windowEnd; | |
private int failures; | |
private readonly int maxFailures; | |
} | |
internal class OpenCircuitBreakerContext : CircuitBreakerContext | |
{ | |
internal OpenCircuitBreakerContext(CircuitBreaker circuitbreaker) : base(circuitbreaker) | |
{ | |
var environment = GetEnvironment(); | |
expiresAt = environment.GetCurrentTime().Add(environment.GetDurationForOpenContext()); | |
} | |
internal override void Notify(Notification notification) | |
{ | |
var currentTime = GetEnvironment().GetCurrentTime(); | |
if(currentTime < expiresAt) | |
return; | |
var locker = circuitbreaker.GetLock(); | |
using(new ScopedAquiredWriteLock(locker)) { | |
ChangeContext(new HalfOpenCircuitBreakerContext(circuitbreaker)); | |
} | |
} | |
internal override bool Accepting(DateTime currentTime) | |
{ | |
return (currentTime > expiresAt); | |
} | |
private readonly DateTime expiresAt; | |
} | |
internal class HalfOpenCircuitBreakerContext : CircuitBreakerContext | |
{ | |
internal HalfOpenCircuitBreakerContext(CircuitBreaker circuitbreaker) : base(circuitbreaker) | |
{ | |
var environment = GetEnvironment(); | |
consecutiveSuccessesRequired = environment.GetSuccessResetCountForHalfOpenContext(); | |
} | |
internal override void Notify(Notification notification) | |
{ | |
var currentTime = GetEnvironment().GetCurrentTime(); | |
var locker = circuitbreaker.GetLock(); | |
if(notification == Notification.Success && ++successes == consecutiveSuccessesRequired) { | |
using(new ScopedAquiredWriteLock(locker)) { | |
ChangeContext(new OpenCircuitBreakerContext(circuitbreaker)); | |
} | |
} | |
if(notification == Notification.Failure) { | |
using(new ScopedAquiredWriteLock(locker)) { | |
ChangeContext(new OpenCircuitBreakerContext(circuitbreaker)); | |
} | |
} | |
} | |
internal override bool Accepting(DateTime currentTime) | |
{ | |
return true; | |
} | |
private readonly int consecutiveSuccessesRequired; | |
private int successes; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment