Skip to content

Commit

Permalink
Add AfterFunc configuration option to hystrix.ConfigureCloser (#111)
Browse files Browse the repository at this point in the history
* Add AfterFunc configuration option to hystrix.ConfigureCloser

* Add AfterFunc field to ConfigureCloser Merge function

* Add json tag to omit AfterFunc from json encoding

Signed-off-by: Chris Guiney <chrisg@tune.com>

Co-authored-by: Chris Guiney <chrisg@tune.com>
  • Loading branch information
chrisguiney and haschrisg authored Mar 7, 2022
1 parent ced1b59 commit 2762902
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
7 changes: 7 additions & 0 deletions v3/closers/hystrix/closer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ var _ circuit.OpenToClosed = &Closer{}

// ConfigureCloser configures values for Closer
type ConfigureCloser struct {
// AfterFunc should simulate time.AfterFunc
AfterFunc func(time.Duration, func()) *time.Timer `json:"-"`

// SleepWindow is https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakersleepwindowinmilliseconds
SleepWindow time.Duration
// HalfOpenAttempts is how many attempts to allow per SleepWindow
Expand All @@ -55,6 +58,9 @@ func (c *ConfigureCloser) Merge(other ConfigureCloser) {
if c.RequiredConcurrentSuccessful == 0 {
c.RequiredConcurrentSuccessful = other.RequiredConcurrentSuccessful
}
if c.AfterFunc == nil {
c.AfterFunc = other.AfterFunc
}
}

var defaultConfigureCloser = ConfigureCloser{
Expand Down Expand Up @@ -141,6 +147,7 @@ func (s *Closer) SetConfigThreadSafe(config ConfigureCloser) {
s.mu.Lock()
defer s.mu.Unlock()
s.config = config
s.reopenCircuitCheck.TimeAfterFunc = config.AfterFunc
s.reopenCircuitCheck.SetSleepDuration(config.SleepWindow)
s.reopenCircuitCheck.SetEventCountToAllow(config.HalfOpenAttempts)
s.closeOnCurrentCount.Set(config.RequiredConcurrentSuccessful)
Expand Down
65 changes: 65 additions & 0 deletions v3/closers/hystrix/closer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,68 @@ func TestCloser_ConcurrentAttempts(t *testing.T) {
// Should reset closer
assertBool(t, !c.ShouldClose(now), "Expected the circuit to not yet close")
}

func TestCloser_AfterFunc(t *testing.T) {
t.Run("afterfunc is used", func(t *testing.T) {
var invocations int
c := Closer{}
c.SetConfigNotThreadSafe(ConfigureCloser{
AfterFunc: func(d time.Duration, f func()) *time.Timer {
invocations++
return time.AfterFunc(d, f)
},
RequiredConcurrentSuccessful: 3,
})

now := time.Now()
c.Opened(now)
c.Success(now, time.Second)
c.Success(now, time.Second)
c.Success(now, time.Second)
c.Success(now, time.Second)

if invocations == 0 {
t.Error("Expected mock AfterFunc to be used")
}
t.Log("invocations: ", invocations)
})
t.Run("afterfunc is set if previously nil", func(t *testing.T) {
var (
countD int
c = ConfigureCloser{AfterFunc: nil}
d = ConfigureCloser{AfterFunc: func(d time.Duration, f func()) *time.Timer {
countD++
return time.AfterFunc(d+1, f)
}}
)
c.Merge(d)
_ = c.AfterFunc(time.Second, func() {})

if countD != 1 {
t.Errorf("expected merge to assign newer AfterFunc")
}
})
t.Run("afterfunc is not merged if already set", func(t *testing.T) {
var (
countC, countD int

c = ConfigureCloser{AfterFunc: func(d time.Duration, f func()) *time.Timer {
countC++
return time.AfterFunc(d, f)
}}
d = ConfigureCloser{AfterFunc: func(d time.Duration, f func()) *time.Timer {
countD++
return time.AfterFunc(d+1, f)
}}
)
c.Merge(d)
_ = c.AfterFunc(time.Second, func() {})

if countD > 0 {
t.Errorf("expected merge to maintain an already set AfterFunc")
}
if countC != 1 {
t.Errorf("expected post-merge to invoke initially set AfterFunc")
}
})
}

0 comments on commit 2762902

Please sign in to comment.