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 := &timestampWaiter{
    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  }