vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/throttle/mysql/mysql_throttle_metric.go (about)

     1  /*
     2   Copyright 2017 GitHub Inc.
     3  
     4   Licensed under MIT License. See https://github.com/github/freno/blob/master/LICENSE
     5  */
     6  
     7  package mysql
     8  
     9  import (
    10  	"fmt"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/patrickmn/go-cache"
    15  
    16  	"vitess.io/vitess/go/stats"
    17  )
    18  
    19  // MetricsQueryType indicates the type of metrics query on MySQL backend. See following.
    20  type MetricsQueryType int
    21  
    22  const (
    23  	// MetricsQueryTypeDefault indictes the default, internal implementation. Specifically, our throttler runs a replication lag query
    24  	MetricsQueryTypeDefault MetricsQueryType = iota
    25  	// MetricsQueryTypeShowGlobal indicatesa SHOW GLOBAL (STATUS|VARIABLES) query
    26  	MetricsQueryTypeShowGlobal
    27  	// MetricsQueryTypeSelect indicates a custom SELECT query
    28  	MetricsQueryTypeSelect
    29  	// MetricsQueryTypeUnknown is an unknown query type, which we cannot run. This is an error
    30  	MetricsQueryTypeUnknown
    31  )
    32  
    33  var mysqlMetricCache = cache.New(cache.NoExpiration, 10*time.Second)
    34  
    35  func getMySQLMetricCacheKey(probe *Probe) string {
    36  	return fmt.Sprintf("%s:%s", probe.Key, probe.MetricQuery)
    37  }
    38  
    39  func cacheMySQLThrottleMetric(probe *Probe, mySQLThrottleMetric *MySQLThrottleMetric) *MySQLThrottleMetric {
    40  	if mySQLThrottleMetric.Err != nil {
    41  		return mySQLThrottleMetric
    42  	}
    43  	if probe.CacheMillis > 0 {
    44  		mysqlMetricCache.Set(getMySQLMetricCacheKey(probe), mySQLThrottleMetric, time.Duration(probe.CacheMillis)*time.Millisecond)
    45  	}
    46  	return mySQLThrottleMetric
    47  }
    48  
    49  func getCachedMySQLThrottleMetric(probe *Probe) *MySQLThrottleMetric {
    50  	if probe.CacheMillis == 0 {
    51  		return nil
    52  	}
    53  	if metric, found := mysqlMetricCache.Get(getMySQLMetricCacheKey(probe)); found {
    54  		mySQLThrottleMetric, _ := metric.(*MySQLThrottleMetric)
    55  		return mySQLThrottleMetric
    56  	}
    57  	return nil
    58  }
    59  
    60  // GetMetricsQueryType analyzes the type of a metrics query
    61  func GetMetricsQueryType(query string) MetricsQueryType {
    62  	if query == "" {
    63  		return MetricsQueryTypeDefault
    64  	}
    65  	if strings.HasPrefix(strings.ToLower(query), "select") {
    66  		return MetricsQueryTypeSelect
    67  	}
    68  	if strings.HasPrefix(strings.ToLower(query), "show global") {
    69  		return MetricsQueryTypeShowGlobal
    70  	}
    71  	return MetricsQueryTypeUnknown
    72  }
    73  
    74  // MySQLThrottleMetric has the probed metric for a mysql instance
    75  type MySQLThrottleMetric struct { // nolint:revive
    76  	ClusterName string
    77  	Key         InstanceKey
    78  	Value       float64
    79  	Err         error
    80  }
    81  
    82  // NewMySQLThrottleMetric creates a new MySQLThrottleMetric
    83  func NewMySQLThrottleMetric() *MySQLThrottleMetric {
    84  	return &MySQLThrottleMetric{Value: 0}
    85  }
    86  
    87  // GetClusterInstanceKey returns the ClusterInstanceKey part of the metric
    88  func (metric *MySQLThrottleMetric) GetClusterInstanceKey() ClusterInstanceKey {
    89  	return GetClusterInstanceKey(metric.ClusterName, &metric.Key)
    90  }
    91  
    92  // Get implements MetricResult
    93  func (metric *MySQLThrottleMetric) Get() (float64, error) {
    94  	return metric.Value, metric.Err
    95  }
    96  
    97  // ReadThrottleMetric returns a metric for the given probe. Either by explicit query
    98  // or via SHOW SLAVE STATUS
    99  func ReadThrottleMetric(probe *Probe, clusterName string, overrideGetMetricFunc func() *MySQLThrottleMetric) (mySQLThrottleMetric *MySQLThrottleMetric) {
   100  	if mySQLThrottleMetric := getCachedMySQLThrottleMetric(probe); mySQLThrottleMetric != nil {
   101  		return mySQLThrottleMetric
   102  		// On cached results we avoid taking latency metrics
   103  	}
   104  
   105  	started := time.Now()
   106  	mySQLThrottleMetric = NewMySQLThrottleMetric()
   107  	mySQLThrottleMetric.ClusterName = clusterName
   108  	mySQLThrottleMetric.Key = probe.Key
   109  
   110  	defer func(metric *MySQLThrottleMetric, started time.Time) {
   111  		go func() {
   112  			stats.GetOrNewGauge("ThrottlerProbesLatency", "probes latency").Set(time.Since(started).Nanoseconds())
   113  			stats.GetOrNewCounter("ThrottlerProbesTotal", "total probes").Add(1)
   114  			if metric.Err != nil {
   115  				stats.GetOrNewCounter("ThrottlerProbesError", "total probes errors").Add(1)
   116  			}
   117  		}()
   118  	}(mySQLThrottleMetric, started)
   119  
   120  	mySQLThrottleMetric = overrideGetMetricFunc()
   121  	return cacheMySQLThrottleMetric(probe, mySQLThrottleMetric)
   122  }