Skip to content

Commit

Permalink
For a FrequencyBreaker, Failures() should return the count since the …
Browse files Browse the repository at this point in the history
…duration start, even after resetting.

Using Failures() in a monitoring system will always show 0 if traffic is doing Reset() on successful attempts faster than your sampling rate.
  • Loading branch information
rubyist committed Aug 25, 2014
1 parent 17d0e75 commit 51bfcdc
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 6 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# Changelog
All notable changes to this project will be documented in this file.

### 1.1.2 - 2014-08-20

### Added
- Nothing

### Deprecated
- Nothing

### Fixed
- For a FrequencyBreaker, Failures() should return the count since the duration start, even after resetting.

## 1.1.1 - 2014-08-20

### Added
Expand Down
15 changes: 11 additions & 4 deletions circuitbreaker.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,15 +213,16 @@ type FrequencyBreaker struct {
// Threshold is the number of failures Breaker will allow before tripping
Threshold int64

_failureTick unsafe.Pointer
_failureTick unsafe.Pointer
failuresSinceTick int64
*TrippableBreaker
}

// NewFrequencyBreaker returns a new FrequencyBreaker with the given duration
// and failure threshold. If a duration is specified as 0 then no duration will be used and
// the behavior will be the same as a ThresholdBreaker
func NewFrequencyBreaker(duration time.Duration, threshold int64) *FrequencyBreaker {
return &FrequencyBreaker{duration, threshold, nil, NewTrippableBreaker(time.Millisecond * 500)}
return &FrequencyBreaker{duration, threshold, nil, 0, NewTrippableBreaker(time.Millisecond * 500)}
}

// Fail records a failure. If the failure count meets the threshold within the duration,
Expand All @@ -232,6 +233,7 @@ func (cb *FrequencyBreaker) Fail() {
}

cb.TrippableBreaker.Fail()
atomic.AddInt64(&cb.failuresSinceTick, 1)
failures := atomic.AddInt64(&cb.failures, 1)
if failures == cb.Threshold {
cb.Trip()
Expand All @@ -241,10 +243,15 @@ func (cb *FrequencyBreaker) Fail() {
// Failures returns the number of failures for this circuit breaker. The failure count
// for a FrequencyBreaker resets when the duration expires.
func (cb *FrequencyBreaker) Failures() int64 {
if cb.Duration > 0 && (time.Since(cb.failureTick()) > cb.Duration) {
if cb.Duration <= 0 {
return cb.TrippableBreaker.Failures()
}

if time.Since(cb.failureTick()) > cb.Duration {
atomic.StoreInt64(&cb.failuresSinceTick, 0)
return 0
}
return cb.TrippableBreaker.Failures()
return atomic.LoadInt64(&cb.failuresSinceTick)
}

func (cb *FrequencyBreaker) frequencyFail() {
Expand Down
10 changes: 8 additions & 2 deletions circuitbreaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ func TestThresholdBreaker(t *testing.T) {
}

cb.Reset()
if cb.failures != 0 {
t.Fatalf("expected reset to set failures to 0, got %d", cb.failures)
if failures := cb.Failures(); failures != 0 {
t.Fatalf("expected reset to set failures to 0, got %d", failures)
}
if cb.Tripped() {
t.Fatal("expected threshold breaker to be open")
Expand Down Expand Up @@ -227,6 +227,12 @@ func TestFrequencyBreakerFailures(t *testing.T) {
if f := cb.Failures(); f != 1 {
t.Fatalf("expected failure count of 1, got %d", f)
}

cb.Reset()
if f := cb.Failures(); f != 1 {
t.Fatalf("expected failure count of 1, got %d", f)
}

time.Sleep(time.Millisecond)
if f := cb.Failures(); f != 0 {
t.Fatalf("expected failures count to be 0, got %d", f)
Expand Down

0 comments on commit 51bfcdc

Please # to comment.