github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/engine/collection/ingest/rate_limiter_test.go (about) 1 package ingest_test 2 3 import ( 4 "fmt" 5 "sync" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/require" 10 "go.uber.org/atomic" 11 "golang.org/x/time/rate" 12 13 "github.com/onflow/flow-go/access" 14 "github.com/onflow/flow-go/engine/collection/ingest" 15 "github.com/onflow/flow-go/model/flow" 16 "github.com/onflow/flow-go/utils/unittest" 17 ) 18 19 var _ access.RateLimiter = (*ingest.AddressRateLimiter)(nil) 20 21 func TestLimiterAddRemoveAddress(t *testing.T) { 22 t.Parallel() 23 24 good1 := unittest.RandomAddressFixture() 25 limited1 := unittest.RandomAddressFixture() 26 limited2 := unittest.RandomAddressFixture() 27 28 numPerSec := rate.Limit(1) 29 burst := 1 30 l := ingest.NewAddressRateLimiter(numPerSec, burst) 31 32 require.False(t, l.IsRateLimited(good1)) 33 require.False(t, l.IsRateLimited(good1)) // address are not limited 34 35 l.AddAddress(limited1) 36 require.Equal(t, []flow.Address{limited1}, l.GetAddresses()) 37 38 require.False(t, l.IsRateLimited(limited1)) // address 1 is not limited on the first call 39 require.True(t, l.IsRateLimited(limited1)) // limited on the second call immediately 40 require.True(t, l.IsRateLimited(limited1)) // limited on the second call immediately 41 42 require.False(t, l.IsRateLimited(good1)) 43 require.False(t, l.IsRateLimited(good1)) // address are not limited 44 45 l.AddAddress(limited2) 46 list := l.GetAddresses() 47 require.Len(t, list, 2) 48 require.Contains(t, list, limited1, limited2) 49 50 require.False(t, l.IsRateLimited(limited2)) // address 2 is not limited on the first call 51 require.True(t, l.IsRateLimited(limited2)) // limited on the second call immediately 52 require.True(t, l.IsRateLimited(limited2)) // limited on the second call immediately 53 54 l.RemoveAddress(limited1) // after remove the limit, it no longer limited 55 require.False(t, l.IsRateLimited(limited1)) 56 require.False(t, l.IsRateLimited(limited1)) 57 58 // but limit2 is still limited 59 require.True(t, l.IsRateLimited(limited2)) 60 } 61 62 func TestLimiterBurst(t *testing.T) { 63 t.Parallel() 64 65 limited1 := unittest.RandomAddressFixture() 66 67 numPerSec := rate.Limit(1) 68 burst := 3 69 l := ingest.NewAddressRateLimiter(numPerSec, burst) 70 71 l.AddAddress(limited1) 72 for i := 0; i < burst; i++ { 73 require.False(t, l.IsRateLimited(limited1), fmt.Sprintf("%v-nth call", i)) 74 } 75 76 require.True(t, l.IsRateLimited(limited1)) // limited 77 require.True(t, l.IsRateLimited(limited1)) // limited 78 } 79 80 // verify that if wait long enough after rate limited 81 func TestLimiterWaitLongEnough(t *testing.T) { 82 t.Parallel() 83 84 addr1 := unittest.RandomAddressFixture() 85 86 // with limit set to 10, it means we allow 10 messages per second, 87 // and with burst set to 1, it means we only allow 1 message at a time, 88 // so the limit is 1 message per 100 milliseconds. 89 // Note rate.Limit(0.1) is not to set 1 message per 100 milliseconds, but 90 // 1 message per 10 seconds. 91 numPerSec := rate.Limit(10) 92 burst := 1 93 l := ingest.NewAddressRateLimiter(numPerSec, burst) 94 95 l.AddAddress(addr1) 96 require.False(t, l.IsRateLimited(addr1)) 97 require.True(t, l.IsRateLimited(addr1)) 98 99 // check every 10 Millisecond then after 100 Millisecond it should be allowed 100 require.Eventually(t, func() bool { 101 return l.Allow(addr1) 102 }, 110*time.Millisecond, 10*time.Millisecond) 103 104 // block again until another 100 ms 105 require.True(t, l.IsRateLimited(addr1)) 106 107 // block until another 100 ms 108 require.Eventually(t, func() bool { 109 return l.Allow(addr1) 110 }, 110*time.Millisecond, 10*time.Millisecond) 111 } 112 113 func TestLimiterConcurrentSafe(t *testing.T) { 114 t.Parallel() 115 good1 := unittest.RandomAddressFixture() 116 limited1 := unittest.RandomAddressFixture() 117 118 numPerSec := rate.Limit(1) 119 burst := 1 120 l := ingest.NewAddressRateLimiter(numPerSec, burst) 121 122 l.AddAddress(limited1) 123 124 wg := sync.WaitGroup{} 125 wg.Add(2) 126 127 succeed := atomic.NewUint64(0) 128 go func(wg *sync.WaitGroup) { 129 defer wg.Done() 130 ok := l.IsRateLimited(limited1) 131 if ok { 132 succeed.Add(1) 133 } 134 require.False(t, l.IsRateLimited(good1)) // never limited 135 }(&wg) 136 137 go func(wg *sync.WaitGroup) { 138 defer wg.Done() 139 ok := l.IsRateLimited(limited1) 140 if ok { 141 succeed.Add(1) 142 } 143 require.False(t, l.IsRateLimited(good1)) // never limited 144 }(&wg) 145 146 wg.Wait() 147 require.Equal(t, uint64(1), succeed.Load()) // should only succeed once 148 } 149 150 func TestLimiterGetSetConfig(t *testing.T) { 151 t.Parallel() 152 153 addr1 := unittest.RandomAddressFixture() 154 155 // with limit set to 10, it means we allow 10 messages per second, 156 // and with burst set to 1, it means we only allow 1 message at a time, 157 // so the limit is 1 message per 100 milliseconds. 158 // Note rate.Limit(0.1) is not to set 1 message per 100 milliseconds, but 159 // 1 message per 10 seconds. 160 numPerSec := rate.Limit(10) 161 burst := 1 162 l := ingest.NewAddressRateLimiter(numPerSec, burst) 163 164 l.AddAddress(addr1) 165 require.False(t, l.IsRateLimited(addr1)) 166 require.True(t, l.IsRateLimited(addr1)) 167 168 limitConfig, burstConfig := l.GetLimitConfig() 169 require.Equal(t, numPerSec, limitConfig) 170 require.Equal(t, burst, burstConfig) 171 172 // change from 1 message per 100 ms to 4 messages per 200 ms 173 l.SetLimitConfig(rate.Limit(20), 4) 174 175 // verify the quota is reset, and the new limit is applied 176 for i := 0; i < 4; i++ { 177 require.False(t, l.IsRateLimited(addr1), fmt.Sprintf("fail at %v-th call", i)) 178 } 179 require.True(t, l.IsRateLimited(addr1)) 180 181 // check every 10 Millisecond then after 100 Millisecond it should be allowed 182 require.Eventually(t, func() bool { 183 return l.Allow(addr1) 184 }, 210*time.Millisecond, 10*time.Millisecond) 185 }