From 113612699103a4d4cd5b1622c666a608f5fc63c0 Mon Sep 17 00:00:00 2001 From: Morgan Tocker Date: Wed, 19 May 2021 09:55:42 -0600 Subject: [PATCH] variable: fix sysvar datarace with deep copy (#24732) --- sessionctx/variable/sysvar.go | 13 +++++++++++-- sessionctx/variable/sysvar_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/sessionctx/variable/sysvar.go b/sessionctx/variable/sysvar.go index 99ff4a09f7af6..1df9e3c0f582c 100644 --- a/sessionctx/variable/sysvar.go +++ b/sessionctx/variable/sysvar.go @@ -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 @@ -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 } diff --git a/sessionctx/variable/sysvar_test.go b/sessionctx/variable/sysvar_test.go index 71979a57b7eef..d236dc142f1dd 100644 --- a/sessionctx/variable/sysvar_test.go +++ b/sessionctx/variable/sysvar_test.go @@ -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") +}