Skip to content

Commit ec46fe3

Browse files
fixed sequence, changed long --> int, added comments
1 parent f0421d2 commit ec46fe3

File tree

1 file changed

+23
-13
lines changed

1 file changed

+23
-13
lines changed

xds/src/main/java/io/grpc/xds/WeightedRoundRobinLoadBalancer.java

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,10 @@ public boolean isEquivalentTo(RoundRobinPicker picker) {
332332
* Implementation of Static Stride Scheduler, replaces EDFScheduler.
333333
* <p>
334334
* The Static Stride Scheduler works by iterating through the list of subchannel weights
335-
* and using modular arithmetic to evenly distribute picks and skips, favoring
336-
* entries with the highest weight.
335+
* and using modular arithmetic to evenly distribute picks and skips, favoring entries with the
336+
* highest weight. It generates a practically equivalent sequence of picks as the EDFScheduler.
337+
* Albeit needing more bandwidth, the Static Stride Scheduler is more performant than the
338+
* EDFScheduler, as it removes the need for a priority queue (and thus mutex locks).
337339
* <p>
338340
* go/static-stride-scheduler
339341
* <p>
@@ -346,14 +348,12 @@ public boolean isEquivalentTo(RoundRobinPicker picker) {
346348
static final class StaticStrideScheduler {
347349
private final int[] scaledWeights;
348350
private final int sizeDivisor;
349-
private final Random random;
350351
private final AtomicInteger sequence;
351352
private static final int K_MAX_WEIGHT = 0xFFFF;
352-
private static final long UINT32_MAX = 0xFFFF_FFFFL;
353353

354354
StaticStrideScheduler(float[] weights, Random random) {
355+
checkArgument(weights.length >= 1, "Couldn't build scheduler: requires at least one weight");
355356
int numChannels = weights.length;
356-
checkArgument(numChannels >= 1, "Couldn't build scheduler: requires at least one weight");
357357
int numWeightedChannels = 0;
358358
double sumWeight = 0;
359359
float maxWeight = 0;
@@ -385,29 +385,39 @@ static final class StaticStrideScheduler {
385385

386386
this.scaledWeights = scaledWeights;
387387
this.sizeDivisor = numChannels;
388-
this.random = random;
389-
this.sequence = new AtomicInteger((int) (this.random.nextDouble() * UINT32_MAX));
388+
this.sequence = new AtomicInteger(random.nextInt());
390389

391390
}
392391

393-
/** Returns the next sequence number and increases sequence with wraparound. */
392+
/** Returns the next sequence number and atomically increases sequence with wraparound. */
394393
private long nextSequence() {
395394
return Integer.toUnsignedLong(sequence.getAndIncrement());
396395
}
397396

397+
public long getSequence() {
398+
return Integer.toUnsignedLong(sequence.get());
399+
}
398400

399-
/** Selects index of next backend server. */
401+
/*
402+
* Selects index of next backend server.
403+
* <p>
404+
* A 2D array is compactly represented where the row represents the generation and the column
405+
* represents the backend index. The value of an element is a boolean value which indicates
406+
* whether or not a backend should be picked now. An atomically incremented counter keeps track
407+
* of our backend and generation through modular arithmetic within the pick() method.
408+
* An offset is also included to minimize consecutive non-picks of a backend.
409+
*/
400410
int pick() {
401411
while (true) {
402412
long sequence = this.nextSequence();
403-
long backendIndex = sequence % this.sizeDivisor;
413+
int backendIndex = (int) (sequence % this.sizeDivisor);
404414
long generation = sequence / this.sizeDivisor;
405-
long weight = this.scaledWeights[(int) backendIndex];
406-
long offset = backendIndex * K_MAX_WEIGHT / 2;
415+
long weight = this.scaledWeights[backendIndex];
416+
long offset = (long) K_MAX_WEIGHT / 2 * backendIndex;
407417
if ((weight * generation + offset) % K_MAX_WEIGHT < K_MAX_WEIGHT - weight) {
408418
continue;
409419
}
410-
return (int) backendIndex;
420+
return backendIndex;
411421
}
412422
}
413423
}

0 commit comments

Comments
 (0)