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  }