From 30044d1330958a8301ab5f2fae7d8125cb499664 Mon Sep 17 00:00:00 2001 From: Leo Antunes Date: Wed, 24 Jan 2024 23:01:54 +0100 Subject: [PATCH] refactor: also move ewma+half-open check into option --- breaker.go | 11 +++++++++-- hoglet.go | 18 ++++++++---------- hoglet_test.go | 4 ++++ 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/breaker.go b/breaker.go index 7c6d498..8e6a326 100644 --- a/breaker.go +++ b/breaker.go @@ -1,6 +1,7 @@ package hoglet import ( + "fmt" "math" "sync/atomic" "time" @@ -123,6 +124,14 @@ func (e *EWMABreaker) observe(halfOpen, failure bool) stateChange { } } +// apply implements Option. +func (e *EWMABreaker) apply(o *options) error { + if o.halfOpenDelay == 0 { + return fmt.Errorf("EWMABreaker requires a half-open delay") + } + return nil +} + // SlidingWindowBreaker is a [Breaker] that uses a sliding window to determine the error rate. type SlidingWindowBreaker struct { windowSize time.Duration @@ -208,8 +217,6 @@ func (s *SlidingWindowBreaker) observe(halfOpen, failure bool) stateChange { } } -var _ Option = (*SlidingWindowBreaker)(nil) - // apply implements Option. func (s *SlidingWindowBreaker) apply(o *options) error { if o.halfOpenDelay == 0 || o.halfOpenDelay > s.windowSize { diff --git a/hoglet.go b/hoglet.go index e93391c..8afeb98 100644 --- a/hoglet.go +++ b/hoglet.go @@ -42,6 +42,8 @@ type Breaker interface { // The halfOpen parameter indicates whether the call was made in half-open state. // The failure parameter indicates whether the call failed. observe(halfOpen, failure bool) stateChange + + Option // breakers can also modify or sanity-check their circuit's options } // ObserverFactory is an interface that allows customizing the per-call observer creation. @@ -87,6 +89,8 @@ func NewCircuit[IN, OUT any](f WrappedFunc[IN, OUT], breaker Breaker, opts ...Op if breaker != nil { o.breaker = breaker + // apply breaker as last, so it can verify + opts = append(opts, breaker) } // the default observerFactory is the circuit itself @@ -98,18 +102,8 @@ func NewCircuit[IN, OUT any](f WrappedFunc[IN, OUT], breaker Breaker, opts ...Op } } - if breakerOpt, ok := breaker.(Option); ok { - if err := breakerOpt.apply(&o); err != nil { - return nil, fmt.Errorf("applying breaker option: %w", err) - } - } - c.options = o - if _, ok := breaker.(*EWMABreaker); ok && c.halfOpenDelay == 0 { - return nil, fmt.Errorf("EWMABreaker requires a half-open delay") - } - return c, nil } @@ -297,3 +291,7 @@ type noopBreaker struct{} func (noopBreaker) observe(halfOpen, failure bool) stateChange { return stateChangeNone } + +func (noopBreaker) apply(*options) error { + return nil +} diff --git a/hoglet_test.go b/hoglet_test.go index 1f2f857..576f152 100644 --- a/hoglet_test.go +++ b/hoglet_test.go @@ -122,6 +122,10 @@ func (mt *mockBreaker) observe(halfOpen, failure bool) stateChange { return stateChangeClose } +func (mt *mockBreaker) apply(o *options) error { + return nil +} + func TestHoglet_Do(t *testing.T) { type calls struct { arg noopIn