github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/pdutil/clock.go (about)

     1  // Copyright 2021 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package pdutil
    15  
    16  import (
    17  	"context"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/pingcap/errors"
    22  	"github.com/pingcap/log"
    23  	pclock "github.com/pingcap/tiflow/engine/pkg/clock"
    24  	"github.com/pingcap/tiflow/pkg/retry"
    25  	"github.com/tikv/client-go/v2/oracle"
    26  	pd "github.com/tikv/pd/client"
    27  	"go.uber.org/zap"
    28  )
    29  
    30  const pdTimeUpdateInterval = 10 * time.Millisecond
    31  
    32  // Clock is a time source of PD cluster.
    33  type Clock interface {
    34  	// CurrentTime returns approximate current time from pd.
    35  	CurrentTime() time.Time
    36  	Run(ctx context.Context)
    37  	Stop()
    38  }
    39  
    40  // clock cache time get from PD periodically and cache it
    41  type clock struct {
    42  	pdClient pd.Client
    43  	mu       struct {
    44  		sync.RWMutex
    45  		// The time encoded in PD ts.
    46  		tsEventTime time.Time
    47  		// The time we receive PD ts.
    48  		tsProcessingTime time.Time
    49  	}
    50  	updateInterval time.Duration
    51  	cancel         context.CancelFunc
    52  	stopCh         chan struct{}
    53  }
    54  
    55  // NewClock return a new clock
    56  func NewClock(ctx context.Context, pdClient pd.Client) (*clock, error) {
    57  	ret := &clock{
    58  		pdClient:       pdClient,
    59  		stopCh:         make(chan struct{}, 1),
    60  		updateInterval: pdTimeUpdateInterval,
    61  	}
    62  	physical, _, err := pdClient.GetTS(ctx)
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	ret.mu.tsEventTime = oracle.GetTimeFromTS(oracle.ComposeTS(physical, 0))
    67  	ret.mu.tsProcessingTime = time.Now()
    68  	return ret, nil
    69  }
    70  
    71  // Run gets time from pd periodically.
    72  func (c *clock) Run(ctx context.Context) {
    73  	ctx, cancel := context.WithCancel(ctx)
    74  	c.mu.Lock()
    75  	c.cancel = cancel
    76  	c.mu.Unlock()
    77  	ticker := time.NewTicker(c.updateInterval)
    78  	defer func() { c.stopCh <- struct{}{} }()
    79  	for {
    80  		select {
    81  		// c.Stop() was called or parent ctx was canceled
    82  		case <-ctx.Done():
    83  			return
    84  		case <-ticker.C:
    85  			err := retry.Do(ctx, func() error {
    86  				physical, _, err := c.pdClient.GetTS(ctx)
    87  				if err != nil {
    88  					log.Info("get time from pd failed, retry later", zap.Error(err))
    89  					return err
    90  				}
    91  				c.mu.Lock()
    92  				c.mu.tsEventTime = oracle.GetTimeFromTS(oracle.ComposeTS(physical, 0))
    93  				c.mu.tsProcessingTime = time.Now()
    94  				c.mu.Unlock()
    95  				return nil
    96  			}, retry.WithBackoffBaseDelay(200), retry.WithMaxTries(10))
    97  			if err != nil {
    98  				log.Warn("get time from pd failed, do not update time cache",
    99  					zap.Time("cachedTime", c.mu.tsEventTime),
   100  					zap.Time("processingTime", c.mu.tsProcessingTime),
   101  					zap.Error(err))
   102  			}
   103  		}
   104  	}
   105  }
   106  
   107  // CurrentTime returns approximate current time from pd.
   108  func (c *clock) CurrentTime() time.Time {
   109  	c.mu.RLock()
   110  	defer c.mu.RUnlock()
   111  	tsEventTime := c.mu.tsEventTime
   112  	current := tsEventTime.Add(time.Since(c.mu.tsProcessingTime))
   113  	return current
   114  }
   115  
   116  // Stop clock.
   117  func (c *clock) Stop() {
   118  	c.mu.Lock()
   119  	c.cancel()
   120  	c.mu.Unlock()
   121  	<-c.stopCh
   122  }
   123  
   124  type clock4Test struct{}
   125  
   126  // NewClock4Test return a new clock for test.
   127  func NewClock4Test() Clock {
   128  	return &clock4Test{}
   129  }
   130  
   131  func (c *clock4Test) CurrentTime() time.Time {
   132  	return time.Now()
   133  }
   134  
   135  func (c *clock4Test) Run(ctx context.Context) {
   136  }
   137  
   138  func (c *clock4Test) Stop() {
   139  }
   140  
   141  type monotonicClock struct {
   142  	clock pclock.Clock
   143  }
   144  
   145  // NewMonotonicClock return a new monotonic clock.
   146  func NewMonotonicClock(pClock pclock.Clock) Clock {
   147  	return &monotonicClock{
   148  		clock: pClock,
   149  	}
   150  }
   151  
   152  func (c *monotonicClock) CurrentTime() time.Time {
   153  	return c.clock.Now()
   154  }
   155  
   156  func (c *monotonicClock) Run(ctx context.Context) {
   157  }
   158  
   159  func (c *monotonicClock) Stop() {
   160  }