Skip to content

Commit 62f39e4

Browse files
authored
shuffle targets during parse phase (#343)
* shuffle targets during parse phase * don't use global rand
1 parent 4bd6fbc commit 62f39e4

File tree

1 file changed

+29
-19
lines changed

1 file changed

+29
-19
lines changed

go/vt/vtgateproxy/discovery.go

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ type JSONGateResolverBuilder struct {
8282
targets map[string][]targetHost
8383
resolvers []*JSONGateResolver
8484

85-
rand *rand.Rand
85+
sorter *shuffleSorter
8686
ticker *time.Ticker
8787
checksum []byte
8888
}
@@ -114,6 +114,7 @@ func RegisterJSONGateResolver(
114114
poolTypeField: poolTypeField,
115115
affinityField: affinityField,
116116
affinityValue: affinityValue,
117+
sorter: newShuffleSorter(),
117118
}
118119

119120
resolver.Register(jsonDiscovery)
@@ -133,9 +134,6 @@ func (*JSONGateResolverBuilder) Scheme() string { return "vtgate" }
133134

134135
// Parse and validate the format of the file and start watching for changes
135136
func (b *JSONGateResolverBuilder) start() error {
136-
137-
b.rand = rand.New(rand.NewSource(time.Now().UnixNano()))
138-
139137
// Perform the initial parse
140138
_, err := b.parse()
141139
if err != nil {
@@ -288,11 +286,7 @@ func (b *JSONGateResolverBuilder) parse() (bool, error) {
288286
}
289287

290288
for poolType := range targets {
291-
if b.affinityField != "" {
292-
sort.Slice(targets[poolType], func(i, j int) bool {
293-
return b.affinityValue == targets[poolType][i].Affinity
294-
})
295-
}
289+
b.sorter.shuffleSort(targets[poolType], b.affinityField, b.affinityValue)
296290
if len(targets[poolType]) > *numConnections {
297291
targets[poolType] = targets[poolType][:*numConnections]
298292
}
@@ -324,32 +318,48 @@ func (b *JSONGateResolverBuilder) GetTargets(poolType string) []targetHost {
324318
targets = append(targets, b.targets[poolType]...)
325319
b.mu.RUnlock()
326320

327-
// Shuffle to ensure every host has a different order to iterate through, putting
328-
// the affinity matching (e.g. same az) hosts at the front and the non-matching ones
329-
// at the end.
330-
//
331-
// Only need to do n-1 swaps since the last host is always in the right place.
321+
b.sorter.shuffleSort(targets, b.affinityField, b.affinityValue)
322+
323+
return targets
324+
}
325+
326+
type shuffleSorter struct {
327+
rand *rand.Rand
328+
mu *sync.Mutex
329+
}
330+
331+
func newShuffleSorter() *shuffleSorter {
332+
return &shuffleSorter{
333+
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
334+
mu: &sync.Mutex{},
335+
}
336+
}
337+
338+
// shuffleSort shuffles a slice of targetHost to ensure every host has a
339+
// different order to iterate through, putting the affinity matching (e.g. same
340+
// az) hosts at the front and the non-matching ones at the end.
341+
func (s *shuffleSorter) shuffleSort(targets []targetHost, affinityField, affinityValue string) {
332342
n := len(targets)
333343
head := 0
344+
// Only need to do n-1 swaps since the last host is always in the right place.
334345
tail := n - 1
335346
for i := 0; i < n-1; i++ {
336-
j := head + b.rand.Intn(tail-head+1)
347+
s.mu.Lock()
348+
j := head + s.rand.Intn(tail-head+1)
349+
s.mu.Unlock()
337350

338-
if *affinityField != "" && *affinityValue == targets[j].Affinity {
351+
if affinityField != "" && affinityValue == targets[j].Affinity {
339352
targets[head], targets[j] = targets[j], targets[head]
340353
head++
341354
} else {
342355
targets[tail], targets[j] = targets[j], targets[tail]
343356
tail--
344357
}
345358
}
346-
347-
return targets
348359
}
349360

350361
// Update the current list of hosts for the given resolver
351362
func (b *JSONGateResolverBuilder) update(r *JSONGateResolver) {
352-
353363
log.V(100).Infof("resolving target %s to %d connections\n", r.target.URL.String(), *numConnections)
354364

355365
targets := b.GetTargets(r.poolType)

0 commit comments

Comments
 (0)