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
13 changes: 11 additions & 2 deletions sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,12 +491,21 @@ 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()
return sysVars[name]
if sysVars[name] == nil {
return nil
}
return sysVars[name].Clone()
}

// SetSysVar sets a sysvar. This will not propagate to the cluster, so it should only be
Expand All @@ -514,7 +523,7 @@ func GetSysVars() map[string]*SysVar {
defer sysVarsLock.RUnlock()
copy := make(map[string]*SysVar, len(sysVars))
for name, sv := range sysVars {
copy[name] = sv
copy[name] = sv.Clone()
}
return copy
}
Expand Down
27 changes: 27 additions & 0 deletions sessionctx/variable/sysvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,3 +555,30 @@ func (*testSysVarSuite) TestInstanceScopedVars(c *C) {
c.Assert(err, IsNil)
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")
}