github.com/matrixorigin/matrixone@v1.2.0/pkg/util/ring/ring_test.go (about) 1 // Copyright 2014 Workiva, LLC 2 // Modifications copyright (C) 2023 MatrixOrigin. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package ring 17 18 import ( 19 "sync" 20 "sync/atomic" 21 "testing" 22 "time" 23 24 "github.com/stretchr/testify/assert" 25 ) 26 27 func TestRingInsert(t *testing.T) { 28 rb := NewRingBuffer[int](5) 29 assert.Equal(t, uint64(8), rb.Cap()) 30 31 err := rb.Put(5) 32 if !assert.Nil(t, err) { 33 return 34 } 35 36 result, _, err := rb.Get() 37 if !assert.Nil(t, err) { 38 return 39 } 40 41 assert.Equal(t, 5, result) 42 } 43 44 func TestRingMultipleInserts(t *testing.T) { 45 rb := NewRingBuffer[int](5) 46 47 err := rb.Put(1) 48 if !assert.Nil(t, err) { 49 return 50 } 51 52 err = rb.Put(2) 53 if !assert.Nil(t, err) { 54 return 55 } 56 57 result, _, err := rb.Get() 58 if !assert.Nil(t, err) { 59 return 60 } 61 62 assert.Equal(t, 1, result) 63 64 result, _, err = rb.Get() 65 if assert.Nil(t, err) { 66 return 67 } 68 69 assert.Equal(t, 2, result) 70 } 71 72 func TestIntertwinedGetAndPut(t *testing.T) { 73 rb := NewRingBuffer[int](5) 74 err := rb.Put(1) 75 if !assert.Nil(t, err) { 76 return 77 } 78 79 result, _, err := rb.Get() 80 if !assert.Nil(t, err) { 81 return 82 } 83 84 assert.Equal(t, 1, result) 85 86 err = rb.Put(2) 87 if !assert.Nil(t, err) { 88 return 89 } 90 91 result, _, err = rb.Get() 92 if !assert.Nil(t, err) { 93 return 94 } 95 96 assert.Equal(t, 2, result) 97 } 98 99 func TestPutToFull(t *testing.T) { 100 rb := NewRingBuffer[int](3) 101 102 for i := 0; i < 4; i++ { 103 err := rb.Put(i) 104 if !assert.Nil(t, err) { 105 return 106 } 107 } 108 109 var wg sync.WaitGroup 110 wg.Add(2) 111 112 go func() { 113 err := rb.Put(4) 114 assert.Nil(t, err) 115 wg.Done() 116 }() 117 118 go func() { 119 defer wg.Done() 120 result, _, err := rb.Get() 121 if !assert.Nil(t, err) { 122 return 123 } 124 125 assert.Equal(t, 0, result) 126 }() 127 128 wg.Wait() 129 } 130 131 func TestOffer(t *testing.T) { 132 rb := NewRingBuffer[string](2) 133 134 ok, err := rb.Offer("foo") 135 assert.True(t, ok) 136 assert.Nil(t, err) 137 ok, err = rb.Offer("bar") 138 assert.True(t, ok) 139 assert.Nil(t, err) 140 ok, err = rb.Offer("baz") 141 assert.False(t, ok) 142 assert.Nil(t, err) 143 144 item, _, err := rb.Get() 145 assert.Nil(t, err) 146 assert.Equal(t, "foo", item) 147 item, _, err = rb.Get() 148 assert.Nil(t, err) 149 assert.Equal(t, "bar", item) 150 } 151 152 func TestRingGetEmpty(t *testing.T) { 153 rb := NewRingBuffer[int](3) 154 155 var wg sync.WaitGroup 156 wg.Add(1) 157 158 // want to kick off this consumer to ensure it blocks 159 go func() { 160 wg.Done() 161 result, _, err := rb.Get() 162 assert.Nil(t, err) 163 assert.Equal(t, 0, result) 164 wg.Done() 165 }() 166 167 wg.Wait() 168 wg.Add(2) 169 170 go func() { 171 defer wg.Done() 172 err := rb.Put(0) 173 assert.Nil(t, err) 174 }() 175 176 wg.Wait() 177 } 178 179 func TestRingPollEmpty(t *testing.T) { 180 rb := NewRingBuffer[int](3) 181 182 _, _, err := rb.Poll(1) 183 assert.Equal(t, ErrTimeout, err) 184 } 185 186 func TestRingPoll(t *testing.T) { 187 rb := NewRingBuffer[string](10) 188 189 // should be able to Poll() before anything is present, without breaking future Puts 190 rb.Poll(time.Millisecond) 191 192 rb.Put(`test`) 193 result, _, err := rb.Poll(0) 194 if !assert.Nil(t, err) { 195 return 196 } 197 198 assert.Equal(t, `test`, result) 199 assert.Equal(t, uint64(0), rb.Len()) 200 201 rb.Put(`1`) 202 rb.Put(`2`) 203 204 result, _, err = rb.Poll(time.Millisecond) 205 if !assert.Nil(t, err) { 206 return 207 } 208 209 assert.Equal(t, `1`, result) 210 assert.Equal(t, uint64(1), rb.Len()) 211 212 result, _, err = rb.Poll(time.Millisecond) 213 if !assert.Nil(t, err) { 214 return 215 } 216 217 assert.Equal(t, `2`, result) 218 219 _, _, err = rb.Poll(5 * time.Millisecond) 220 assert.Equal(t, ErrTimeout, err) 221 } 222 223 func TestRingLen(t *testing.T) { 224 rb := NewRingBuffer[int](4) 225 assert.Equal(t, uint64(0), rb.Len()) 226 227 rb.Put(1) 228 assert.Equal(t, uint64(1), rb.Len()) 229 230 rb.Get() 231 assert.Equal(t, uint64(0), rb.Len()) 232 233 for i := 0; i < 4; i++ { 234 rb.Put(1) 235 } 236 assert.Equal(t, uint64(4), rb.Len()) 237 238 rb.Get() 239 assert.Equal(t, uint64(3), rb.Len()) 240 } 241 242 func TestDisposeOnGet(t *testing.T) { 243 numThreads := 8 244 var wg sync.WaitGroup 245 wg.Add(numThreads) 246 rb := NewRingBuffer[int](4) 247 var spunUp sync.WaitGroup 248 spunUp.Add(numThreads) 249 250 for i := 0; i < numThreads; i++ { 251 go func() { 252 spunUp.Done() 253 defer wg.Done() 254 _, _, err := rb.Get() 255 assert.NotNil(t, err) 256 }() 257 } 258 259 spunUp.Wait() 260 rb.Dispose() 261 262 wg.Wait() 263 assert.True(t, rb.IsDisposed()) 264 } 265 266 func TestDisposeOnPut(t *testing.T) { 267 numThreads := 8 268 var wg sync.WaitGroup 269 wg.Add(numThreads) 270 rb := NewRingBuffer[int](4) 271 var spunUp sync.WaitGroup 272 spunUp.Add(numThreads) 273 274 // fill up the queue 275 for i := 0; i < 4; i++ { 276 rb.Put(i) 277 } 278 279 // it's now full 280 for i := 0; i < numThreads; i++ { 281 go func(i int) { 282 spunUp.Done() 283 defer wg.Done() 284 err := rb.Put(i) 285 assert.NotNil(t, err) 286 }(i) 287 } 288 289 spunUp.Wait() 290 291 rb.Dispose() 292 293 wg.Wait() 294 295 assert.True(t, rb.IsDisposed()) 296 } 297 298 func BenchmarkRBLifeCycle(b *testing.B) { 299 rb := NewRingBuffer[int](64) 300 301 counter := uint64(0) 302 var wg sync.WaitGroup 303 wg.Add(1) 304 305 go func() { 306 defer wg.Done() 307 for { 308 _, _, err := rb.Get() 309 assert.Nil(b, err) 310 311 if atomic.AddUint64(&counter, 1) == uint64(b.N) { 312 return 313 } 314 } 315 }() 316 317 b.ResetTimer() 318 319 for i := 0; i < b.N; i++ { 320 rb.Put(i) 321 } 322 323 wg.Wait() 324 } 325 326 func BenchmarkRBLifeCycleContention(b *testing.B) { 327 rb := NewRingBuffer[int](64) 328 329 var wwg sync.WaitGroup 330 var rwg sync.WaitGroup 331 wwg.Add(10) 332 rwg.Add(10) 333 334 for i := 0; i < 10; i++ { 335 go func() { 336 for { 337 _, _, err := rb.Get() 338 if err == ErrDisposed { 339 rwg.Done() 340 return 341 } else { 342 assert.Nil(b, err) 343 } 344 } 345 }() 346 } 347 348 b.ResetTimer() 349 350 for i := 0; i < 10; i++ { 351 go func() { 352 for j := 0; j < b.N; j++ { 353 rb.Put(j) 354 } 355 wwg.Done() 356 }() 357 } 358 359 wwg.Wait() 360 rb.Dispose() 361 rwg.Wait() 362 } 363 364 func BenchmarkRBPut(b *testing.B) { 365 rb := NewRingBuffer[int](uint64(b.N)) 366 367 b.ResetTimer() 368 369 for i := 0; i < b.N; i++ { 370 ok, err := rb.Offer(i) 371 if !ok { 372 b.Fail() 373 } 374 if err != nil { 375 b.Log(err) 376 b.Fail() 377 } 378 } 379 } 380 381 func BenchmarkRBGet(b *testing.B) { 382 rb := NewRingBuffer[int](uint64(b.N)) 383 384 for i := 0; i < b.N; i++ { 385 rb.Offer(i) 386 } 387 388 b.ResetTimer() 389 390 for i := 0; i < b.N; i++ { 391 rb.Get() 392 } 393 } 394 395 func BenchmarkRBAllocation(b *testing.B) { 396 for i := 0; i < b.N; i++ { 397 NewRingBuffer[int](1024) 398 } 399 } 400 401 func TestRingReset(t *testing.T) { 402 rb := NewRingBuffer[int](1) 403 assert.Equal(t, uint64(0), rb.queue) 404 assert.Equal(t, uint64(0), rb.dequeue) 405 406 assert.NoError(t, rb.Put(1)) 407 assert.Equal(t, uint64(1), rb.queue) 408 assert.Equal(t, uint64(0), rb.dequeue) 409 410 rb.Reset() 411 assert.Equal(t, uint64(0), rb.queue) 412 assert.Equal(t, uint64(0), rb.dequeue) 413 v, ok, err := rb.Poll(time.Millisecond * 10) 414 assert.Error(t, err) 415 assert.False(t, ok) 416 assert.Equal(t, 0, v) 417 418 assert.NoError(t, rb.Put(1)) 419 }