Skip to content

Commit

Permalink
sessionctx/variable: avoid SysVar clone every time when visiting syst…
Browse files Browse the repository at this point in the history
…em variable (pingcap#26308)
  • Loading branch information
tiancaiamao authored Jul 19, 2021
1 parent 350cbd1 commit a542c58
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 43 deletions.
28 changes: 12 additions & 16 deletions sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ const (
)

// SysVar is for system variable.
// All the fields of SysVar should be READ ONLY after created.
type SysVar struct {
// Scope is for whether can be changed or not
Scope ScopeFlag
Expand Down Expand Up @@ -556,30 +557,24 @@ func UnregisterSysVar(name string) {
sysVarsLock.Unlock()
}

// Clone deep copies the sysvar struct to avoid a race
func (sv *SysVar) Clone() *SysVar {
dst := *sv
return &dst
}

// GetSysVar returns sys var info for name as key.
func GetSysVar(name string) *SysVar {
name = strings.ToLower(name)
sysVarsLock.RLock()
defer sysVarsLock.RUnlock()
if sysVars[name] == nil {
return nil
}
return sysVars[name].Clone()

return sysVars[name]
}

// SetSysVar sets a sysvar. This will not propagate to the cluster, so it should only be
// SetSysVar sets a sysvar. In fact, SysVar is immutable.
// SetSysVar is implemented by register a new SysVar with the same name again.
// This will not propagate to the cluster, so it should only be
// used for instance scoped AUTO variables such as system_time_zone.
func SetSysVar(name string, value string) {
name = strings.ToLower(name)
sysVarsLock.Lock()
defer sysVarsLock.Unlock()
sysVars[name].Value = value
old := GetSysVar(name)
tmp := *old
tmp.Value = value
RegisterSysVar(&tmp)
}

// GetSysVars deep copies the sysVars list under a RWLock
Expand All @@ -588,7 +583,8 @@ func GetSysVars() map[string]*SysVar {
defer sysVarsLock.RUnlock()
copy := make(map[string]*SysVar, len(sysVars))
for name, sv := range sysVars {
copy[name] = sv.Clone()
tmp := *sv
copy[name] = &tmp
}
return copy
}
Expand Down
27 changes: 0 additions & 27 deletions sessionctx/variable/sysvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,33 +571,6 @@ func (*testSysVarSuite) TestInstanceScopedVars(c *C) {
c.Assert(val, Equals, vars.TxnScope.GetVarValue())
}

// Calling GetSysVars/GetSysVar needs to return a deep copy, otherwise there will be data races.
// This is a bit unfortunate, since the only time the race occurs is in the testsuite (Enabling/Disabling SEM) and
// during startup (setting the .Value of ScopeNone variables). In future it might also be able
// to fix this by delaying the LoadSysVarCacheLoop start time until after the server is fully initialized.
func (*testSysVarSuite) TestDeepCopyGetSysVars(c *C) {
// Check GetSysVar
sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "datarace", Value: On, Type: TypeBool}
RegisterSysVar(&sv)
svcopy := GetSysVar("datarace")
svcopy.Name = "datarace2"
c.Assert(sv.Name, Equals, "datarace")
c.Assert(GetSysVar("datarace").Name, Equals, "datarace")
UnregisterSysVar("datarace")

// Check GetSysVars
sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "datarace", Value: On, Type: TypeBool}
RegisterSysVar(&sv)
for name, svcopy := range GetSysVars() {
if name == "datarace" {
svcopy.Name = "datarace2"
}
}
c.Assert(sv.Name, Equals, "datarace")
c.Assert(GetSysVar("datarace").Name, Equals, "datarace")
UnregisterSysVar("datarace")
}

// Test that sysvars defaults are logically valid. i.e.
// the default itself must validate without error provided the scope and read-only is correct.
// The default values should also be normalized for consistency.
Expand Down

0 comments on commit a542c58

Please sign in to comment.