Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-metrics throttler: adding InnoDB history_list_length metric #17262

Merged
34 changes: 34 additions & 0 deletions go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,16 @@ func throttledApps(tablet *cluster.Vttablet) (resp *http.Response, respBody stri
return resp, respBody, err
}

func vitessThrottleCheck(tablet *cluster.Vttablet, skipRequestHeartbeats bool) (*vtctldatapb.CheckThrottlerResponse, error) {
flags := &throttle.CheckFlags{
Scope: base.ShardScope,
SkipRequestHeartbeats: skipRequestHeartbeats,
MultiMetricsEnabled: true,
}
resp, err := throttler.CheckThrottler(clusterInstance, tablet, throttlerapp.VitessName, flags)
return resp, err
}

func throttleCheck(tablet *cluster.Vttablet, skipRequestHeartbeats bool) (*vtctldatapb.CheckThrottlerResponse, error) {
flags := &throttle.CheckFlags{
Scope: base.ShardScope,
Expand Down Expand Up @@ -305,6 +315,17 @@ func TestInitialThrottler(t *testing.T) {
waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_THRESHOLD_EXCEEDED)
})
t.Run("setting high threshold", func(t *testing.T) {
{
req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.LoadAvgMetricName.String(), Threshold: 5555}
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil)
assert.NoError(t, err)
}
{
req := &vtctldatapb.UpdateThrottlerConfigRequest{MetricName: base.MysqldLoadAvgMetricName.String(), Threshold: 5555}
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil)
assert.NoError(t, err)
}

req := &vtctldatapb.UpdateThrottlerConfigRequest{Threshold: extremelyHighThreshold.Seconds()}
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil)
assert.NoError(t, err)
Expand All @@ -317,6 +338,19 @@ func TestInitialThrottler(t *testing.T) {
t.Run("validating OK response from throttler with high threshold", func(t *testing.T) {
waitForThrottleCheckStatus(t, primaryTablet, tabletmanagerdatapb.CheckThrottlerResponseCode_OK)
})
t.Run("validating vitess app throttler check", func(t *testing.T) {
resp, err := vitessThrottleCheck(primaryTablet, true)
require.NoError(t, err)
for _, metricName := range base.KnownMetricNames {
t.Run(metricName.String(), func(t *testing.T) {
assert.Contains(t, resp.Check.Metrics, metricName.String())
metric := resp.Check.Metrics[metricName.String()]
require.NotNil(t, metric)
assert.Equal(t, tabletmanagerdatapb.CheckThrottlerResponseCode_OK, metric.ResponseCode, "metric: %+v", metric)
})
}
})

t.Run("setting low threshold", func(t *testing.T) {
req := &vtctldatapb.UpdateThrottlerConfigRequest{Threshold: throttler.DefaultThreshold.Seconds()}
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, req, nil, nil)
Expand Down
1 change: 1 addition & 0 deletions go/vt/vttablet/tabletserver/throttle/base/metric_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const (
ThreadsRunningMetricName MetricName = "threads_running"
CustomMetricName MetricName = "custom"
LoadAvgMetricName MetricName = "loadavg"
HistoryListLengthMetricName MetricName = "history_list_length"
MysqldLoadAvgMetricName MetricName = "mysqld-loadavg"
MysqldDatadirUsedRatioMetricName MetricName = "mysqld-datadir-used-ratio"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,17 @@ func TestKnownMetricNames(t *testing.T) {
assert.Contains(t, KnownMetricNames, LoadAvgMetricName)
assert.Contains(t, KnownMetricNames, CustomMetricName)
assert.Contains(t, KnownMetricNames, DefaultMetricName)
assert.Contains(t, KnownMetricNames, HistoryListLengthMetricName)
assert.Contains(t, KnownMetricNames, MysqldLoadAvgMetricName)
assert.Contains(t, KnownMetricNames, MysqldDatadirUsedRatioMetricName)
}

func TestSingleWordCamelKnownMetricNames(t *testing.T) {
func TestKnownMetricNamesPascalCase(t *testing.T) {
expectCases := map[MetricName]string{
LagMetricName: "Lag",
ThreadsRunningMetricName: "ThreadsRunning",
LoadAvgMetricName: "Loadavg",
HistoryListLengthMetricName: "HistoryListLength",
CustomMetricName: "Custom",
DefaultMetricName: "Default",
MysqldLoadAvgMetricName: "MysqldLoadavg",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2024 The Vitess Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package base

import (
"context"
"math"
"sync/atomic"
"time"
)

var (
historyListLengthQuery = "select count as history_len from information_schema.INNODB_METRICS where name = 'trx_rseg_history_len'"

cachedHistoryListLengthMetric atomic.Pointer[ThrottleMetric]
historyListLengthCacheDuration = 5 * time.Second
historyListLengthDefaultThreshold = math.Pow10(9)
)

var _ SelfMetric = registerSelfMetric(&HistoryListLengthSelfMetric{})

type HistoryListLengthSelfMetric struct {
}

func (m *HistoryListLengthSelfMetric) Name() MetricName {
return HistoryListLengthMetricName
}

func (m *HistoryListLengthSelfMetric) DefaultScope() Scope {
return SelfScope
}

func (m *HistoryListLengthSelfMetric) DefaultThreshold() float64 {
return historyListLengthDefaultThreshold
}

func (m *HistoryListLengthSelfMetric) RequiresConn() bool {
return true
}

func (m *HistoryListLengthSelfMetric) Read(ctx context.Context, params *SelfMetricReadParams) *ThrottleMetric {
// This function will be called sequentially, and therefore does not need strong mutex protection. Still, we use atomics
// to ensure correctness in case an external goroutine tries to read the metric concurrently.
metric := cachedHistoryListLengthMetric.Load()
if metric != nil {
return metric
}
metric = ReadSelfMySQLThrottleMetric(ctx, params.Conn, historyListLengthQuery)
cachedHistoryListLengthMetric.Store(metric)
time.AfterFunc(historyListLengthCacheDuration, func() {
cachedHistoryListLengthMetric.Store(nil)
})
return metric
}
17 changes: 17 additions & 0 deletions go/vt/vttablet/tabletserver/throttle/throttler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ var (
Value: 2.718,
Err: nil,
},
base.HistoryListLengthMetricName: &base.ThrottleMetric{
Scope: base.SelfScope,
Alias: "",
Value: 5,
Err: nil,
},
base.MysqldLoadAvgMetricName: &base.ThrottleMetric{
Scope: base.SelfScope,
Alias: "",
Expand Down Expand Up @@ -105,6 +111,11 @@ var (
ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK,
Value: 5.1,
},
base.HistoryListLengthMetricName.String(): {
StatusCode: http.StatusOK,
ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK,
Value: 6,
},
base.MysqldLoadAvgMetricName.String(): {
StatusCode: http.StatusOK,
ResponseCode: tabletmanagerdatapb.CheckThrottlerResponseCode_OK,
Expand Down Expand Up @@ -1853,6 +1864,7 @@ func TestChecks(t *testing.T) {
assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because flags.Scope is set
assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because flags.Scope is set
assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because flags.Scope is set
assert.EqualValues(t, 5, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // self value, because flags.Scope is set
assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // self value, because flags.Scope is set
assert.EqualValues(t, 0.85, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // self value, because flags.Scope is set
for _, metric := range checkResult.Metrics {
Expand Down Expand Up @@ -1914,6 +1926,7 @@ func TestChecks(t *testing.T) {
assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, because flags.Scope is set
assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value, because flags.Scope is set
assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, because flags.Scope is set
assert.EqualValues(t, 6, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // shard value, because flags.Scope is set
assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // shard value, because flags.Scope is set
assert.EqualValues(t, 0.87, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // shard value, because flags.Scope is set
for _, metric := range checkResult.Metrics {
Expand Down Expand Up @@ -1948,6 +1961,7 @@ func TestChecks(t *testing.T) {
assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // self value, because "self" is the default scope for threads_running
assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // self value, because "self" is the default scope for custom
assert.EqualValues(t, 2.718, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // self value, because "self" is the default scope for loadavg
assert.EqualValues(t, 5, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // self value, because "self" is the default scope for loadavg
assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // self value, because "self" is the default scope for loadavg
assert.EqualValues(t, 0.85, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // self value, because "self" is the default scope for loadavg
assert.EqualValues(t, base.ShardScope.String(), checkResult.Metrics[base.LagMetricName.String()].Scope)
Expand All @@ -1970,6 +1984,7 @@ func TestChecks(t *testing.T) {
base.MetricName("custom"),
base.MetricName("shard/loadavg"),
base.MetricName("shard/mysqld-loadavg"),
base.MetricName("self/history_list_length"),
base.MetricName("self/mysqld-datadir-used-ratio"),
base.MetricName("default"),
}
Expand All @@ -1986,6 +2001,7 @@ func TestChecks(t *testing.T) {
assert.EqualValues(t, 26, checkResult.Metrics[base.ThreadsRunningMetricName.String()].Value) // shard value, even though scope name is in metric name
assert.EqualValues(t, 17, checkResult.Metrics[base.CustomMetricName.String()].Value) // shard value because flags.Scope is set
assert.EqualValues(t, 5.1, checkResult.Metrics[base.LoadAvgMetricName.String()].Value) // shard value, not because scope name is in metric name but because flags.Scope is set
assert.EqualValues(t, 6, checkResult.Metrics[base.HistoryListLengthMetricName.String()].Value) // shard value, even though scope name is in metric name
assert.EqualValues(t, 0.3311, checkResult.Metrics[base.MysqldLoadAvgMetricName.String()].Value) // shard value, not because scope name is in metric name but because flags.Scope is set
assert.EqualValues(t, 0.87, checkResult.Metrics[base.MysqldDatadirUsedRatioMetricName.String()].Value) // shard value, even though scope name is in metric name
for _, metric := range checkResult.Metrics {
Expand Down Expand Up @@ -2257,6 +2273,7 @@ func TestReplica(t *testing.T) {
assert.Error(t, metricResult.Error, "metricName=%v, value=%v, threshold=%v", metricName, metricResult.Value, metricResult.Threshold)
assert.ErrorIs(t, metricResult.Error, base.ErrThresholdExceeded)
case base.ThreadsRunningMetricName,
base.HistoryListLengthMetricName,
base.MysqldLoadAvgMetricName,
base.MysqldDatadirUsedRatioMetricName:
assert.NoError(t, metricResult.Error, "metricName=%v, value=%v, threshold=%v", metricName, metricResult.Value, metricResult.Threshold)
Expand Down
Loading