github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/client/timestamp_waiter_test.go (about)

     1  // Copyright 2023 Matrix Origin
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package client
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/lni/goutils/leaktest"
    24  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    25  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    26  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestGetTimestamp(t *testing.T) {
    32  	runTimestampWaiterTests(
    33  		t,
    34  		func(tw *timestampWaiter) {
    35  			ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
    36  			defer cancel()
    37  
    38  			for i := int64(1); i < 100; i++ {
    39  				ts := newTestTimestamp(i)
    40  				tw.latestTS.Store(&ts)
    41  				v, err := tw.GetTimestamp(ctx, newTestTimestamp(i))
    42  				require.NoError(t, err)
    43  				assert.Equal(t, ts.Next(), v)
    44  			}
    45  		},
    46  	)
    47  }
    48  
    49  func TestGetTimestampWithWaitTimeout(t *testing.T) {
    50  	runTimestampWaiterTests(
    51  		t,
    52  		func(tw *timestampWaiter) {
    53  			timeout := time.Millisecond * 100
    54  			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
    55  			defer cancel()
    56  
    57  			_, err := tw.GetTimestamp(ctx, newTestTimestamp(10))
    58  			require.Error(t, err)
    59  		},
    60  	)
    61  }
    62  
    63  func TestGetTimestampWithNotified(t *testing.T) {
    64  	runTimestampWaiterTests(
    65  		t,
    66  		func(tw *timestampWaiter) {
    67  			timeout := time.Second * 10
    68  			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
    69  			defer cancel()
    70  
    71  			c := make(chan struct{})
    72  			go func() {
    73  				defer close(c)
    74  				tw.NotifyLatestCommitTS(newTestTimestamp(10))
    75  			}()
    76  			<-c
    77  			ts, err := tw.GetTimestamp(ctx, newTestTimestamp(10))
    78  			require.NoError(t, err)
    79  			v := newTestTimestamp(10)
    80  			assert.Equal(t, v.Next(), ts)
    81  		},
    82  	)
    83  }
    84  
    85  func TestNotifyWaiters(t *testing.T) {
    86  	tw := &timestampWaiter{}
    87  	tw.mu.cancelC = make(chan struct{}, 1)
    88  	var values []*waiter
    89  
    90  	w, err := tw.addToWait(newTestTimestamp(1))
    91  	assert.NoError(t, err)
    92  	values = append(values, w)
    93  
    94  	w, err = tw.addToWait(newTestTimestamp(6))
    95  	assert.NoError(t, err)
    96  	values = append(values, w)
    97  
    98  	w, err = tw.addToWait(newTestTimestamp(3))
    99  	assert.NoError(t, err)
   100  	values = append(values, w)
   101  
   102  	w, err = tw.addToWait(newTestTimestamp(2))
   103  	assert.NoError(t, err)
   104  	values = append(values, w)
   105  
   106  	w, err = tw.addToWait(newTestTimestamp(5))
   107  	assert.NoError(t, err)
   108  	values = append(values, w)
   109  
   110  	var wg sync.WaitGroup
   111  	for _, w := range values {
   112  		wg.Add(1)
   113  		go func(w *waiter) {
   114  			defer wg.Done()
   115  			w.wait(context.Background())
   116  		}(w)
   117  	}
   118  	tw.notifyWaiters(newTestTimestamp(4))
   119  	assert.Equal(t, 2, len(tw.mu.waiters))
   120  	assert.Equal(t, newTestTimestamp(6), tw.mu.waiters[0].waitAfter)
   121  	assert.Equal(t, newTestTimestamp(5), tw.mu.waiters[1].waitAfter)
   122  
   123  	tw.notifyWaiters(newTestTimestamp(7))
   124  	wg.Wait()
   125  	assert.Equal(t, 0, len(tw.mu.waiters))
   126  }
   127  
   128  func TestRemoveWaiters(t *testing.T) {
   129  	tw := &timestampWaiter{}
   130  	tw.mu.cancelC = make(chan struct{}, 1)
   131  	var wg sync.WaitGroup
   132  	for i := 0; i < 10; i++ {
   133  		w, err := tw.addToWait(newTestTimestamp(int64(i)))
   134  		assert.NoError(t, err)
   135  		wg.Add(1)
   136  		go func(w *waiter) {
   137  			defer wg.Done()
   138  			// return waiter canceled error
   139  			assert.Error(t, w.wait(context.Background()))
   140  		}(w)
   141  	}
   142  	tw.Pause()
   143  	wg.Wait()
   144  	assert.Equal(t, 0, len(tw.mu.waiters))
   145  }
   146  
   147  func TestGetTimestampWithCanceled(t *testing.T) {
   148  	runTimestampWaiterTests(
   149  		t,
   150  		func(tw *timestampWaiter) {
   151  			timeout := time.Second * 5
   152  			ctx, cancel := context.WithTimeout(context.TODO(), timeout)
   153  			defer cancel()
   154  
   155  			c := make(chan struct{})
   156  			go func() {
   157  				// If it is not canceled, it will hang here util context timeout.
   158  				_, err := tw.GetTimestamp(ctx, newTestTimestamp(10))
   159  				require.Equal(t, moerr.NewWaiterPausedNoCtx(), err)
   160  				c <- struct{}{}
   161  			}()
   162  			// we could only cancel the waiters that are already in the queue.
   163  			// The waiters after cancel, will wait for notify channel.
   164  			for {
   165  				tw.mu.Lock()
   166  				if len(tw.mu.waiters) > 0 {
   167  					tw.mu.Unlock()
   168  					break
   169  				}
   170  				tw.mu.Unlock()
   171  				time.Sleep(time.Millisecond * 10)
   172  			}
   173  			tw.Pause()
   174  			<-c
   175  		},
   176  	)
   177  }
   178  
   179  func BenchmarkGetTimestampWithWaitNotify(b *testing.B) {
   180  	runTimestampWaiterTests(
   181  		b,
   182  		func(tw *timestampWaiter) {
   183  			ctx, cancel := context.WithCancel(context.Background())
   184  			defer cancel()
   185  
   186  			var wg sync.WaitGroup
   187  			wg.Add(1)
   188  			c := make(chan struct{})
   189  			timer := time.NewTicker(time.Nanosecond)
   190  			defer timer.Stop()
   191  			go func() {
   192  				defer wg.Done()
   193  				var v int64
   194  				for {
   195  					select {
   196  					case <-timer.C:
   197  						tw.NotifyLatestCommitTS(newTestTimestamp(v))
   198  						v++
   199  					case <-c:
   200  						return
   201  					}
   202  				}
   203  			}()
   204  			b.ResetTimer()
   205  			for i := 0; i < b.N; i++ {
   206  				ts := newTestTimestamp(int64(i))
   207  				v, err := tw.GetTimestamp(ctx, ts)
   208  				if err != nil {
   209  					panic(err)
   210  				}
   211  				if v.LessEq(ts) {
   212  					panic(v)
   213  				}
   214  			}
   215  			close(c)
   216  			wg.Wait()
   217  		},
   218  	)
   219  }
   220  
   221  func BenchmarkGetTimestampWithNoWait(b *testing.B) {
   222  	runTimestampWaiterTests(
   223  		b,
   224  		func(tw *timestampWaiter) {
   225  			ctx, cancel := context.WithCancel(context.Background())
   226  			defer cancel()
   227  
   228  			tw.NotifyLatestCommitTS(newTestTimestamp(1))
   229  			ts := newTestTimestamp(0)
   230  			b.ResetTimer()
   231  			for i := 0; i < b.N; i++ {
   232  				v, err := tw.GetTimestamp(ctx, ts)
   233  				if err != nil {
   234  					panic(err)
   235  				}
   236  				if v.LessEq(ts) {
   237  					panic(v)
   238  				}
   239  			}
   240  		},
   241  	)
   242  }
   243  
   244  func newTestTimestamp(v int64) timestamp.Timestamp {
   245  	return timestamp.Timestamp{
   246  		PhysicalTime: v,
   247  	}
   248  }
   249  
   250  func runTimestampWaiterTests(
   251  	t testing.TB,
   252  	fn func(*timestampWaiter)) {
   253  	defer leaktest.AfterTest(t)()
   254  	runtime.SetupProcessLevelRuntime(runtime.DefaultRuntime())
   255  	tw := NewTimestampWaiter()
   256  	defer tw.Close()
   257  	fn(tw.(*timestampWaiter))
   258  }