Skip to content

Commit 364209f

Browse files
authored
Throttler: exempt apps via UpdateThrottlerConfig --throttle-app-exempt (#13666)
Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com>
1 parent b065394 commit 364209f

16 files changed

Lines changed: 376 additions & 144 deletions

File tree

go/cmd/vtctldclient/command/throttler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func init() {
8686
UpdateThrottlerConfig.Flags().StringVar(&throttledAppRule.Name, "throttle-app", "", "an app name to throttle")
8787
UpdateThrottlerConfig.Flags().Float64Var(&throttledAppRule.Ratio, "throttle-app-ratio", throttle.DefaultThrottleRatio, "ratio to throttle app (app specififed in --throttled-app)")
8888
UpdateThrottlerConfig.Flags().DurationVar(&throttledAppDuration, "throttle-app-duration", throttle.DefaultAppThrottleDuration, "duration after which throttled app rule expires (app specififed in --throttled-app)")
89+
UpdateThrottlerConfig.Flags().BoolVar(&throttledAppRule.Exempt, "throttle-app-exempt", throttledAppRule.Exempt, "exempt this app from being at all throttled. WARNING: use with extreme care, as this is likely to push metrics beyond the throttler's threshold, and starve other apps")
8990

9091
Root.AddCommand(UpdateThrottlerConfig)
9192
}

go/test/endtoend/onlineddl/vrepl/onlineddl_vrepl_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func TestSchemaChange(t *testing.T) {
249249
}
250250
})
251251
t.Run("updating throttler config", func(t *testing.T) {
252-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, customThreshold, noCustomQuery)
252+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, customThreshold, noCustomQuery, nil)
253253
require.NoError(t, err)
254254
})
255255

go/test/endtoend/tabletmanager/throttler_topo/throttler_test.go

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ import (
2828

2929
"vitess.io/vitess/go/mysql"
3030
"vitess.io/vitess/go/sqltypes"
31-
"vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base"
32-
3331
"vitess.io/vitess/go/test/endtoend/cluster"
3432
"vitess.io/vitess/go/test/endtoend/throttler"
33+
"vitess.io/vitess/go/vt/logutil"
34+
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
35+
"vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base"
3536

3637
"github.com/stretchr/testify/assert"
3738
"github.com/stretchr/testify/require"
@@ -45,6 +46,7 @@ const (
4546
onDemandHeartbeatDuration = 5 * time.Second
4647
throttlerEnabledTimeout = 60 * time.Second
4748
useDefaultQuery = ""
49+
testAppName = "test"
4850
)
4951

5052
var (
@@ -170,12 +172,12 @@ func throttledApps(tablet *cluster.Vttablet) (resp *http.Response, respBody stri
170172
}
171173

172174
func throttleCheck(tablet *cluster.Vttablet, skipRequestHeartbeats bool) (*http.Response, error) {
173-
resp, err := httpClient.Get(fmt.Sprintf("http://localhost:%d/%s?app=test&s=%t", tablet.HTTPPort, checkAPIPath, skipRequestHeartbeats))
175+
resp, err := httpClient.Get(fmt.Sprintf("http://localhost:%d/%s?app=%s&s=%t", tablet.HTTPPort, checkAPIPath, testAppName, skipRequestHeartbeats))
174176
return resp, err
175177
}
176178

177179
func throttleCheckSelf(tablet *cluster.Vttablet) (*http.Response, error) {
178-
return httpClient.Get(fmt.Sprintf("http://localhost:%d/%s?app=test", tablet.HTTPPort, checkSelfAPIPath))
180+
return httpClient.Get(fmt.Sprintf("http://localhost:%d/%s?app=%s", tablet.HTTPPort, checkSelfAPIPath, testAppName))
179181
}
180182

181183
func warmUpHeartbeat(t *testing.T) (respStatus int) {
@@ -245,7 +247,7 @@ func TestInitialThrottler(t *testing.T) {
245247
waitForThrottleCheckStatus(t, primaryTablet, http.StatusOK)
246248
})
247249
t.Run("enabling throttler with very low threshold", func(t *testing.T) {
248-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, unreasonablyLowThreshold.Seconds(), useDefaultQuery)
250+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, unreasonablyLowThreshold.Seconds(), useDefaultQuery, nil)
249251
assert.NoError(t, err)
250252

251253
// Wait for the throttler to be enabled everywhere with the new config.
@@ -257,7 +259,7 @@ func TestInitialThrottler(t *testing.T) {
257259
waitForThrottleCheckStatus(t, primaryTablet, http.StatusTooManyRequests)
258260
})
259261
t.Run("disabling throttler", func(t *testing.T) {
260-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, true, unreasonablyLowThreshold.Seconds(), useDefaultQuery)
262+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, true, unreasonablyLowThreshold.Seconds(), useDefaultQuery, nil)
261263
assert.NoError(t, err)
262264

263265
// Wait for the throttler to be disabled everywhere.
@@ -271,7 +273,7 @@ func TestInitialThrottler(t *testing.T) {
271273
t.Run("enabling throttler, again", func(t *testing.T) {
272274
// Enable the throttler again with the default query which also moves us back
273275
// to the default threshold.
274-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, 0, useDefaultQuery)
276+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, 0, useDefaultQuery, nil)
275277
assert.NoError(t, err)
276278

277279
// Wait for the throttler to be enabled everywhere again with the default config.
@@ -283,7 +285,7 @@ func TestInitialThrottler(t *testing.T) {
283285
waitForThrottleCheckStatus(t, primaryTablet, http.StatusTooManyRequests)
284286
})
285287
t.Run("setting high threshold", func(t *testing.T) {
286-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, false, extremelyHighThreshold.Seconds(), useDefaultQuery)
288+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, false, extremelyHighThreshold.Seconds(), useDefaultQuery, nil)
287289
assert.NoError(t, err)
288290

289291
// Wait for the throttler to be enabled everywhere with new config.
@@ -295,7 +297,7 @@ func TestInitialThrottler(t *testing.T) {
295297
waitForThrottleCheckStatus(t, primaryTablet, http.StatusOK)
296298
})
297299
t.Run("setting low threshold", func(t *testing.T) {
298-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, false, throttler.DefaultThreshold.Seconds(), useDefaultQuery)
300+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, false, throttler.DefaultThreshold.Seconds(), useDefaultQuery, nil)
299301
assert.NoError(t, err)
300302

301303
// Wait for the throttler to be enabled everywhere with new config.
@@ -392,6 +394,26 @@ func TestLag(t *testing.T) {
392394
defer resp.Body.Close()
393395
assert.Equalf(t, http.StatusTooManyRequests, resp.StatusCode, "Unexpected response from throttler: %s", getResponseBody(resp))
394396
})
397+
t.Run("exempting test app", func(t *testing.T) {
398+
appRule := &topodatapb.ThrottledAppRule{
399+
Name: testAppName,
400+
ExpiresAt: logutil.TimeToProto(time.Now().Add(time.Hour)),
401+
Exempt: true,
402+
}
403+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, false, throttler.DefaultThreshold.Seconds(), useDefaultQuery, appRule)
404+
assert.NoError(t, err)
405+
waitForThrottleCheckStatus(t, primaryTablet, http.StatusOK)
406+
})
407+
t.Run("unexempting test app", func(t *testing.T) {
408+
appRule := &topodatapb.ThrottledAppRule{
409+
Name: testAppName,
410+
ExpiresAt: logutil.TimeToProto(time.Now()),
411+
}
412+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, false, false, throttler.DefaultThreshold.Seconds(), useDefaultQuery, appRule)
413+
assert.NoError(t, err)
414+
waitForThrottleCheckStatus(t, primaryTablet, http.StatusTooManyRequests)
415+
})
416+
395417
t.Run("starting replication", func(t *testing.T) {
396418
err := clusterInstance.VtctlclientProcess.ExecuteCommand("StartReplication", replicaTablet.Alias)
397419
assert.NoError(t, err)
@@ -436,7 +458,7 @@ func TestCustomQuery(t *testing.T) {
436458
defer cluster.PanicHandler(t)
437459

438460
t.Run("enabling throttler with custom query and threshold", func(t *testing.T) {
439-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, customThreshold, customQuery)
461+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, customThreshold, customQuery, nil)
440462
assert.NoError(t, err)
441463

442464
// Wait for the throttler to be enabled everywhere with new custom config.
@@ -504,7 +526,7 @@ func TestRestoreDefaultQuery(t *testing.T) {
504526

505527
// Validate going back from custom-query to default-query (replication lag) still works.
506528
t.Run("enabling throttler with default query and threshold", func(t *testing.T) {
507-
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, throttler.DefaultThreshold.Seconds(), useDefaultQuery)
529+
_, err := throttler.UpdateThrottlerTopoConfig(clusterInstance, true, false, throttler.DefaultThreshold.Seconds(), useDefaultQuery, nil)
508530
assert.NoError(t, err)
509531

510532
// Wait for the throttler to be up and running everywhere again with the default config.

go/test/endtoend/throttler/util.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
"vitess.io/vitess/go/test/endtoend/cluster"
3434
"vitess.io/vitess/go/vt/concurrency"
3535
"vitess.io/vitess/go/vt/log"
36+
"vitess.io/vitess/go/vt/logutil"
37+
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
3638
"vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/base"
3739
"vitess.io/vitess/go/vt/vttablet/tabletserver/throttle/throttlerapp"
3840
)
@@ -57,7 +59,7 @@ var DefaultConfig = &Config{
5759
// This retries the command until it succeeds or times out as the
5860
// SrvKeyspace record may not yet exist for a newly created
5961
// Keyspace that is still initializing before it becomes serving.
60-
func UpdateThrottlerTopoConfigRaw(vtctldProcess *cluster.VtctldClientProcess, keyspaceName string, enable bool, disable bool, threshold float64, metricsQuery string) (result string, err error) {
62+
func UpdateThrottlerTopoConfigRaw(vtctldProcess *cluster.VtctldClientProcess, keyspaceName string, enable bool, disable bool, threshold float64, metricsQuery string, appRule *topodatapb.ThrottledAppRule) (result string, err error) {
6163
args := []string{}
6264
args = append(args, "UpdateThrottlerConfig")
6365
if enable {
@@ -75,6 +77,14 @@ func UpdateThrottlerTopoConfigRaw(vtctldProcess *cluster.VtctldClientProcess, ke
7577
} else {
7678
args = append(args, "--check-as-check-shard")
7779
}
80+
if appRule != nil {
81+
args = append(args, "--throttle-app", appRule.Name)
82+
args = append(args, "--throttle-app-duration", logutil.ProtoToTime(appRule.ExpiresAt).Sub(time.Now()).String())
83+
args = append(args, "--throttle-app-ratio", fmt.Sprintf("%f", appRule.Ratio))
84+
if appRule.Exempt {
85+
args = append(args, "--throttle-app-exempt")
86+
}
87+
}
7888
args = append(args, keyspaceName)
7989

8090
ctx, cancel := context.WithTimeout(context.Background(), ConfigTimeout)
@@ -100,14 +110,14 @@ func UpdateThrottlerTopoConfigRaw(vtctldProcess *cluster.VtctldClientProcess, ke
100110
// This retries the command until it succeeds or times out as the
101111
// SrvKeyspace record may not yet exist for a newly created
102112
// Keyspace that is still initializing before it becomes serving.
103-
func UpdateThrottlerTopoConfig(clusterInstance *cluster.LocalProcessCluster, enable bool, disable bool, threshold float64, metricsQuery string) (string, error) {
113+
func UpdateThrottlerTopoConfig(clusterInstance *cluster.LocalProcessCluster, enable bool, disable bool, threshold float64, metricsQuery string, appRule *topodatapb.ThrottledAppRule) (string, error) {
104114
rec := concurrency.AllErrorRecorder{}
105115
var (
106116
err error
107117
res strings.Builder
108118
)
109119
for _, ks := range clusterInstance.Keyspaces {
110-
ires, err := UpdateThrottlerTopoConfigRaw(&clusterInstance.VtctldClientProcess, ks.Name, enable, disable, threshold, metricsQuery)
120+
ires, err := UpdateThrottlerTopoConfigRaw(&clusterInstance.VtctldClientProcess, ks.Name, enable, disable, threshold, metricsQuery, appRule)
111121
if err != nil {
112122
rec.RecordError(err)
113123
}
@@ -335,7 +345,7 @@ func WaitForThrottledApp(t *testing.T, tablet *cluster.Vttablet, throttlerApp th
335345
// The throttler is configued to use the standard replication lag metric. The function waits until the throttler is confirmed
336346
// to be running on all tablets.
337347
func EnableLagThrottlerAndWaitForStatus(t *testing.T, clusterInstance *cluster.LocalProcessCluster, lag time.Duration) {
338-
_, err := UpdateThrottlerTopoConfig(clusterInstance, true, false, lag.Seconds(), "")
348+
_, err := UpdateThrottlerTopoConfig(clusterInstance, true, false, lag.Seconds(), "", nil)
339349
require.NoError(t, err)
340350

341351
for _, ks := range clusterInstance.Keyspaces {

go/test/endtoend/vreplication/cluster_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ func (vc *VitessCluster) AddKeyspace(t *testing.T, cells []*Cell, ksName string,
369369
}
370370

371371
log.Infof("Applying throttler config for keyspace %s", keyspace.Name)
372-
res, err := throttler.UpdateThrottlerTopoConfigRaw(vc.VtctldClient, keyspace.Name, true, false, throttlerConfig.Threshold, throttlerConfig.Query)
372+
res, err := throttler.UpdateThrottlerTopoConfigRaw(vc.VtctldClient, keyspace.Name, true, false, throttlerConfig.Threshold, throttlerConfig.Query, nil)
373373
require.NoError(t, err, res)
374374

375375
cellsToWatch := ""

0 commit comments

Comments
 (0)