github.com/jxskiss/gopkg/v2@v2.14.9-0.20240514120614-899f3e7952b4/utils/retry/breaker.go (about) 1 package retry 2 3 import ( 4 "sync" 5 "time" 6 ) 7 8 const windowSize = 10 9 const rollingRetryThreshold = 30 10 11 var breakerMap sync.Map 12 13 type breakerKey struct { 14 name string 15 overloadRatio float64 16 } 17 18 func getBreaker(name string, overloadRatio float64) *breaker { 19 key := breakerKey{ 20 name: name, 21 overloadRatio: overloadRatio, 22 } 23 w, ok := breakerMap.Load(key) 24 if !ok { 25 w, _ = breakerMap.LoadOrStore(key, &breaker{ 26 ratio: overloadRatio, 27 }) 28 } 29 return w.(*breaker) 30 } 31 32 type breaker struct { 33 ratio float64 34 succ bucket 35 fail bucket 36 } 37 38 func (b *breaker) shouldRetry() bool { 39 nowUnix := time.Now().Unix() 40 count := b.fail.sum(nowUnix) 41 if count > rollingRetryThreshold && 42 count > b.ratio*b.succ.sum(nowUnix) { 43 return false 44 } 45 return true 46 } 47 48 type bucket struct { 49 mu sync.RWMutex 50 index [windowSize]int64 51 count [windowSize]float64 52 } 53 54 func (b *bucket) incr(nowUnix int64) { 55 idx := nowUnix % windowSize 56 b.mu.Lock() 57 if b.index[idx] != nowUnix { 58 b.index[idx] = nowUnix 59 b.count[idx] = 0 60 } 61 b.count[idx]++ 62 b.mu.Unlock() 63 } 64 65 func (b *bucket) sum(nowUnix int64) float64 { 66 var sum float64 67 threshold := nowUnix - windowSize 68 b.mu.RLock() 69 for i := 0; i < windowSize; i++ { 70 if b.index[i] >= threshold { 71 sum += b.count[i] 72 } 73 } 74 b.mu.RUnlock() 75 return sum 76 }