vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/repltracker/repltracker.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package repltracker 18 19 import ( 20 "sync" 21 "time" 22 23 "vitess.io/vitess/go/stats" 24 "vitess.io/vitess/go/vt/log" 25 "vitess.io/vitess/go/vt/mysqlctl" 26 querypb "vitess.io/vitess/go/vt/proto/query" 27 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 28 "vitess.io/vitess/go/vt/vttablet/tabletserver/heartbeat" 29 "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" 30 ) 31 32 var ( 33 // HeartbeatWrites keeps a count of the number of heartbeats written over time. 34 writes = stats.NewCounter("HeartbeatWrites", "Count of heartbeats written over time") 35 // HeartbeatWriteErrors keeps a count of errors encountered while writing heartbeats. 36 writeErrors = stats.NewCounter("HeartbeatWriteErrors", "Count of errors encountered while writing heartbeats") 37 // HeartbeatReads keeps a count of the number of heartbeats read over time. 38 reads = stats.NewCounter("HeartbeatReads", "Count of heartbeats read over time") 39 // HeartbeatReadErrors keeps a count of errors encountered while reading heartbeats. 40 readErrors = stats.NewCounter("HeartbeatReadErrors", "Count of errors encountered while reading heartbeats") 41 // HeartbeatCumulativeLagNs is incremented by the current lag at each heartbeat read interval. Plotting this 42 // over time allows calculating of a rolling average lag. 43 cumulativeLagNs = stats.NewCounter("HeartbeatCumulativeLagNs", "Incremented by the current lag at each heartbeat read interval") 44 // HeartbeatCurrentLagNs is a point-in-time calculation of the lag, updated at each heartbeat read interval. 45 currentLagNs = stats.NewGauge("HeartbeatCurrentLagNs", "Point in time calculation of the heartbeat lag") 46 // HeartbeatLagNsHistogram is a histogram of the lag values. Cutoffs are 0, 1ms, 10ms, 100ms, 1s, 10s, 100s, 1000s 47 heartbeatLagNsHistogram = stats.NewGenericHistogram("HeartbeatLagNsHistogram", 48 "Histogram of lag values in nanoseconds", []int64{0, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12}, 49 []string{"0", "1ms", "10ms", "100ms", "1s", "10s", "100s", "1000s", ">1000s"}, "Count", "Total") 50 ) 51 52 // ReplTracker tracks replication lag. 53 type ReplTracker struct { 54 mode string 55 forceHeartbeat bool 56 57 mu sync.Mutex 58 isPrimary bool 59 60 hw *heartbeatWriter 61 hr *heartbeatReader 62 poller *poller 63 } 64 65 // NewReplTracker creates a new ReplTracker. 66 func NewReplTracker(env tabletenv.Env, alias *topodatapb.TabletAlias) *ReplTracker { 67 return &ReplTracker{ 68 mode: env.Config().ReplicationTracker.Mode, 69 forceHeartbeat: env.Config().EnableLagThrottler || env.Config().ReplicationTracker.HeartbeatOnDemandSeconds.Get() > 0, 70 hw: newHeartbeatWriter(env, alias), 71 hr: newHeartbeatReader(env), 72 poller: &poller{}, 73 } 74 } 75 76 // HeartbeatWriter returns the heartbeat writer used by this tracker 77 func (rt *ReplTracker) HeartbeatWriter() heartbeat.HeartbeatWriter { 78 return rt.hw 79 } 80 81 // InitDBConfig initializes the target name. 82 func (rt *ReplTracker) InitDBConfig(target *querypb.Target, mysqld mysqlctl.MysqlDaemon) { 83 rt.hw.InitDBConfig(target) 84 rt.hr.InitDBConfig(target) 85 rt.poller.InitDBConfig(mysqld) 86 } 87 88 // MakePrimary must be called if the tablet type becomes PRIMARY. 89 func (rt *ReplTracker) MakePrimary() { 90 rt.mu.Lock() 91 defer rt.mu.Unlock() 92 log.Info("Replication Tracker: going into primary mode") 93 94 rt.isPrimary = true 95 if rt.mode == tabletenv.Heartbeat { 96 rt.hr.Close() 97 rt.hw.Open() 98 } 99 if rt.forceHeartbeat { 100 rt.hw.Open() 101 } 102 } 103 104 // MakeNonPrimary must be called if the tablet type becomes non-PRIMARY. 105 func (rt *ReplTracker) MakeNonPrimary() { 106 rt.mu.Lock() 107 defer rt.mu.Unlock() 108 log.Info("Replication Tracker: going into non-primary mode") 109 110 rt.isPrimary = false 111 switch rt.mode { 112 case tabletenv.Heartbeat: 113 rt.hw.Close() 114 rt.hr.Open() 115 case tabletenv.Polling: 116 // Run the status once to pre-initialize values. 117 rt.poller.Status() 118 } 119 if rt.forceHeartbeat { 120 rt.hw.Close() 121 } 122 } 123 124 // Close closes ReplTracker. 125 func (rt *ReplTracker) Close() { 126 rt.hw.Close() 127 rt.hr.Close() 128 log.Info("Replication Tracker: closed") 129 } 130 131 // Status reports the replication status. 132 func (rt *ReplTracker) Status() (time.Duration, error) { 133 rt.mu.Lock() 134 defer rt.mu.Unlock() 135 136 switch { 137 case rt.isPrimary || rt.mode == tabletenv.Disable: 138 return 0, nil 139 case rt.mode == tabletenv.Heartbeat: 140 return rt.hr.Status() 141 } 142 // rt.mode == tabletenv.Poller 143 return rt.poller.Status() 144 } 145 146 // EnableHeartbeat enables or disables writes of heartbeat. This functionality 147 // is only used by tests. 148 func (rt *ReplTracker) EnableHeartbeat(enable bool) { 149 rt.hw.enableWrites(enable) 150 }