Skip to content

Commit e2d602a

Browse files
authored
*: add tidb_slow_log_max_per_sec variable to control the number of slow logs written per second (#63996)
close #63995
1 parent 71c61b9 commit e2d602a

8 files changed

Lines changed: 158 additions & 13 deletions

File tree

pkg/executor/adapter.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1713,6 +1713,11 @@ func (a *ExecStmt) LogSlowQuery(txnTS uint64, succ bool, hasMoreResults bool) {
17131713
}
17141714
}
17151715

1716+
if !vardef.GlobalSlowLogRateLimiter.Allow() {
1717+
sampleLoggerFactory().Info("slow log skipped due to rate limiting", zap.Int64("tidb_slow_log_max_per_sec", int64(vardef.GlobalSlowLogRateLimiter.Limit())))
1718+
return
1719+
}
1720+
17161721
if slowItems == nil {
17171722
slowItems = &variable.SlowQueryLogItems{}
17181723
}

pkg/executor/adapter_slow_log.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ import (
2525
"github.com/pingcap/tidb/pkg/parser/ast"
2626
"github.com/pingcap/tidb/pkg/sessionctx/slowlogrule"
2727
"github.com/pingcap/tidb/pkg/sessionctx/variable"
28+
"github.com/pingcap/tidb/pkg/util/logutil"
2829
"github.com/tikv/client-go/v2/util"
30+
"go.uber.org/zap"
2931
)
3032

3133
func init() {
@@ -41,6 +43,8 @@ func init() {
4143
}
4244
}
4345

46+
var sampleLoggerFactory = logutil.SampleLoggerFactory(time.Minute, 1, zap.String(logutil.LogFieldCategory, "slow log"))
47+
4448
func mergeConditionFields(dst, src map[string]struct{}) {
4549
for k := range src {
4650
dst[k] = struct{}{}

pkg/executor/adapter_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,61 @@ func TestWriteSlowLog(t *testing.T) {
384384
checkWriteSlowLog(true)
385385
}
386386

387+
func TestSlowLogMaxPerSec(t *testing.T) {
388+
store := testkit.CreateMockStore(t)
389+
tk := testkit.NewTestKit(t, store)
390+
391+
// default value
392+
tk.MustQuery(`show variables like "tidb_slow_log_max_per_sec"`).Check(
393+
testkit.Rows("tidb_slow_log_max_per_sec 0"),
394+
)
395+
_, err := tk.Exec(`select @@SESSION.tidb_slow_log_max_per_sec`)
396+
require.Equal(t, "[variable:1238]Variable 'tidb_slow_log_max_per_sec' is a GLOBAL variable", err.Error())
397+
tk.MustQuery(`select @@Global.tidb_slow_log_max_per_sec`).Check(
398+
testkit.Rows("0"),
399+
)
400+
401+
// test errors
402+
_, err = tk.Exec(`set session tidb_slow_log_max_per_sec="0"`)
403+
require.Equal(t, "[variable:1229]Variable 'tidb_slow_log_max_per_sec' is a GLOBAL variable and should be set with SET GLOBAL", err.Error())
404+
_, err = tk.Exec(`set global tidb_slow_log_max_per_sec=""`)
405+
require.Equal(t, "[variable:1232]Incorrect argument type to variable 'tidb_slow_log_max_per_sec'", err.Error())
406+
_, err = tk.Exec(`set global tidb_slow_log_max_per_sec="1.23"`)
407+
require.Equal(t, "[variable:1232]Incorrect argument type to variable 'tidb_slow_log_max_per_sec'", err.Error())
408+
409+
// test warnings
410+
_, err = tk.Exec(`set global tidb_slow_log_max_per_sec="-1"`)
411+
tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_slow_log_max_per_sec value: '-1'"))
412+
tk.MustQuery(`select @@Global.tidb_slow_log_max_per_sec`).Check(
413+
testkit.Rows("0"),
414+
)
415+
tk.MustExec(`set global tidb_slow_log_max_per_sec="1234567"`)
416+
tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_slow_log_max_per_sec value: '1234567'"))
417+
tk.MustQuery(`show variables like "tidb_slow_log_max_per_sec"`).Check(
418+
testkit.Rows("tidb_slow_log_max_per_sec 1000000"),
419+
)
420+
421+
// normal
422+
tk.MustExec(`set global tidb_slow_log_max_per_sec="2"`)
423+
require.True(t, vardef.GlobalSlowLogRateLimiter.Allow())
424+
require.True(t, vardef.GlobalSlowLogRateLimiter.Allow())
425+
require.False(t, vardef.GlobalSlowLogRateLimiter.Allow())
426+
tk.MustQuery(`show variables like "tidb_slow_log_max_per_sec"`).Check(
427+
testkit.Rows("tidb_slow_log_max_per_sec 2"),
428+
)
429+
tk.MustQuery(`select @@Global.tidb_slow_log_max_per_sec`).Check(
430+
testkit.Rows("2"),
431+
)
432+
// no limit
433+
tk.MustExec(`set global tidb_slow_log_max_per_sec="0"`)
434+
require.True(t, vardef.GlobalSlowLogRateLimiter.Allow())
435+
require.True(t, vardef.GlobalSlowLogRateLimiter.Allow())
436+
require.True(t, vardef.GlobalSlowLogRateLimiter.Allow())
437+
tk.MustQuery(`show variables like "tidb_slow_log_max_per_sec"`).Check(
438+
testkit.Rows("tidb_slow_log_max_per_sec 0"),
439+
)
440+
}
441+
387442
func BenchmarkCheckSlowThreshold(b *testing.B) {
388443
b.StopTimer()
389444
b.ReportAllocs()

pkg/sessionctx/vardef/BUILD.bazel

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ go_library(
1919
"//pkg/util/paging",
2020
"//pkg/util/size",
2121
"@com_github_pingcap_tipb//go-tipb",
22+
"@org_golang_x_time//rate",
2223
"@org_uber_go_atomic//:atomic",
2324
],
2425
)
@@ -36,5 +37,6 @@ go_test(
3637
deps = [
3738
"//pkg/config/kerneltype",
3839
"@com_github_stretchr_testify//require",
40+
"@org_golang_x_time//rate",
3941
],
4042
)

pkg/sessionctx/vardef/tidb_vars.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/pingcap/tidb/pkg/util/size"
3333
"github.com/pingcap/tipb/go-tipb"
3434
"go.uber.org/atomic"
35+
"golang.org/x/time/rate"
3536
)
3637

3738
/*
@@ -234,6 +235,10 @@ const (
234235
// TiDBSlowLogRules defines multi-dimensional trigger rules for flexible slow log control.
235236
TiDBSlowLogRules = "tidb_slow_log_rules"
236237

238+
// TiDBSlowLogMaxPerSec is the maximum number of slow logs that can be recorded per second in the server.
239+
// The default value is 0, which means no rate limiting is applied.
240+
TiDBSlowLogMaxPerSec = "tidb_slow_log_max_per_sec"
241+
237242
// TiDBSlowTxnLogThreshold is used to set the slow transaction log threshold in the server.
238243
TiDBSlowTxnLogThreshold = "tidb_slow_txn_log_threshold"
239244

@@ -1759,19 +1764,20 @@ var (
17591764
// the value of `tidb_analyze_column_options` determines the behavior of the analyze operation.
17601765
// 2. If `tidb_persist_analyze_options` is disabled, `tidb_analyze_column_options` is used directly to decide
17611766
// whether to analyze all columns or just the predicate columns.
1762-
AnalyzeColumnOptions = atomic.NewString(DefTiDBAnalyzeColumnOptions)
1763-
GlobalLogMaxDays = atomic.NewInt32(int32(config.GetGlobalConfig().Log.File.MaxDays))
1764-
QueryLogMaxLen = atomic.NewInt32(DefTiDBQueryLogMaxLen)
1765-
EnablePProfSQLCPU = atomic.NewBool(false)
1766-
EnableBatchDML = atomic.NewBool(false)
1767-
EnableTmpStorageOnOOM = atomic.NewBool(DefTiDBEnableTmpStorageOnOOM)
1768-
DDLReorgWorkerCounter int32 = DefTiDBDDLReorgWorkerCount
1769-
DDLReorgBatchSize int32 = DefTiDBDDLReorgBatchSize
1770-
DDLFlashbackConcurrency int32 = DefTiDBDDLFlashbackConcurrency
1771-
DDLErrorCountLimit int64 = DefTiDBDDLErrorCountLimit
1772-
DDLReorgRowFormat int64 = DefTiDBRowFormatV2
1773-
DDLReorgMaxWriteSpeed = atomic.NewInt64(DefTiDBDDLReorgMaxWriteSpeed)
1774-
MaxDeltaSchemaCount int64 = DefTiDBMaxDeltaSchemaCount
1767+
AnalyzeColumnOptions = atomic.NewString(DefTiDBAnalyzeColumnOptions)
1768+
GlobalLogMaxDays = atomic.NewInt32(int32(config.GetGlobalConfig().Log.File.MaxDays))
1769+
QueryLogMaxLen = atomic.NewInt32(DefTiDBQueryLogMaxLen)
1770+
EnablePProfSQLCPU = atomic.NewBool(false)
1771+
EnableBatchDML = atomic.NewBool(false)
1772+
EnableTmpStorageOnOOM = atomic.NewBool(DefTiDBEnableTmpStorageOnOOM)
1773+
DDLReorgWorkerCounter int32 = DefTiDBDDLReorgWorkerCount
1774+
DDLReorgBatchSize int32 = DefTiDBDDLReorgBatchSize
1775+
DDLFlashbackConcurrency int32 = DefTiDBDDLFlashbackConcurrency
1776+
DDLErrorCountLimit int64 = DefTiDBDDLErrorCountLimit
1777+
DDLReorgRowFormat int64 = DefTiDBRowFormatV2
1778+
DDLReorgMaxWriteSpeed = atomic.NewInt64(DefTiDBDDLReorgMaxWriteSpeed)
1779+
MaxDeltaSchemaCount int64 = DefTiDBMaxDeltaSchemaCount
1780+
GlobalSlowLogRateLimiter = rate.NewLimiter(rate.Inf, 1)
17751781
// DDLSlowOprThreshold is the threshold for ddl slow operations, uint is millisecond.
17761782
DDLSlowOprThreshold = config.GetGlobalConfig().Instance.DDLSlowOprThreshold
17771783
GlobalSlowLogRules = atomic.NewPointer[slowlogrule.GlobalSlowLogRules](

pkg/sessionctx/vardef/tidb_vars_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515
package vardef
1616

1717
import (
18+
"sync"
1819
"testing"
1920

2021
"github.com/pingcap/tidb/pkg/config/kerneltype"
2122
"github.com/stretchr/testify/require"
23+
"golang.org/x/time/rate"
2224
)
2325

2426
func TestIsMDLEnabledInNextGen(t *testing.T) {
@@ -36,3 +38,48 @@ func TestIsMDLEnabledInNextGen(t *testing.T) {
3638
SetEnableMDL(true)
3739
require.True(t, IsMDLEnabled())
3840
}
41+
42+
func runConcurrentTest(b *testing.B, limiter interface {
43+
Allow() bool
44+
}, goroutines int) {
45+
var wg sync.WaitGroup
46+
startCh := make(chan struct{})
47+
48+
cnt := b.N / goroutines
49+
for g := 0; g < goroutines; g++ {
50+
wg.Add(1)
51+
go func() {
52+
defer wg.Done()
53+
54+
<-startCh
55+
for i := 0; i < cnt; i++ {
56+
limiter.Allow()
57+
}
58+
}()
59+
}
60+
61+
b.ResetTimer()
62+
close(startCh)
63+
wg.Wait()
64+
b.StopTimer()
65+
}
66+
67+
const limit = 10000
68+
69+
func BenchmarkRateLimiterSimple(b *testing.B) {
70+
b.ReportAllocs()
71+
rl := rate.NewLimiter(rate.Limit(limit), limit)
72+
runConcurrentTest(b, rl, 1)
73+
}
74+
75+
func BenchmarkRateLimiterCurrency100(b *testing.B) {
76+
b.ReportAllocs()
77+
rl := rate.NewLimiter(rate.Limit(limit), limit)
78+
runConcurrentTest(b, rl, 100)
79+
}
80+
81+
func BenchmarkRateLimiterCurrency1000(b *testing.B) {
82+
b.ReportAllocs()
83+
rl := rate.NewLimiter(rate.Limit(limit), limit)
84+
runConcurrentTest(b, rl, 1000)
85+
}

pkg/sessionctx/variable/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ go_library(
9090
"@com_github_tikv_client_go_v2//util",
9191
"@com_github_twmb_murmur3//:murmur3",
9292
"@org_golang_x_exp//maps",
93+
"@org_golang_x_time//rate",
9394
"@org_uber_go_atomic//:atomic",
9495
"@org_uber_go_zap//:zap",
9596
],

pkg/sessionctx/variable/sysvar.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import (
6666
"github.com/tikv/client-go/v2/oracle/oracles"
6767
tikvcliutil "github.com/tikv/client-go/v2/util"
6868
"go.uber.org/zap"
69+
"golang.org/x/time/rate"
6970
)
7071

7172
type concurrencySetter func(s *SessionVars, v int)
@@ -3804,6 +3805,30 @@ var defaultSysVars = []*SysVar{
38043805
return vardef.GlobalSlowLogRules.Load().RawRules, nil
38053806
},
38063807
},
3808+
{
3809+
Scope: vardef.ScopeGlobal,
3810+
Name: vardef.TiDBSlowLogMaxPerSec,
3811+
Value: "0",
3812+
Type: vardef.TypeInt,
3813+
MinValue: 0, MaxValue: 1000000,
3814+
SetGlobal: func(_ context.Context, sv *SessionVars, s string) error {
3815+
d := TidbOptInt(s, 0)
3816+
if d == int(vardef.GlobalSlowLogRateLimiter.Limit()) {
3817+
return nil
3818+
}
3819+
3820+
if d == 0 {
3821+
vardef.GlobalSlowLogRateLimiter.SetLimit(rate.Inf)
3822+
return nil
3823+
}
3824+
vardef.GlobalSlowLogRateLimiter.SetLimit(rate.Limit(d))
3825+
vardef.GlobalSlowLogRateLimiter.SetBurst(d)
3826+
return nil
3827+
},
3828+
GetGlobal: func(ctx context.Context, sv *SessionVars) (string, error) {
3829+
return strconv.Itoa(int(vardef.GlobalSlowLogRateLimiter.Limit())), nil
3830+
},
3831+
},
38073832
}
38083833

38093834
// GlobalSystemVariableInitialValue gets the default value for a system variable including ones that are dynamically set (e.g. based on the store)

0 commit comments

Comments
 (0)