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 := ×tampWaiter{} 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 := ×tampWaiter{} 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 }