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 }