session, sessionctx: consider the dependency of sysvars for session states (#46244)

close pingcap/tidb#46214
This commit is contained in:
djshow832
2023-08-22 16:30:34 +08:00
committed by GitHub
parent a8cfe88aba
commit dd593b2e03
5 changed files with 77 additions and 5 deletions

View File

@ -4315,7 +4315,10 @@ func (s *session) DecodeSessionStates(ctx context.Context,
}
// Decode session variables.
for name, val := range sessionStates.SystemVars {
names := variable.OrderByDependency(sessionStates.SystemVars)
// Some variables must be set before others, e.g. tidb_enable_noop_functions should be before noop variables.
for _, name := range names {
val := sessionStates.SystemVars[name]
// Experimental system variables may change scope, data types, or even be removed.
// We just ignore the errors and continue.
if err := s.sessionVars.SetSystemVar(name, val); err != nil {

View File

@ -171,6 +171,28 @@ func TestSystemVars(t *testing.T) {
checkStmt: "select rand()",
expectedValue: "0.11641535266900002",
},
{
// tidb_enforce_mpp depends on tidb_allow_mpp.
stmts: []string{
"set @@global.tidb_allow_mpp=0",
"set @@tidb_allow_mpp=1",
"set @@tidb_enforce_mpp=1",
},
inSessionStates: true,
varName: variable.TiDBEnforceMPPExecution,
expectedValue: "1",
},
{
// tx_read_only depends on tidb_enable_noop_functions.
stmts: []string{
"set @@global.tidb_enable_noop_functions=0",
"set @@tidb_enable_noop_functions=1",
"set @@tx_read_only=1",
},
inSessionStates: true,
varName: variable.TxReadOnly,
expectedValue: "1",
},
}
if !sem.IsEnabled() {

View File

@ -678,7 +678,7 @@ var defaultSysVars = []*SysVar{
(*SetPDClientDynamicOption.Load())(TiDBEnableTSOFollowerProxy, val)
return nil
}},
{Scope: ScopeGlobal, Name: TiDBEnableLocalTxn, Value: BoolToOnOff(DefTiDBEnableLocalTxn), Hidden: true, Type: TypeBool, GetGlobal: func(_ context.Context, sv *SessionVars) (string, error) {
{Scope: ScopeGlobal, Name: TiDBEnableLocalTxn, Value: BoolToOnOff(DefTiDBEnableLocalTxn), Hidden: true, Type: TypeBool, Depended: true, GetGlobal: func(_ context.Context, sv *SessionVars) (string, error) {
return BoolToOnOff(EnableLocalTxn.Load()), nil
}, SetGlobal: func(_ context.Context, s *SessionVars, val string) error {
oldVal := EnableLocalTxn.Load()
@ -803,7 +803,7 @@ var defaultSysVars = []*SysVar{
return nil
}},
{Scope: ScopeGlobal, Name: TiDBEnableTelemetry, Value: BoolToOnOff(DefTiDBEnableTelemetry), Type: TypeBool},
{Scope: ScopeGlobal, Name: TiDBEnableHistoricalStats, Value: On, Type: TypeBool},
{Scope: ScopeGlobal, Name: TiDBEnableHistoricalStats, Value: On, Type: TypeBool, Depended: true},
/* tikv gc metrics */
{Scope: ScopeGlobal, Name: TiDBGCEnable, Value: On, Type: TypeBool, GetGlobal: func(_ context.Context, s *SessionVars) (string, error) {
return getTiDBTableValue(s, "tikv_gc_enable", On)
@ -1549,7 +1549,7 @@ var defaultSysVars = []*SysVar{
}},
{Scope: ScopeGlobal | ScopeSession, Name: BlockEncryptionMode, Value: "aes-128-ecb", Type: TypeEnum, PossibleValues: []string{"aes-128-ecb", "aes-192-ecb", "aes-256-ecb", "aes-128-cbc", "aes-192-cbc", "aes-256-cbc", "aes-128-ofb", "aes-192-ofb", "aes-256-ofb", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb"}},
/* TiDB specific variables */
{Scope: ScopeGlobal | ScopeSession, Name: TiDBAllowMPPExecution, Type: TypeBool, Value: BoolToOnOff(DefTiDBAllowMPPExecution), SetSession: func(s *SessionVars, val string) error {
{Scope: ScopeGlobal | ScopeSession, Name: TiDBAllowMPPExecution, Type: TypeBool, Value: BoolToOnOff(DefTiDBAllowMPPExecution), Depended: true, SetSession: func(s *SessionVars, val string) error {
s.allowMPPExecution = TiDBOptOn(val)
return nil
}},
@ -1893,7 +1893,7 @@ var defaultSysVars = []*SysVar{
s.TiDBOptJoinReorderThreshold = tidbOptPositiveInt32(val, DefTiDBOptJoinReorderThreshold)
return nil
}},
{Scope: ScopeGlobal | ScopeSession, Name: TiDBEnableNoopFuncs, Value: DefTiDBEnableNoopFuncs, Type: TypeEnum, PossibleValues: []string{Off, On, Warn}, Validation: func(vars *SessionVars, normalizedValue string, originalValue string, scope ScopeFlag) (string, error) {
{Scope: ScopeGlobal | ScopeSession, Name: TiDBEnableNoopFuncs, Value: DefTiDBEnableNoopFuncs, Type: TypeEnum, PossibleValues: []string{Off, On, Warn}, Depended: true, Validation: func(vars *SessionVars, normalizedValue string, originalValue string, scope ScopeFlag) (string, error) {
// The behavior is very weird if someone can turn TiDBEnableNoopFuncs OFF, but keep any of the following on:
// TxReadOnly, TransactionReadOnly, OfflineMode, SuperReadOnly, serverReadOnly, SQLAutoIsNull
// To prevent this strange position, prevent setting to OFF when any of these sysVars are ON of the same scope.

View File

@ -155,6 +155,10 @@ type SysVar struct {
// GetStateValue gets the value for session states, which is used for migrating sessions.
// We need a function to override GetSession sometimes, because GetSession may not return the real value.
GetStateValue func(*SessionVars) (string, bool, error)
// Depended indicates whether other variables depend on this one. That is, if this one is not correctly set,
// another variable cannot be set either.
// This flag is used to decide the order to replay session variables.
Depended bool
// skipInit defines if the sysvar should be loaded into the session on init.
// This is only important to set for sysvars that include session scope,
// since global scoped sysvars are not-applicable.
@ -614,6 +618,22 @@ func GetSysVars() map[string]*SysVar {
return m
}
// OrderByDependency orders the vars by dependency. The depended sys vars are in the front.
// Unknown sys vars are treated as not depended.
func OrderByDependency(names map[string]string) []string {
depended, notDepended := make([]string, 0, len(names)), make([]string, 0, len(names))
sysVarsLock.RLock()
defer sysVarsLock.RUnlock()
for name := range names {
if sv, ok := sysVars[name]; ok && sv.Depended {
depended = append(depended, name)
} else {
notDepended = append(notDepended, name)
}
}
return append(depended, notDepended...)
}
func init() {
sysVars = make(map[string]*SysVar)
for _, v := range defaultSysVars {

View File

@ -19,11 +19,13 @@ import (
"encoding/json"
"fmt"
"runtime"
"slices"
"strings"
"testing"
"time"
"github.com/pingcap/tidb/config"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/parser/mysql"
"github.com/pingcap/tidb/parser/terror"
"github.com/pingcap/tidb/types"
@ -687,3 +689,28 @@ func TestTimeValidationWithTimezone(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "23:59 +0800", val)
}
func TestOrderByDependency(t *testing.T) {
// Some other exceptions:
// - tidb_snapshot and tidb_read_staleness can not be set at the same time. It doesn't affect dependency.
vars := map[string]string{
"unknown": "1",
TxReadOnly: "1",
SQLAutoIsNull: "1",
TiDBEnableNoopFuncs: "1",
TiDBEnforceMPPExecution: "1",
TiDBAllowMPPExecution: "1",
TiDBTxnScope: kv.LocalTxnScope,
TiDBEnableLocalTxn: "1",
TiDBEnablePlanReplayerContinuousCapture: "1",
TiDBEnableHistoricalStats: "1",
}
names := OrderByDependency(vars)
require.Greater(t, slices.Index(names, TxReadOnly), slices.Index(names, TiDBEnableNoopFuncs))
require.Greater(t, slices.Index(names, SQLAutoIsNull), slices.Index(names, TiDBEnableNoopFuncs))
require.Greater(t, slices.Index(names, TiDBEnforceMPPExecution), slices.Index(names, TiDBAllowMPPExecution))
// Depended variables below are global variables, so actually it doesn't matter.
require.Greater(t, slices.Index(names, TiDBTxnScope), slices.Index(names, TiDBEnableLocalTxn))
require.Greater(t, slices.Index(names, TiDBEnablePlanReplayerContinuousCapture), slices.Index(names, TiDBEnableHistoricalStats))
require.Contains(t, names, "unknown")
}