Skip to content

Commit 470b303

Browse files
[release-24.0] vtorc: fix data race in forgetAliases cache initialization (#19843) (#19847)
Signed-off-by: Arthur Schreiber <arthur@planetscale.com> Co-authored-by: vitess-bot[bot] <108069721+vitess-bot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d2273cc commit 470b303

2 files changed

Lines changed: 20 additions & 24 deletions

File tree

go/vt/vtorc/inst/instance_dao.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import (
2626
"strconv"
2727
"strings"
2828
"sync"
29-
"sync/atomic"
3029
"time"
3130

3231
"github.com/patrickmn/go-cache"
@@ -54,31 +53,37 @@ var (
5453
instanceWriteSem = semaphore.NewWeighted(config.GetBackendWriteConcurrency())
5554
)
5655

57-
var forgetAliases *cache.Cache
56+
var (
57+
forgetAliases *cache.Cache
58+
forgetAliasesOnce sync.Once
59+
)
5860

5961
var (
6062
readTopologyInstanceCounter = stats.NewCounter("InstanceReadTopology", "Number of times an instance was read from the topology")
6163
readInstanceCounter = stats.NewCounter("InstanceRead", "Number of times an instance was read")
6264
currentErrantGTIDCount = stats.NewGaugesWithSingleLabel("CurrentErrantGTIDCount", "Number of errant GTIDs a vttablet currently has", "TabletAlias")
6365
)
6466

65-
var (
66-
emptyQuotesRegexp = regexp.MustCompile(`^""$`)
67-
cacheInitializationCompleted atomic.Bool
68-
)
67+
var emptyQuotesRegexp = regexp.MustCompile(`^""$`)
6968

7069
func init() {
7170
go initializeInstanceDao()
7271
}
7372

7473
func initializeInstanceDao() {
7574
config.WaitForConfigurationToBeLoaded()
76-
InitializeForgetAliasesCache()
75+
initForgetAliasesCache()
76+
}
77+
78+
func initForgetAliasesCache() {
79+
forgetAliasesOnce.Do(func() {
80+
forgetAliases = cache.New(config.GetInstancePollTime()*3, time.Second)
81+
})
7782
}
7883

84+
// InitializeForgetAliasesCache ensures the forgetAliases cache is initialized.
7985
func InitializeForgetAliasesCache() {
80-
forgetAliases = cache.New(config.GetInstancePollTime()*3, time.Second)
81-
cacheInitializationCompleted.Store(true)
86+
initForgetAliasesCache()
8287
}
8388

8489
// ExecDBWriteFunc chooses how to execute a write onto the database: whether synchronously or not
@@ -1105,6 +1110,7 @@ func UpdateInstanceLastAttemptedCheck(tabletAlias *topodatapb.TabletAlias) error
11051110

11061111
// InstanceIsForgotten returns true if an instance was forgotten.
11071112
func InstanceIsForgotten(tabletAlias *topodatapb.TabletAlias) bool {
1113+
initForgetAliasesCache()
11081114
tabletAliasString := topoproto.TabletAliasString(tabletAlias)
11091115
_, found := forgetAliases.Get(tabletAliasString)
11101116
return found
@@ -1118,6 +1124,7 @@ func ForgetInstance(tabletAlias *topodatapb.TabletAlias) error {
11181124
log.Error(errMsg)
11191125
return errors.New(errMsg)
11201126
}
1127+
initForgetAliasesCache()
11211128
tabletAliasString := topoproto.TabletAliasString(tabletAlias)
11221129
forgetAliases.Set(tabletAliasString, true, cache.DefaultExpiration)
11231130
log.Info(fmt.Sprintf("Forgetting: %v", tabletAliasString))

go/vt/vtorc/inst/instance_dao_test.go

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,8 @@ func TestReadOutdatedInstances(t *testing.T) {
558558
},
559559
}
560560

561-
// wait for the forgetAliases cache to be initialized to prevent data race.
562-
waitForCacheInitialization()
561+
// Ensure the forgetAliases cache is initialized before overriding it.
562+
InitializeForgetAliasesCache()
563563

564564
// We are setting InstancePollSeconds to 59 minutes, just for the test.
565565
oldVal := config.GetInstancePollTime()
@@ -747,8 +747,8 @@ func TestForgetInstanceAndInstanceIsForgotten(t *testing.T) {
747747
},
748748
}
749749

750-
// wait for the forgetAliases cache to be initialized to prevent data race.
751-
waitForCacheInitialization()
750+
// Ensure the forgetAliases cache is initialized before overriding it.
751+
InitializeForgetAliasesCache()
752752

753753
oldCache := forgetAliases
754754
// Clear the database after the test. The easiest way to do that is to run all the initialization commands again.
@@ -810,17 +810,6 @@ func TestSnapshotTopologies(t *testing.T) {
810810
require.Equal(t, []string{"zone1-0000000100", "zone1-0000000101", "zone1-0000000112", "zone2-0000000200"}, tabletAliases)
811811
}
812812

813-
// waitForCacheInitialization waits for the cache to be initialized to prevent data race in tests
814-
// that alter the cache or depend on its behaviour.
815-
func waitForCacheInitialization() {
816-
for {
817-
if cacheInitializationCompleted.Load() {
818-
return
819-
}
820-
time.Sleep(100 * time.Millisecond)
821-
}
822-
}
823-
824813
func TestGetDatabaseState(t *testing.T) {
825814
// Clear the database after the test. The easiest way to do that is to run all the initialization commands again.
826815
defer func() {

0 commit comments

Comments
 (0)