rubyist / circuitbreaker

Circuit Breakers in Go
MIT License
1.12k stars 108 forks source link

How long dose it recover when it breaker #61

Open YuanWeiKang opened 4 years ago

YuanWeiKang commented 4 years ago

it's code

func TestBreaker22(t *testing.T) {
    brk := NewBreakerWithOptions(&Options{
        ShouldTrip: RateTripFunc(0.6, 100),
    })
    flag := 0
    start := time.Now().UnixNano()
    for i := 0; i < 200; i++ {
        err := brk.Call(func() error {
            return testDoSomething(flag)
        }, 0)

        last := atomic.LoadInt64(&brk.lastFailure)
        since := brk.Clock.Now().Sub(time.Unix(0, last))
        trip := atomic.LoadInt32(&brk.tripped)
        fmt.Printf("i:%d, trip:%d, last:%d, since:%+v, backOff:%+v, flag:%d, error:%s\n", i, trip, last, since, brk.nextBackOff, flag, err)

        if err == nil && brk.tripped == 0 {
            fmt.Printf("i:%d, trip:%d, duration:%d\n", i, brk.tripped, time.Now().UnixNano()-start)
            //break
        }
        if err != nil && strings.Contains(err.Error(), "breaker open") {
            flag = 1
            //time.Sleep(200 * time.Millisecond)
        }
    }
}

func testDoSomething(flag int) error {
    if flag == 0 {
        return fmt.Errorf("bad request")
    } else {
        return nil
    }
}

How do I calculate the recovery time?

macdabby commented 4 years ago

I'm not sure what the recovery time is but it looks like it's not configurable and not documented.

macdabby commented 4 years ago

This is where it is defined: https://github.com/rubyist/circuitbreaker/blob/4afb8473475bafc430776dcaafd1e6d84a8032ad/circuitbreaker.go#L391

macdabby commented 4 years ago

it seems that it's using exponential backoff by default: https://github.com/rubyist/circuitbreaker/blob/4afb8473475bafc430776dcaafd1e6d84a8032ad/circuitbreaker.go#L95

apundir commented 4 years ago

The backoff time is defined by backing backing.ExponentialBackOff , it's assigned with default values and further customized at:

https://github.com/rubyist/circuitbreaker/blob/4afb8473475bafc430776dcaafd1e6d84a8032ad/circuitbreaker.go#L139-L146

The actual backoff time is controlled by ExponentialBackOff type. Entire logic is documented and explained in official documentation. Following is the snippet from the documentation that explains this logic

Example: Given the following default arguments, for 10 tries the sequence will be, and assuming we go over the MaxElapsedTime on the 10th try:

Request #  RetryInterval (seconds)  Randomized Interval (seconds)

 1          0.5                     [0.25,   0.75]
 2          0.75                    [0.375,  1.125]
 3          1.125                   [0.562,  1.687]
 4          1.687                   [0.8435, 2.53]
 5          2.53                    [1.265,  3.795]
 6          3.795                   [1.897,  5.692]
 7          5.692                   [2.846,  8.538]
 8          8.538                   [4.269, 12.807]
 9         12.807                   [6.403, 19.210]
10         19.210                   backoff.Stop

It's also worth noting that default value of InitialInterval is 500ms and that of MaxElapsedTime is 0 in circuitbreaker. Thus table above gives you each backoff duration window with the exception that it doesn't stop growing at Request # 10 since the value of MaxElapsedTime is 0 by default in circuitbreaker. It'll keep on backing off exponentially with same logic till it attains max value of 1 minute (default from backoff) thus leading to final randomized interval between 30 and 90 seconds with default values from breaker.

You can customize these values if you wish to. Use NewBreakerWithOptions directly instead of convenient NewXXXX variant wrappers and pass your own instance of ExponentialBackOff with specific values as per your needs.