Search code examples
c#exceptiontimespancircuit-breaker

Lazy timeout resolution Circuit Breaker is throwing ArgumentOutOfRangeException


Ayende posted a modification to Davy Brion's circuit breaker, wherein he changed the timeout resolution to a lazy model.

private readonly DateTime expiry;

public OpenState(CircuitBreaker outer)
    : base(outer)
{
    expiry = DateTime.UtcNow + outer.timeout;
}

public override void ProtectedCodeIsAboutToBeCalled()
{
    if(DateTime.UtcNow < expiry)
        throw new OpenCircuitException();

    outer.MoveToHalfOpenState();
}

However, the constructor can fail because a TimeSpan can quickly overflow the maximum value of a DateTime. For example, when the circuit breaker’s timeout is the maximum value of a TimeSpan.

System.ArgumentOutOfRangeException was caught

Message="The added or subtracted value results in an un-representable DateTime."

...

at System.DateTime.op_Addition(DateTime d, TimeSpan t)

How do we avoid this problem and maintain the expected behavior?


Solution

  • Do a little bit of math

    Determine if the timeout is greater than the remainder of the maximum value of a DateTime and the current DateTime. A maximum TimeSpan usually indicates a functionally infinite timeout (making this a "one-time" circuit breaker).

    public OpenState(CircuitBreaker outer)
        : base(outer)
    {
        DateTime now = DateTime.UtcNow;
        expiry = outer.Timeout > (DateTime.MaxValue - now) 
            ? DateTime.MaxValue
            : now + outer.Timeout;
    }