2025-03-03 17:06:04 +03:00
|
|
|
package tree
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
circuitBreaker struct {
|
|
|
|
breakDuration time.Duration
|
|
|
|
threshold int
|
|
|
|
|
|
|
|
mu sync.Mutex
|
2025-03-04 12:17:20 +03:00
|
|
|
state map[uint64]state
|
2025-03-03 17:06:04 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
state struct {
|
|
|
|
counter int
|
|
|
|
breakTimestamp time.Time
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
var ErrCBClosed = errors.New("circuit breaker is closed")
|
|
|
|
|
2025-03-04 12:20:24 +03:00
|
|
|
func newCircuitBreaker(breakDuration time.Duration, threshold int) *circuitBreaker {
|
2025-03-03 17:06:04 +03:00
|
|
|
return &circuitBreaker{
|
|
|
|
breakDuration: breakDuration,
|
|
|
|
threshold: threshold,
|
2025-03-04 12:17:20 +03:00
|
|
|
state: make(map[uint64]state),
|
2025-03-03 17:06:04 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 12:17:20 +03:00
|
|
|
func (c *circuitBreaker) Do(id uint64, f func() error) error {
|
2025-03-03 17:06:04 +03:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
2025-03-04 12:17:20 +03:00
|
|
|
if _, ok := c.state[id]; !ok {
|
|
|
|
c.state[id] = state{}
|
2025-03-03 17:06:04 +03:00
|
|
|
}
|
|
|
|
|
2025-03-04 12:17:20 +03:00
|
|
|
s := c.state[id]
|
2025-03-03 17:06:04 +03:00
|
|
|
defer func() {
|
2025-03-04 12:17:20 +03:00
|
|
|
c.state[id] = s
|
2025-03-03 17:06:04 +03:00
|
|
|
}()
|
|
|
|
|
|
|
|
if time.Since(s.breakTimestamp) < c.breakDuration {
|
|
|
|
return ErrCBClosed
|
|
|
|
}
|
|
|
|
|
2025-03-04 12:17:20 +03:00
|
|
|
err := f()
|
2025-03-03 17:06:04 +03:00
|
|
|
if err == nil {
|
|
|
|
s.counter = 0
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
s.counter++
|
|
|
|
if s.counter >= c.threshold {
|
|
|
|
s.counter = c.threshold
|
|
|
|
s.breakTimestamp = time.Now()
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|