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  }