Skip to content

Commit

Permalink
variable: fix sysvar datarace with deep copy (#24732)
Browse files Browse the repository at this point in the history
  • Loading branch information
morgo authored May 19, 2021
1 parent 89cf970 commit 1136126
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 2 deletions.
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")
}

0 comments on commit 1136126

Please sign in to comment.