github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/ingester/limiter_test.go (about) 1 package ingester 2 3 import ( 4 "fmt" 5 "math" 6 "testing" 7 "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 "golang.org/x/time/rate" 12 13 "github.com/grafana/loki/pkg/validation" 14 ) 15 16 func TestLimiter_AssertMaxStreamsPerUser(t *testing.T) { 17 tests := map[string]struct { 18 maxLocalStreamsPerUser int 19 maxGlobalStreamsPerUser int 20 ringReplicationFactor int 21 ringIngesterCount int 22 streams int 23 expected error 24 }{ 25 "both local and global limit are disabled": { 26 maxLocalStreamsPerUser: 0, 27 maxGlobalStreamsPerUser: 0, 28 ringReplicationFactor: 1, 29 ringIngesterCount: 1, 30 streams: 100, 31 expected: nil, 32 }, 33 "current number of streams is below the limit": { 34 maxLocalStreamsPerUser: 0, 35 maxGlobalStreamsPerUser: 1000, 36 ringReplicationFactor: 3, 37 ringIngesterCount: 10, 38 streams: 299, 39 expected: nil, 40 }, 41 "current number of streams is above the limit": { 42 maxLocalStreamsPerUser: 0, 43 maxGlobalStreamsPerUser: 1000, 44 ringReplicationFactor: 3, 45 ringIngesterCount: 10, 46 streams: 300, 47 expected: fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 300, 300, 0, 1000, 300), 48 }, 49 "both local and global limits are disabled": { 50 maxLocalStreamsPerUser: 0, 51 maxGlobalStreamsPerUser: 0, 52 ringReplicationFactor: 1, 53 ringIngesterCount: 1, 54 streams: math.MaxInt32 - 1, 55 expected: nil, 56 }, 57 "only local limit is enabled": { 58 maxLocalStreamsPerUser: 1000, 59 maxGlobalStreamsPerUser: 0, 60 ringReplicationFactor: 1, 61 ringIngesterCount: 1, 62 streams: 3000, 63 expected: fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 1000, 1000, 0, 0), 64 }, 65 "only global limit is enabled with replication-factor=1": { 66 maxLocalStreamsPerUser: 0, 67 maxGlobalStreamsPerUser: 1000, 68 ringReplicationFactor: 1, 69 ringIngesterCount: 10, 70 streams: 3000, 71 expected: fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 100, 0, 1000, 100), 72 }, 73 "only global limit is enabled with replication-factor=3": { 74 maxLocalStreamsPerUser: 0, 75 maxGlobalStreamsPerUser: 1000, 76 ringReplicationFactor: 3, 77 ringIngesterCount: 10, 78 streams: 3000, 79 expected: fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 300, 0, 1000, 300), 80 }, 81 "both local and global limits are set with local limit < global limit": { 82 maxLocalStreamsPerUser: 150, 83 maxGlobalStreamsPerUser: 1000, 84 ringReplicationFactor: 3, 85 ringIngesterCount: 10, 86 streams: 3000, 87 expected: fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 150, 150, 1000, 300), 88 }, 89 "both local and global limits are set with local limit > global limit": { 90 maxLocalStreamsPerUser: 500, 91 maxGlobalStreamsPerUser: 1000, 92 ringReplicationFactor: 3, 93 ringIngesterCount: 10, 94 streams: 3000, 95 expected: fmt.Errorf(errMaxStreamsPerUserLimitExceeded, "test", 3000, 300, 500, 1000, 300), 96 }, 97 } 98 99 for testName, testData := range tests { 100 testData := testData 101 102 t.Run(testName, func(t *testing.T) { 103 // Mock the ring 104 ring := &ringCountMock{count: testData.ringIngesterCount} 105 106 // Mock limits 107 limits, err := validation.NewOverrides(validation.Limits{ 108 MaxLocalStreamsPerUser: testData.maxLocalStreamsPerUser, 109 MaxGlobalStreamsPerUser: testData.maxGlobalStreamsPerUser, 110 }, nil) 111 require.NoError(t, err) 112 113 limiter := NewLimiter(limits, NilMetrics, ring, testData.ringReplicationFactor) 114 actual := limiter.AssertMaxStreamsPerUser("test", testData.streams) 115 116 assert.Equal(t, testData.expected, actual) 117 }) 118 } 119 } 120 121 func TestLimiter_minNonZero(t *testing.T) { 122 t.Parallel() 123 124 tests := map[string]struct { 125 first int 126 second int 127 expected int 128 }{ 129 "both zero": { 130 first: 0, 131 second: 0, 132 expected: 0, 133 }, 134 "first is zero": { 135 first: 0, 136 second: 1, 137 expected: 1, 138 }, 139 "second is zero": { 140 first: 1, 141 second: 0, 142 expected: 1, 143 }, 144 "both non zero, second > first": { 145 first: 1, 146 second: 2, 147 expected: 1, 148 }, 149 "both non zero, first > second": { 150 first: 2, 151 second: 1, 152 expected: 1, 153 }, 154 } 155 156 for testName, testData := range tests { 157 testData := testData 158 159 t.Run(testName, func(t *testing.T) { 160 limiter := NewLimiter(nil, NilMetrics, nil, 0) 161 assert.Equal(t, testData.expected, limiter.minNonZero(testData.first, testData.second)) 162 }) 163 } 164 } 165 166 type ringCountMock struct { 167 count int 168 } 169 170 func (m *ringCountMock) HealthyInstancesCount() int { 171 return m.count 172 } 173 174 // Assert some of the weirder (bug?) behavior of golang.org/x/time/rate 175 func TestGoLimiter(t *testing.T) { 176 for _, tc := range []struct { 177 desc string 178 lim *rate.Limiter 179 at time.Time 180 burst int 181 limit rate.Limit 182 allow int 183 exp bool 184 }{ 185 { 186 // I (owen-d) think this _should_ work and am supplying this test 187 // case by way of explanation for how the StreamRateLimiter 188 // works around the rate.Inf edge case. 189 desc: "changing inf limits unnecessarily cordons", 190 lim: rate.NewLimiter(rate.Inf, 0), 191 at: time.Now(), 192 burst: 2, 193 limit: 1, 194 allow: 1, 195 exp: false, 196 }, 197 { 198 desc: "non inf limit works", 199 lim: rate.NewLimiter(1, 2), 200 at: time.Now(), 201 burst: 2, 202 limit: 1, 203 allow: 1, 204 exp: true, 205 }, 206 } { 207 t.Run(tc.desc, func(t *testing.T) { 208 tc.lim.SetBurstAt(tc.at, tc.burst) 209 tc.lim.SetLimitAt(tc.at, tc.limit) 210 require.Equal(t, tc.exp, tc.lim.AllowN(tc.at, tc.allow)) 211 }) 212 } 213 }