github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/utils/ratelimiter/internal/rate_limiter_map.go (about) 1 package internal 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/libp2p/go-libp2p/core/peer" 8 "golang.org/x/time/rate" 9 10 "github.com/onflow/flow-go/module/irrecoverable" 11 ) 12 13 type rateLimiterMetadata struct { 14 mu *sync.RWMutex 15 // limiter the rate limiter 16 limiter *rate.Limiter 17 // lastRateLimit the last timestamp this the peer was rate limited. 18 lastRateLimit time.Time 19 // lastAccessed the last timestamp when the limiter was used. This is used to Cleanup old limiter data 20 lastAccessed time.Time 21 } 22 23 // newRateLimiterMetadata returns a new rateLimiterMetadata 24 func newRateLimiterMetadata(limiter *rate.Limiter) *rateLimiterMetadata { 25 return &rateLimiterMetadata{ 26 mu: &sync.RWMutex{}, 27 limiter: limiter, 28 lastAccessed: time.Now(), 29 lastRateLimit: time.Time{}, 30 } 31 } 32 33 // Limiter returns rateLimiterMetadata.limiter.. 34 func (m *rateLimiterMetadata) Limiter() *rate.Limiter { 35 m.mu.RLock() 36 defer m.mu.RUnlock() 37 return m.limiter 38 } 39 40 // LastRateLimit returns rateLimiterMetadata.lastRateLimit. 41 func (m *rateLimiterMetadata) LastRateLimit() time.Time { 42 m.mu.RLock() 43 defer m.mu.RUnlock() 44 return m.lastRateLimit 45 } 46 47 // SetLastRateLimit sets rateLimiterMetadata.lastRateLimit. 48 func (m *rateLimiterMetadata) SetLastRateLimit(lastRateLimit time.Time) { 49 m.mu.Lock() 50 defer m.mu.Unlock() 51 m.lastRateLimit = lastRateLimit 52 } 53 54 // LastAccessed returns rateLimiterMetadata.lastAccessed. 55 func (m *rateLimiterMetadata) LastAccessed() time.Time { 56 m.mu.RLock() 57 defer m.mu.RUnlock() 58 return m.lastAccessed 59 } 60 61 // SetLastAccessed sets rateLimiterMetadata.lastAccessed. 62 func (m *rateLimiterMetadata) SetLastAccessed(lastAccessed time.Time) { 63 m.mu.Lock() 64 defer m.mu.Unlock() 65 m.lastAccessed = lastAccessed 66 } 67 68 // RateLimiterMap stores a rateLimiterMetadata for each peer in an underlying map. 69 type RateLimiterMap struct { 70 // mu read write mutex used to synchronize updates to the rate limiter map. 71 mu sync.RWMutex 72 // ttl time to live is the duration in which a rate limiter is stored in the limiters map. 73 // Stale rate limiters from peers that have not interacted in a while will be cleaned up to 74 // free up unused resources. 75 ttl time.Duration 76 // cleanupInterval the interval in which stale rate limiter's are removed from the limiters map 77 // to free up unused resources. 78 cleanupInterval time.Duration 79 // limiters map that stores rate limiter metadata for each peer. 80 limiters map[peer.ID]*rateLimiterMetadata 81 } 82 83 func NewLimiterMap(ttl, cleanupInterval time.Duration) *RateLimiterMap { 84 return &RateLimiterMap{ 85 mu: sync.RWMutex{}, 86 limiters: make(map[peer.ID]*rateLimiterMetadata), 87 ttl: ttl, 88 cleanupInterval: cleanupInterval, 89 } 90 } 91 92 // Get returns limiter in RateLimiterMap map 93 func (r *RateLimiterMap) Get(peerID peer.ID) (*rateLimiterMetadata, bool) { 94 r.mu.RLock() 95 defer r.mu.RUnlock() 96 if lmtr, ok := r.limiters[peerID]; ok { 97 lmtr.SetLastAccessed(time.Now()) 98 return lmtr, ok 99 } 100 return nil, false 101 } 102 103 // Store stores limiter in RateLimiterMap map 104 func (r *RateLimiterMap) Store(peerID peer.ID, lmtr *rate.Limiter) { 105 r.mu.Lock() 106 defer r.mu.Unlock() 107 r.limiters[peerID] = newRateLimiterMetadata(lmtr) 108 } 109 110 // UpdateLastRateLimit sets the lastRateLimit field of the rateLimiterMetadata for a peer. 111 func (r *RateLimiterMap) UpdateLastRateLimit(peerID peer.ID, lastRateLimit time.Time) { 112 r.mu.RLock() 113 defer r.mu.RUnlock() 114 r.limiters[peerID].SetLastRateLimit(lastRateLimit) 115 } 116 117 // Remove deletes peerID key from underlying map. 118 func (r *RateLimiterMap) Remove(peerID peer.ID) { 119 r.mu.Lock() 120 defer r.mu.Unlock() 121 r.removeUnlocked(peerID) 122 } 123 124 // removeUnlocked removes peerID key from underlying map without acquiring a lock. 125 func (r *RateLimiterMap) removeUnlocked(peerID peer.ID) { 126 delete(r.limiters, peerID) 127 } 128 129 // cleanup check the TTL for all keys in map and Remove isExpired keys. 130 func (r *RateLimiterMap) cleanup() { 131 r.mu.Lock() 132 defer r.mu.Unlock() 133 for peerID, item := range r.limiters { 134 if time.Since(item.LastAccessed()) > r.ttl { 135 r.removeUnlocked(peerID) 136 } 137 } 138 } 139 140 // CleanupLoop starts a loop that periodically removes stale peers. 141 // This func blocks until the signaler context is canceled, when context 142 // is canceled the limiter map is cleaned up before the cleanup loop exits. 143 func (r *RateLimiterMap) CleanupLoop(ctx irrecoverable.SignalerContext) { 144 ticker := time.NewTicker(r.cleanupInterval) 145 defer ticker.Stop() 146 defer r.cleanup() 147 for { 148 select { 149 case <-ctx.Done(): 150 return 151 default: 152 } 153 154 select { 155 case <-ctx.Done(): 156 return 157 case <-ticker.C: 158 r.cleanup() 159 } 160 } 161 }