github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/client/timestamp_waiter.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 "runtime" 20 "sync" 21 "sync/atomic" 22 23 "github.com/matrixorigin/matrixone/pkg/common/moerr" 24 "github.com/matrixorigin/matrixone/pkg/common/stopper" 25 "github.com/matrixorigin/matrixone/pkg/pb/timestamp" 26 "github.com/matrixorigin/matrixone/pkg/txn/util" 27 ) 28 29 const ( 30 maxNotifiedCount = 64 31 ) 32 33 type timestampWaiter struct { 34 stopper stopper.Stopper 35 notifiedC chan timestamp.Timestamp 36 latestTS atomic.Pointer[timestamp.Timestamp] 37 mu struct { 38 sync.Mutex 39 cancelC chan struct{} 40 waiters []*waiter 41 lastNotified timestamp.Timestamp 42 } 43 } 44 45 // NewTimestampWaiter create timestamp waiter 46 func NewTimestampWaiter() TimestampWaiter { 47 tw := ×tampWaiter{ 48 stopper: *stopper.NewStopper("timestamp-waiter", 49 stopper.WithLogger(util.GetLogger().RawLogger())), 50 notifiedC: make(chan timestamp.Timestamp, maxNotifiedCount), 51 } 52 tw.mu.cancelC = make(chan struct{}, 1) 53 if err := tw.stopper.RunTask(tw.handleEvent); err != nil { 54 panic(err) 55 } 56 return tw 57 } 58 59 func (tw *timestampWaiter) GetTimestamp(ctx context.Context, ts timestamp.Timestamp) (timestamp.Timestamp, error) { 60 latest := tw.latestTS.Load() 61 if latest != nil && latest.GreaterEq(ts) { 62 return latest.Next(), nil 63 } 64 65 w, err := tw.addToWait(ts) 66 if err != nil { 67 return timestamp.Timestamp{}, err 68 } 69 if w != nil { 70 defer w.close() 71 if err := w.wait(ctx); err != nil { 72 return timestamp.Timestamp{}, err 73 } 74 } 75 v := tw.latestTS.Load() 76 return v.Next(), nil 77 } 78 79 func (tw *timestampWaiter) NotifyLatestCommitTS(ts timestamp.Timestamp) { 80 util.LogTxnPushedTimestampUpdated(ts) 81 tw.notifiedC <- ts 82 } 83 84 func (tw *timestampWaiter) Pause() { 85 tw.mu.Lock() 86 defer tw.mu.Unlock() 87 select { 88 case _, ok := <-tw.mu.cancelC: 89 if !ok { 90 // The channel is already closed. 91 return 92 } 93 default: 94 } 95 close(tw.mu.cancelC) 96 tw.mu.cancelC = nil 97 tw.removeWaitersLocked() 98 util.LogTimestampWaiterCanceled() 99 } 100 101 func (tw *timestampWaiter) Resume() { 102 tw.mu.Lock() 103 defer tw.mu.Unlock() 104 tw.mu.cancelC = make(chan struct{}, 1) 105 } 106 107 func (tw *timestampWaiter) CancelC() chan struct{} { 108 tw.mu.Lock() 109 defer tw.mu.Unlock() 110 return tw.mu.cancelC 111 } 112 113 func (tw *timestampWaiter) Close() { 114 tw.stopper.Stop() 115 } 116 117 func (tw *timestampWaiter) LatestTS() timestamp.Timestamp { 118 if tw == nil { 119 return timestamp.Timestamp{} 120 } 121 ts := tw.latestTS.Load() 122 if ts == nil { 123 return timestamp.Timestamp{} 124 } 125 return *ts 126 } 127 128 func (tw *timestampWaiter) addToWait(ts timestamp.Timestamp) (*waiter, error) { 129 tw.mu.Lock() 130 defer tw.mu.Unlock() 131 if !tw.mu.lastNotified.IsEmpty() && 132 tw.mu.lastNotified.GreaterEq(ts) { 133 return nil, nil 134 } 135 136 if tw.mu.cancelC == nil { 137 return nil, moerr.NewWaiterPausedNoCtx() 138 } 139 w := newWaiter(ts, tw.mu.cancelC) 140 w.ref() 141 tw.mu.waiters = append(tw.mu.waiters, w) 142 return w, nil 143 } 144 145 func (tw *timestampWaiter) notifyWaiters(ts timestamp.Timestamp) { 146 tw.mu.Lock() 147 defer tw.mu.Unlock() 148 waiters := tw.mu.waiters[:0] 149 for idx, w := range tw.mu.waiters { 150 if w != nil && ts.GreaterEq(w.waitAfter) { 151 w.notify() 152 w.unref() 153 tw.mu.waiters[idx] = nil 154 w = nil 155 } 156 if w != nil { 157 waiters = append(waiters, w) 158 } 159 } 160 161 tw.mu.waiters = waiters 162 tw.mu.lastNotified = ts 163 } 164 165 func (tw *timestampWaiter) removeWaitersLocked() { 166 for idx, w := range tw.mu.waiters { 167 if w != nil { 168 w.unref() 169 tw.mu.waiters[idx] = nil 170 } 171 } 172 tw.mu.waiters = tw.mu.waiters[:0] 173 } 174 175 func (tw *timestampWaiter) handleEvent(ctx context.Context) { 176 var latest timestamp.Timestamp 177 for { 178 select { 179 case <-ctx.Done(): 180 return 181 case ts := <-tw.notifiedC: 182 ts = tw.fetchLatestTS(ts) 183 if latest.Less(ts) { 184 latest = ts 185 tw.latestTS.Store(&ts) 186 tw.notifyWaiters(ts) 187 } 188 } 189 } 190 } 191 192 func (tw *timestampWaiter) fetchLatestTS(ts timestamp.Timestamp) timestamp.Timestamp { 193 for { 194 select { 195 case v := <-tw.notifiedC: 196 if v.Greater(ts) { 197 ts = v 198 } 199 default: 200 return ts 201 } 202 } 203 } 204 205 var ( 206 waitersPool = sync.Pool{ 207 New: func() any { 208 w := &waiter{ 209 notifyC: make(chan struct{}, 1), 210 } 211 runtime.SetFinalizer(w, func(w *waiter) { 212 close(w.notifyC) 213 }) 214 return w 215 }, 216 } 217 ) 218 219 type waiter struct { 220 waitAfter timestamp.Timestamp 221 notifyC chan struct{} 222 refCount atomic.Int32 223 // cancelC is the channel which is used to notify there is no 224 // need to wait anymore. 225 cancelC chan struct{} 226 } 227 228 func newWaiter(ts timestamp.Timestamp, c chan struct{}) *waiter { 229 w := waitersPool.Get().(*waiter) 230 w.init(ts, c) 231 return w 232 } 233 234 func (w *waiter) init(ts timestamp.Timestamp, c chan struct{}) { 235 w.waitAfter = ts 236 w.cancelC = c 237 if w.ref() != 1 { 238 panic("BUG: waiter init must has ref count 1") 239 } 240 } 241 242 func (w *waiter) wait(ctx context.Context) error { 243 select { 244 case <-ctx.Done(): 245 return ctx.Err() 246 case <-w.notifyC: 247 return nil 248 case <-w.cancelC: 249 return moerr.NewWaiterPausedNoCtx() 250 } 251 } 252 253 func (w *waiter) notify() { 254 w.notifyC <- struct{}{} 255 } 256 257 func (w *waiter) ref() int32 { 258 return w.refCount.Add(1) 259 } 260 261 func (w *waiter) unref() { 262 n := w.refCount.Add(-1) 263 if n < 0 { 264 panic("BUG: negative ref count") 265 } 266 if n == 0 { 267 w.reset() 268 waitersPool.Put(w) 269 } 270 } 271 272 func (w *waiter) close() { 273 w.unref() 274 } 275 276 func (w *waiter) reset() { 277 for { 278 select { 279 case <-w.notifyC: 280 case _, ok := <-w.cancelC: 281 if !ok { 282 return 283 } 284 default: 285 return 286 } 287 } 288 }