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  }