github.com/iotexproject/iotex-core@v1.14.1-rc1/pkg/counter/counter.go (about) 1 // Copyright (c) 2018 IoTeX 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package counter 7 8 import ( 9 "sync" 10 "time" 11 ) 12 13 // SlidingWindowCounter is used to count the number of events happened in the last X duration (in terms of a sliding 14 // window). Interval defines how big the time window is and SlotGranularity defines how fine grained the counter is. 15 type SlidingWindowCounter struct { 16 Interval time.Duration 17 SlotGranularity time.Duration 18 window []uint64 19 count uint64 20 headIdx int 21 lastUpdateTime time.Time 22 locker sync.Mutex 23 } 24 25 // NewSlidingWindowCounter creates an instance of SlidingWindowCounter 26 func NewSlidingWindowCounter(i time.Duration, sg time.Duration) *SlidingWindowCounter { 27 c := &SlidingWindowCounter{Interval: i, SlotGranularity: sg} 28 c.window = make([]uint64, i/sg) 29 c.count = 0 30 c.headIdx = 0 31 c.lastUpdateTime = time.Now() 32 return c 33 } 34 35 // NewSlidingWindowCounterWithSecondSlot creates an instance of SlidingWindowCounter with the second level slot 36 func NewSlidingWindowCounterWithSecondSlot(i time.Duration) *SlidingWindowCounter { 37 return NewSlidingWindowCounter(i, time.Second) 38 } 39 40 // Increment increase the counter by 1. It's a blocking operation. 41 func (c *SlidingWindowCounter) Increment() { 42 c.locker.Lock() 43 defer c.locker.Unlock() 44 45 c.refresh() 46 c.window[c.headIdx]++ 47 c.count++ 48 } 49 50 // Count reads the current gauge. It's a blocking operation. 51 func (c *SlidingWindowCounter) Count() uint64 { 52 c.locker.Lock() 53 defer c.locker.Unlock() 54 55 c.refresh() 56 return c.count 57 } 58 59 func (c *SlidingWindowCounter) refresh() { 60 now := time.Now() 61 duration := int(now.Sub(c.lastUpdateTime) / c.SlotGranularity) 62 if duration >= len(c.window) { 63 for i := 0; i < len(c.window); i++ { 64 if i == 0 { 65 c.window[i] = 1 66 } else { 67 c.window[i] = 0 68 } 69 } 70 c.headIdx = 0 71 c.count = 0 72 73 } else { 74 for i := 0; i < duration; i++ { 75 c.headIdx++ 76 if c.headIdx >= len(c.window) { 77 c.headIdx = 0 78 } 79 c.count -= c.window[c.headIdx] 80 c.window[c.headIdx] = 0 81 } 82 } 83 // Only change the lastUpdateTime when duration is greater than 0. That said, lastUpdateTime is updated only when 84 // the delta is greater than the slog granularity. This is to prevent keep updating the lastUpdateTime if incoming 85 // messages is so frequent that now - lastUpdateTime is always smaller than slog granularity, eventually always 86 // increasing the counter in the same slot. 87 if duration > 0 { 88 c.lastUpdateTime = now 89 } 90 }