Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion pkg/executor/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,9 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) {
}
sc.LastInsertIDSet = false
sc.PrevAffectedRows = 0
if vars.StmtCtx.InUpdateStmt || vars.StmtCtx.InDeleteStmt || vars.StmtCtx.InInsertStmt || vars.StmtCtx.InSetSessionStatesStmt {
if vars.StmtCtx.InSetSessionStatesStmt {
sc.PrevAffectedRows = vars.StmtCtx.PrevAffectedRows
} else if vars.StmtCtx.InUpdateStmt || vars.StmtCtx.InDeleteStmt || vars.StmtCtx.InInsertStmt {
sc.PrevAffectedRows = int64(vars.StmtCtx.AffectedRows())
} else if vars.StmtCtx.InSelectStmt {
sc.PrevAffectedRows = -1
Expand Down
1 change: 1 addition & 0 deletions pkg/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ func RegisterMetrics() {
// StmtSummary
prometheus.MustRegister(StmtSummaryWindowRecordCount)
prometheus.MustRegister(StmtSummaryWindowEvictedCount)
prometheus.MustRegister(StmtSummaryEvictedLogCounter)
}

// Register registers custom collectors.
Expand Down
14 changes: 14 additions & 0 deletions pkg/metrics/metrics_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ func readGaugeValue(t *testing.T, gauge prometheus.Gauge) float64 {
return m.GetGauge().GetValue()
}

func readCounterValue(t *testing.T, counter prometheus.Counter) float64 {
t.Helper()
m := &dto.Metric{}
require.NoError(t, counter.Write(m))
return m.GetCounter().GetValue()
}

func countCollectedMetrics(collector prometheus.Collector) int {
ch := make(chan prometheus.Metric, 16)
collector.Collect(ch)
Expand All @@ -51,6 +58,7 @@ func TestStmtSummaryMetricLabels(t *testing.T) {
InitStmtSummaryMetrics()
require.Equal(t, 0, countCollectedMetrics(StmtSummaryWindowRecordCount))
require.Equal(t, 0, countCollectedMetrics(StmtSummaryWindowEvictedCount))
require.Equal(t, 0, countCollectedMetrics(StmtSummaryEvictedLogCounter))

SetStmtSummaryWindowMetrics(StmtSummaryTypeV1, 3, 1)
require.Equal(t, 1, countCollectedMetrics(StmtSummaryWindowRecordCount))
Expand All @@ -63,4 +71,10 @@ func TestStmtSummaryMetricLabels(t *testing.T) {
require.Equal(t, 2, countCollectedMetrics(StmtSummaryWindowEvictedCount))
require.Equal(t, 5.0, readGaugeValue(t, StmtSummaryWindowRecordCount.WithLabelValues(StmtSummaryTypeV2)))
require.Equal(t, 2.0, readGaugeValue(t, StmtSummaryWindowEvictedCount.WithLabelValues(StmtSummaryTypeV2)))

StmtSummaryEvictedLogCounter.WithLabelValues(StmtSummaryTypeV2, StmtSummaryEvictedLogResultPersisted).Add(3)
StmtSummaryEvictedLogCounter.WithLabelValues(StmtSummaryTypeV2, StmtSummaryEvictedLogResultDropped).Inc()
require.Equal(t, 2, countCollectedMetrics(StmtSummaryEvictedLogCounter))
require.Equal(t, 3.0, readCounterValue(t, StmtSummaryEvictedLogCounter.WithLabelValues(StmtSummaryTypeV2, StmtSummaryEvictedLogResultPersisted)))
require.Equal(t, 1.0, readCounterValue(t, StmtSummaryEvictedLogCounter.WithLabelValues(StmtSummaryTypeV2, StmtSummaryEvictedLogResultDropped)))
}
16 changes: 16 additions & 0 deletions pkg/metrics/stmtsummary.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const (
StmtSummaryTypeV1 = "v1"
// StmtSummaryTypeV2 marks metrics reported by the persistent statement summary implementation.
StmtSummaryTypeV2 = "v2"

// StmtSummaryEvictedLogResultPersisted marks evicted records submitted to the stmt log.
StmtSummaryEvictedLogResultPersisted = "persisted"
// StmtSummaryEvictedLogResultDropped marks evicted records dropped before reaching the stmt log.
StmtSummaryEvictedLogResultDropped = "dropped"
)

var (
Expand All @@ -39,6 +44,9 @@ var (
// This value resets to 0 when the window rotates.
StmtSummaryWindowEvictedCount *prometheus.GaugeVec

// StmtSummaryEvictedLogCounter counts v2 evicted-log persistence outcomes.
StmtSummaryEvictedLogCounter *prometheus.CounterVec

stmtSummaryWindowRecordCountV1 prometheus.Gauge
stmtSummaryWindowRecordCountV2 prometheus.Gauge
stmtSummaryWindowEvictedCountV1 prometheus.Gauge
Expand All @@ -65,6 +73,14 @@ func InitStmtSummaryMetrics() {
Help: "The number of LRU evictions in the current statement summary window.",
}, []string{LblType})

StmtSummaryEvictedLogCounter = metricscommon.NewCounterVec(
prometheus.CounterOpts{
Namespace: "tidb",
Subsystem: "stmt_summary",
Name: "evicted_log_total",
Help: "The number of v2 statement summary evicted-log records by result.",
}, []string{LblType, LblResult})

stmtSummaryWindowMetricsMu.Lock()
stmtSummaryWindowRecordCountV1 = nil
stmtSummaryWindowRecordCountV2 = nil
Expand Down
1 change: 1 addition & 0 deletions pkg/sessionctx/sessionstates/session_states_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ func TestStatementCtx(t *testing.T) {
return nil
},
checkFunc: func(tk *testkit.TestKit, param any) {
require.Equal(t, uint64(0), tk.Session().AffectedRows())
tk.MustQuery("select row_count()").Check(testkit.Rows("-1"))
},
},
Expand Down
12 changes: 12 additions & 0 deletions pkg/sessionctx/vardef/tidb_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,16 @@ const (
// TiDBStmtSummaryMaxSQLLength indicates the max length of displayed normalized sql and sample sql.
TiDBStmtSummaryMaxSQLLength = "tidb_stmt_summary_max_sql_length"

// TiDBStmtSummaryPersistEvicted controls whether per-record LRU evictions
// in the v2 (persistent) statement summary are persisted to the stmt log.
// Off by default because it adds log volume proportional to eviction rate.
TiDBStmtSummaryPersistEvicted = "tidb_stmt_summary_persist_evicted"

// TiDBStmtSummaryGroupByUser, when enabled, adds the executing user to the
// statement summary grouping key so the same digest run by different users
// produces separate rows. Off by default to avoid cardinality growth.
TiDBStmtSummaryGroupByUser = "tidb_stmt_summary_group_by_user"

// TiDBIgnoreInlistPlanDigest enables TiDB to generate the same plan digest with SQL using different in-list arguments.
TiDBIgnoreInlistPlanDigest = "tidb_ignore_inlist_plan_digest"

Expand Down Expand Up @@ -1598,6 +1608,8 @@ const (
DefTiDBStmtSummaryHistorySize = 24
DefTiDBStmtSummaryMaxStmtCount = 3000
DefTiDBStmtSummaryMaxSQLLength = 32768
DefTiDBStmtSummaryPersistEvicted = false
DefTiDBStmtSummaryGroupByUser = false
DefTiDBCapturePlanBaseline = Off
DefTiDBIgnoreInlistPlanDigest = true
DefTiDBEnableIndexMerge = true
Expand Down
2 changes: 1 addition & 1 deletion pkg/sessionctx/variable/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -3169,7 +3169,7 @@ func (s *SessionVars) DecodeSessionStates(_ context.Context, sessionStates *sess
s.HypoTiFlashReplicas = sessionStates.HypoTiFlashReplicas

// Decode StatementContext.
s.StmtCtx.SetAffectedRows(uint64(sessionStates.LastAffectedRows))
s.StmtCtx.PrevAffectedRows = sessionStates.LastAffectedRows
s.StmtCtx.PrevLastInsertID = sessionStates.LastInsertID
s.StmtCtx.SetWarnings(sessionStates.Warnings)
return
Expand Down
8 changes: 8 additions & 0 deletions pkg/sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,14 @@ var defaultSysVars = []*SysVar{
SetGlobal: func(_ context.Context, s *SessionVars, val string) error {
return stmtsummaryv2.SetMaxSQLLength(TidbOptInt(val, vardef.DefTiDBStmtSummaryMaxSQLLength))
}},
{Scope: vardef.ScopeGlobal, Name: vardef.TiDBStmtSummaryPersistEvicted, Value: BoolToOnOff(vardef.DefTiDBStmtSummaryPersistEvicted), Type: vardef.TypeBool, AllowEmpty: true,
SetGlobal: func(_ context.Context, s *SessionVars, val string) error {
return stmtsummaryv2.SetPersistEvicted(TiDBOptOn(val))
}},
{Scope: vardef.ScopeGlobal, Name: vardef.TiDBStmtSummaryGroupByUser, Value: BoolToOnOff(vardef.DefTiDBStmtSummaryGroupByUser), Type: vardef.TypeBool, AllowEmpty: true,
SetGlobal: func(_ context.Context, s *SessionVars, val string) error {
return stmtsummaryv2.SetGroupByUser(TiDBOptOn(val))
}},
{Scope: vardef.ScopeGlobal, Name: vardef.TiDBCapturePlanBaseline, Value: vardef.DefTiDBCapturePlanBaseline, Type: vardef.TypeBool, AllowEmptyAll: true},
{Scope: vardef.ScopeGlobal, Name: vardef.TiDBEvolvePlanTaskMaxTime, Value: strconv.Itoa(vardef.DefTiDBEvolvePlanTaskMaxTime), Type: vardef.TypeInt, MinValue: -1, MaxValue: math.MaxInt64},
{Scope: vardef.ScopeGlobal, Name: vardef.TiDBEvolvePlanTaskStartTime, Value: vardef.DefTiDBEvolvePlanTaskStartTime, Type: vardef.TypeTime},
Expand Down
2 changes: 1 addition & 1 deletion pkg/util/stmtsummary/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ go_test(
],
embed = [":stmtsummary"],
flaky = True,
shard_count = 26,
shard_count = 28,
deps = [
"//pkg/meta/model",
"//pkg/metrics",
Expand Down
4 changes: 2 additions & 2 deletions pkg/util/stmtsummary/evicted_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func newInduceSsbde(beginTime int64, endTime int64) *stmtSummaryByDigestElement
// generate new StmtDigestKey and stmtSummaryByDigest
func generateStmtSummaryByDigestKeyValue(schema string, beginTime int64, endTime int64) (*StmtDigestKey, *stmtSummaryByDigest) {
key := &StmtDigestKey{}
key.Init(schema, "", "", "", "")
key.Init(schema, "", "", "", "", "")
value := newInduceSsbd(beginTime, endTime)
return key, value
}
Expand Down Expand Up @@ -191,7 +191,7 @@ func TestSimpleStmtSummaryByDigestEvicted(t *testing.T) {
require.Equal(t, "{begin: 8, end: 9, count: 1}, {begin: 5, end: 6, count: 1}, {begin: 2, end: 3, count: 1}", getAllEvicted(ssbde))

evictedKey = &StmtDigestKey{}
evictedKey.Init("b", "", "", "", "")
evictedKey.Init("b", "", "", "", "", "")
ssbde.AddEvicted(evictedKey, evictedValue, 4)
require.Equal(t, "{begin: 8, end: 9, count: 2}, {begin: 5, end: 6, count: 2}, {begin: 2, end: 3, count: 2}, {begin: 1, end: 2, count: 1}", getAllEvicted(ssbde))

Expand Down
59 changes: 55 additions & 4 deletions pkg/util/stmtsummary/statement_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"bytes"
"cmp"
"container/list"
"encoding/binary"
"fmt"
"math"
"slices"
Expand Down Expand Up @@ -53,8 +54,16 @@ type StmtDigestKey struct {
}

// Init initialize the hash key.
func (key *StmtDigestKey) Init(schemaName, digest, prevDigest, planDigest, resourceGroupName string) {
length := len(schemaName) + len(digest) + len(prevDigest) + len(planDigest) + len(resourceGroupName)
// When user is empty (group_by_user disabled), the hash is byte-identical to
// the pre-user-dimension layout. When user is non-empty, the hash appends a
// length-prefixed user segment after resourceGroupName so the boundary is
// unambiguous and pairs like ("rg", "alice") and ("rga", "lice") cannot
// collide.
func (key *StmtDigestKey) Init(schemaName, digest, prevDigest, planDigest, resourceGroupName, user string) {
length := len(schemaName) + len(digest) + len(prevDigest) + len(planDigest) + len(resourceGroupName) + len(user)
if len(user) > 0 {
length += 4
}
if cap(key.hash) < length {
key.hash = make([]byte, 0, length)
} else {
Expand All @@ -65,6 +74,12 @@ func (key *StmtDigestKey) Init(schemaName, digest, prevDigest, planDigest, resou
key.hash = append(key.hash, hack.Slice(prevDigest)...)
key.hash = append(key.hash, hack.Slice(planDigest)...)
key.hash = append(key.hash, hack.Slice(resourceGroupName)...)
if len(user) > 0 {
var buf [4]byte
binary.BigEndian.PutUint32(buf[:], uint32(len(user)))
key.hash = append(key.hash, buf[:]...)
key.hash = append(key.hash, hack.Slice(user)...)
}
}

// Hash implements SimpleLRUCache.Key.
Expand All @@ -90,6 +105,7 @@ type stmtSummaryByDigestMap struct {
optRefreshInterval *atomic2.Int64
optHistorySize *atomic2.Int32
optMaxSQLLength *atomic2.Int32
optGroupByUser *atomic2.Bool

// other stores summary of evicted data.
other *stmtSummaryByDigestEvicted
Expand Down Expand Up @@ -324,6 +340,7 @@ func newStmtSummaryByDigestMap() *stmtSummaryByDigestMap {
optRefreshInterval: atomic2.NewInt64(1800),
optHistorySize: atomic2.NewInt32(24),
optMaxSQLLength: atomic2.NewInt32(32768),
optGroupByUser: atomic2.NewBool(false),
other: ssbde,
}
newSsMap.summaryMap.SetOnEvict(func(k kvcache.Key, v kvcache.Value) {
Expand Down Expand Up @@ -357,8 +374,6 @@ func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) {
}

key := StmtDigestKeyPool.Get().(*StmtDigestKey)
// Init hash value in advance, to reduce the time holding the lock.
key.Init(sei.SchemaName, sei.Digest, sei.PrevSQLDigest, sei.PlanDigest, sei.ResourceGroupName)

var exist bool

Expand All @@ -372,6 +387,15 @@ func (ssMap *stmtSummaryByDigestMap) AddStatement(sei *StmtExecInfo) {
ssMap.Lock()
defer ssMap.Unlock()

// Decide userForKey under the lock so SetGroupByUser's flag flip + Clear
// is atomic w.r.t. AddStatement; otherwise a post-clear insert could land
// under the wrong grouping mode.
userForKey := ""
if ssMap.optGroupByUser.Load() {
userForKey = sei.User
}
key.Init(sei.SchemaName, sei.Digest, sei.PrevSQLDigest, sei.PlanDigest, sei.ResourceGroupName, userForKey)

// Check again. Statements could be added before disabling the flag and after Clear().
if !ssMap.Enabled() {
return
Expand Down Expand Up @@ -413,6 +437,11 @@ func (ssMap *stmtSummaryByDigestMap) Clear() {
ssMap.Lock()
defer ssMap.Unlock()

ssMap.clearLocked()
}

// clearLocked removes all statement summaries. ssMap.Lock must be held.
func (ssMap *stmtSummaryByDigestMap) clearLocked() {
ssMap.summaryMap.DeleteAll()
ssMap.other.Clear()
ssMap.beginTimeForCurInterval = 0
Expand Down Expand Up @@ -520,6 +549,28 @@ func (ssMap *stmtSummaryByDigestMap) historySize() int {
return int(ssMap.optHistorySize.Load())
}

// SetGroupByUser enables or disables grouping statement summaries by the
// executing user. Switching the flag clears existing data because existing
// rows were aggregated under a different grouping key.
func (ssMap *stmtSummaryByDigestMap) SetGroupByUser(value bool) error {
// Hold ssMap.Lock across the flag flip and clear so AddStatement (which
// reads the flag under the same lock) cannot insert a record with the
// old grouping mode after Clear() completes.
ssMap.Lock()
defer ssMap.Unlock()
if ssMap.optGroupByUser.Load() == value {
return nil
}
ssMap.optGroupByUser.Store(value)
ssMap.clearLocked()
return nil
}

// GroupByUser reports whether statement summaries are grouped by user.
func (ssMap *stmtSummaryByDigestMap) GroupByUser() bool {
return ssMap.optGroupByUser.Load()
}

// SetHistorySize sets the history size for all summaries.
func (ssMap *stmtSummaryByDigestMap) SetMaxStmtCount(value uint) error {
// `optMaxStmtCount` and `ssMap` don't need to be strictly atomically updated.
Expand Down
Loading