diff --git a/go/vt/vtgateproxy/discovery.go b/go/vt/vtgateproxy/discovery.go index a4b48340bfd..ee5512830ea 100644 --- a/go/vt/vtgateproxy/discovery.go +++ b/go/vt/vtgateproxy/discovery.go @@ -82,7 +82,7 @@ type JSONGateResolverBuilder struct { targets map[string][]targetHost resolvers []*JSONGateResolver - rand *rand.Rand + sorter *shuffleSorter ticker *time.Ticker checksum []byte } @@ -114,6 +114,7 @@ func RegisterJSONGateResolver( poolTypeField: poolTypeField, affinityField: affinityField, affinityValue: affinityValue, + sorter: newShuffleSorter(), } resolver.Register(jsonDiscovery) @@ -133,9 +134,6 @@ func (*JSONGateResolverBuilder) Scheme() string { return "vtgate" } // Parse and validate the format of the file and start watching for changes func (b *JSONGateResolverBuilder) start() error { - - b.rand = rand.New(rand.NewSource(time.Now().UnixNano())) - // Perform the initial parse _, err := b.parse() if err != nil { @@ -288,11 +286,7 @@ func (b *JSONGateResolverBuilder) parse() (bool, error) { } for poolType := range targets { - if b.affinityField != "" { - sort.Slice(targets[poolType], func(i, j int) bool { - return b.affinityValue == targets[poolType][i].Affinity - }) - } + b.sorter.shuffleSort(targets[poolType], b.affinityField, b.affinityValue) if len(targets[poolType]) > *numConnections { targets[poolType] = targets[poolType][:*numConnections] } @@ -324,18 +318,37 @@ func (b *JSONGateResolverBuilder) GetTargets(poolType string) []targetHost { targets = append(targets, b.targets[poolType]...) b.mu.RUnlock() - // Shuffle to ensure every host has a different order to iterate through, putting - // the affinity matching (e.g. same az) hosts at the front and the non-matching ones - // at the end. - // - // Only need to do n-1 swaps since the last host is always in the right place. + b.sorter.shuffleSort(targets, b.affinityField, b.affinityValue) + + return targets +} + +type shuffleSorter struct { + rand *rand.Rand + mu *sync.Mutex +} + +func newShuffleSorter() *shuffleSorter { + return &shuffleSorter{ + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + mu: &sync.Mutex{}, + } +} + +// shuffleSort shuffles a slice of targetHost to ensure every host has a +// different order to iterate through, putting the affinity matching (e.g. same +// az) hosts at the front and the non-matching ones at the end. +func (s *shuffleSorter) shuffleSort(targets []targetHost, affinityField, affinityValue string) { n := len(targets) head := 0 + // Only need to do n-1 swaps since the last host is always in the right place. tail := n - 1 for i := 0; i < n-1; i++ { - j := head + b.rand.Intn(tail-head+1) + s.mu.Lock() + j := head + s.rand.Intn(tail-head+1) + s.mu.Unlock() - if *affinityField != "" && *affinityValue == targets[j].Affinity { + if affinityField != "" && affinityValue == targets[j].Affinity { targets[head], targets[j] = targets[j], targets[head] head++ } else { @@ -343,13 +356,10 @@ func (b *JSONGateResolverBuilder) GetTargets(poolType string) []targetHost { tail-- } } - - return targets } // Update the current list of hosts for the given resolver func (b *JSONGateResolverBuilder) update(r *JSONGateResolver) { - log.V(100).Infof("resolving target %s to %d connections\n", r.target.URL.String(), *numConnections) targets := b.GetTargets(r.poolType)