Skip to content

Commit 7ec9ae7

Browse files
balance: fix potential data race when setting config by adding a lock (#808) (#809)
Co-authored-by: djshow832 <[email protected]>
1 parent c22b4db commit 7ec9ae7

File tree

2 files changed

+47
-1
lines changed

2 files changed

+47
-1
lines changed

pkg/balance/factor/factor_balance.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package factor
66
import (
77
"sort"
88
"strconv"
9+
"sync"
910
"time"
1011

1112
"github.com/pingcap/tiproxy/lib/config"
@@ -24,8 +25,8 @@ const (
2425
var _ policy.BalancePolicy = (*FactorBasedBalance)(nil)
2526

2627
// FactorBasedBalance is the default balance policy.
27-
// It's not concurrency-safe for now.
2828
type FactorBasedBalance struct {
29+
sync.Mutex
2930
factors []Factor
3031
// to reduce memory allocation
3132
cachedList []scoredBackend
@@ -53,6 +54,8 @@ func NewFactorBasedBalance(lg *zap.Logger, mr metricsreader.MetricsReader) *Fact
5354
// Init creates factors at the first time.
5455
// TODO: create factors according to config and update policy when config changes.
5556
func (fbb *FactorBasedBalance) Init(cfg *config.Config) {
57+
fbb.Lock()
58+
defer fbb.Unlock()
5659
fbb.factors = make([]Factor, 0, 7)
5760
fbb.setFactors(cfg)
5861
}
@@ -178,6 +181,8 @@ func (fbb *FactorBasedBalance) BackendToRoute(backends []policy.BackendCtx) poli
178181
return nil
179182
}
180183

184+
fbb.Lock()
185+
defer fbb.Unlock()
181186
scoredBackends := fbb.updateScore(backends)
182187
if !fbb.canBeRouted(scoredBackends[0].scoreBits) {
183188
return nil
@@ -240,6 +245,9 @@ func (fbb *FactorBasedBalance) BackendsToBalance(backends []policy.BackendCtx) (
240245
if len(backends) <= 1 {
241246
return
242247
}
248+
249+
fbb.Lock()
250+
defer fbb.Unlock()
243251
scoredBackends := fbb.updateScore(backends)
244252
if !fbb.canBeRouted(scoredBackends[0].scoreBits) {
245253
return
@@ -304,10 +312,14 @@ func (fbb *FactorBasedBalance) canBeRouted(score uint64) bool {
304312
}
305313

306314
func (fbb *FactorBasedBalance) SetConfig(cfg *config.Config) {
315+
fbb.Lock()
316+
defer fbb.Unlock()
307317
fbb.setFactors(cfg)
308318
}
309319

310320
func (fbb *FactorBasedBalance) Close() {
321+
fbb.Lock()
322+
defer fbb.Unlock()
311323
for _, factor := range fbb.factors {
312324
factor.Close()
313325
}

pkg/balance/factor/factor_balance_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@
44
package factor
55

66
import (
7+
"context"
78
"slices"
89
"strconv"
910
"testing"
11+
"time"
1012

1113
"github.com/pingcap/tiproxy/lib/config"
1214
"github.com/pingcap/tiproxy/lib/util/logger"
15+
"github.com/pingcap/tiproxy/lib/util/waitgroup"
1316
"github.com/pingcap/tiproxy/pkg/balance/policy"
1417
"github.com/stretchr/testify/require"
1518
"go.uber.org/zap"
@@ -471,3 +474,34 @@ func TestCanBeRouted(t *testing.T) {
471474
require.Equal(t, test.routed, b != nil, "test index %d", tIdx)
472475
}
473476
}
477+
478+
func TestSetFactorConcurrently(t *testing.T) {
479+
fbb := NewFactorBasedBalance(zap.NewNop(), newMockMetricsReader())
480+
var wg waitgroup.WaitGroup
481+
cfg := &config.Config{}
482+
fbb.Init(cfg)
483+
wg.Add(10)
484+
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
485+
wg.Run(func() {
486+
defer wg.Done()
487+
policies := []string{config.BalancePolicyConnection, config.BalancePolicyResource, config.BalancePolicyLocation}
488+
for i := 0; ctx.Err() != nil; i++ {
489+
cfg.Balance = config.Balance{
490+
Policy: policies[i%len(policies)],
491+
}
492+
fbb.SetConfig(cfg)
493+
}
494+
})
495+
for i := 0; i < 9; i++ {
496+
wg.Run(func() {
497+
defer wg.Done()
498+
for ctx.Err() != nil {
499+
backends := createBackends(5)
500+
fbb.BackendToRoute(backends)
501+
}
502+
})
503+
}
504+
wg.Wait()
505+
cancel()
506+
fbb.Close()
507+
}

0 commit comments

Comments
 (0)