2323import static io .grpc .ConnectivityState .READY ;
2424import static io .grpc .ConnectivityState .SHUTDOWN ;
2525import static io .grpc .ConnectivityState .TRANSIENT_FAILURE ;
26+ import static io .grpc .InternalEquivalentAddressGroup .ATTR_WEIGHT ;
2627import static io .grpc .LoadBalancer .HAS_HEALTH_PRODUCER_LISTENER_KEY ;
2728import static io .grpc .LoadBalancer .HEALTH_CONSUMER_LISTENER_ARG_KEY ;
2829import static io .grpc .LoadBalancer .IS_PETIOLE_POLICY ;
7071import io .grpc .internal .PickFirstLeafLoadBalancer .PickFirstLeafLoadBalancerConfig ;
7172import java .net .InetSocketAddress ;
7273import java .net .SocketAddress ;
74+ import java .util .ArrayDeque ;
7375import java .util .ArrayList ;
7476import java .util .Arrays ;
7577import java .util .Collections ;
7678import java .util .List ;
79+ import java .util .Queue ;
80+ import java .util .Random ;
7781import java .util .concurrent .ScheduledExecutorService ;
7882import java .util .concurrent .TimeUnit ;
7983import org .junit .After ;
@@ -149,6 +153,7 @@ public void uncaughtException(Thread t, Throwable e) {
149153
150154 private String originalHappyEyeballsEnabledValue ;
151155 private String originalSerializeRetriesValue ;
156+ private boolean originalWeightedShuffling ;
152157
153158 private long backoffMillis ;
154159
@@ -165,6 +170,8 @@ public void setUp() {
165170 System .setProperty (PickFirstLoadBalancerProvider .GRPC_PF_USE_HAPPY_EYEBALLS ,
166171 Boolean .toString (enableHappyEyeballs ));
167172
173+ originalWeightedShuffling = PickFirstLeafLoadBalancer .weightedShuffling ;
174+
168175 for (int i = 1 ; i <= 5 ; i ++) {
169176 SocketAddress addr = new FakeSocketAddress ("server" + i );
170177 servers .add (new EquivalentAddressGroup (addr ));
@@ -207,6 +214,7 @@ public void tearDown() {
207214 System .setProperty (PickFirstLoadBalancerProvider .GRPC_PF_USE_HAPPY_EYEBALLS ,
208215 originalHappyEyeballsEnabledValue );
209216 }
217+ PickFirstLeafLoadBalancer .weightedShuffling = originalWeightedShuffling ;
210218
211219 loadBalancer .shutdown ();
212220 verifyNoMoreInteractions (mockArgs );
@@ -242,6 +250,12 @@ public void pickAfterResolved() {
242250 verifyNoMoreInteractions (mockHelper );
243251 }
244252
253+ @ Test
254+ public void pickAfterResolved_shuffle_oppositeWeightedShuffling () {
255+ PickFirstLeafLoadBalancer .weightedShuffling = !PickFirstLeafLoadBalancer .weightedShuffling ;
256+ pickAfterResolved_shuffle ();
257+ }
258+
245259 @ Test
246260 public void pickAfterResolved_shuffle () {
247261 servers .remove (4 );
@@ -305,6 +319,103 @@ public void pickAfterResolved_noShuffle() {
305319 assertNotNull (pickerCaptor .getValue ().pickSubchannel (mockArgs ));
306320 }
307321
322+ @ Test
323+ public void pickAfterResolved_shuffleImplicitUniform_oppositeWeightedShuffling () {
324+ PickFirstLeafLoadBalancer .weightedShuffling = !PickFirstLeafLoadBalancer .weightedShuffling ;
325+ pickAfterResolved_shuffleImplicitUniform ();
326+ }
327+
328+ @ Test
329+ public void pickAfterResolved_shuffleImplicitUniform () {
330+ EquivalentAddressGroup eag1 = new EquivalentAddressGroup (new FakeSocketAddress ("server1" ));
331+ EquivalentAddressGroup eag2 = new EquivalentAddressGroup (new FakeSocketAddress ("server2" ));
332+ EquivalentAddressGroup eag3 = new EquivalentAddressGroup (new FakeSocketAddress ("server3" ));
333+
334+ int [] counts = countAddressSelections (99 , Arrays .asList (eag1 , eag2 , eag3 ));
335+ assertThat (counts [0 ]).isWithin (7 ).of (33 );
336+ assertThat (counts [1 ]).isWithin (7 ).of (33 );
337+ assertThat (counts [2 ]).isWithin (7 ).of (33 );
338+ }
339+
340+ @ Test
341+ public void pickAfterResolved_shuffleExplicitUniform_oppositeWeightedShuffling () {
342+ PickFirstLeafLoadBalancer .weightedShuffling = !PickFirstLeafLoadBalancer .weightedShuffling ;
343+ pickAfterResolved_shuffleExplicitUniform ();
344+ }
345+
346+ @ Test
347+ public void pickAfterResolved_shuffleExplicitUniform () {
348+ EquivalentAddressGroup eag1 = new EquivalentAddressGroup (
349+ new FakeSocketAddress ("server1" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 111L ).build ());
350+ EquivalentAddressGroup eag2 = new EquivalentAddressGroup (
351+ new FakeSocketAddress ("server2" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 111L ).build ());
352+ EquivalentAddressGroup eag3 = new EquivalentAddressGroup (
353+ new FakeSocketAddress ("server3" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 111L ).build ());
354+
355+ int [] counts = countAddressSelections (99 , Arrays .asList (eag1 , eag2 , eag3 ));
356+ assertThat (counts [0 ]).isWithin (7 ).of (33 );
357+ assertThat (counts [1 ]).isWithin (7 ).of (33 );
358+ assertThat (counts [2 ]).isWithin (7 ).of (33 );
359+ }
360+
361+ @ Test
362+ public void pickAfterResolved_shuffleWeighted_noWeightedShuffling () {
363+ PickFirstLeafLoadBalancer .weightedShuffling = false ;
364+ EquivalentAddressGroup eag1 = new EquivalentAddressGroup (
365+ new FakeSocketAddress ("server1" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 12L ).build ());
366+ EquivalentAddressGroup eag2 = new EquivalentAddressGroup (
367+ new FakeSocketAddress ("server2" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 3L ).build ());
368+ EquivalentAddressGroup eag3 = new EquivalentAddressGroup (
369+ new FakeSocketAddress ("server3" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 1L ).build ());
370+
371+ int [] counts = countAddressSelections (100 , Arrays .asList (eag1 , eag2 , eag3 ));
372+ assertThat (counts [0 ]).isWithin (7 ).of (33 );
373+ assertThat (counts [1 ]).isWithin (7 ).of (33 );
374+ assertThat (counts [2 ]).isWithin (7 ).of (33 );
375+ }
376+
377+ @ Test
378+ public void pickAfterResolved_shuffleWeighted_weightedShuffling () {
379+ PickFirstLeafLoadBalancer .weightedShuffling = true ;
380+ EquivalentAddressGroup eag1 = new EquivalentAddressGroup (
381+ new FakeSocketAddress ("server1" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 12L ).build ());
382+ EquivalentAddressGroup eag2 = new EquivalentAddressGroup (
383+ new FakeSocketAddress ("server2" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 3L ).build ());
384+ EquivalentAddressGroup eag3 = new EquivalentAddressGroup (
385+ new FakeSocketAddress ("server3" ), Attributes .newBuilder ().set (ATTR_WEIGHT , 1L ).build ());
386+
387+ int [] counts = countAddressSelections (100 , Arrays .asList (eag1 , eag2 , eag3 ));
388+ assertThat (counts [0 ]).isWithin (7 ).of (75 ); // 100*12/16
389+ assertThat (counts [1 ]).isWithin (7 ).of (19 ); // 100*3/16
390+ assertThat (counts [2 ]).isWithin (7 ).of (6 ); // 100*1/16
391+ }
392+
393+ /** Returns int[index_of_eag] array with number of times each eag was selected. */
394+ private int [] countAddressSelections (int trials , List <EquivalentAddressGroup > eags ) {
395+ int [] counts = new int [eags .size ()];
396+ Random random = new Random (1 );
397+ for (int i = 0 ; i < trials ; i ++) {
398+ RecordingHelper helper = new RecordingHelper ();
399+ LoadBalancer lb = new PickFirstLeafLoadBalancer (helper );
400+ assertThat (lb .acceptResolvedAddresses (ResolvedAddresses .newBuilder ()
401+ .setAddresses (eags )
402+ .setAttributes (affinity )
403+ .setLoadBalancingPolicyConfig (
404+ new PickFirstLeafLoadBalancerConfig (true , random .nextLong ()))
405+ .build ()))
406+ .isSameInstanceAs (Status .OK );
407+ helper .subchannels .remove ().listener .onSubchannelState (
408+ ConnectivityStateInfo .forNonError (READY ));
409+
410+ assertThat (helper .state ).isEqualTo (READY );
411+ Subchannel subchannel = helper .picker .pickSubchannel (mockArgs ).getSubchannel ();
412+ counts [eags .indexOf (subchannel .getAddresses ())]++;
413+
414+ lb .shutdown ();
415+ }
416+ return counts ;
417+ }
418+
308419 @ Test
309420 public void requestConnectionPicker () {
310421 // Set up
@@ -2945,13 +3056,7 @@ public String toString() {
29453056 }
29463057 }
29473058
2948- private class MockHelperImpl extends LoadBalancer .Helper {
2949- private final List <Subchannel > subchannels ;
2950-
2951- public MockHelperImpl (List <? extends Subchannel > subchannels ) {
2952- this .subchannels = new ArrayList <Subchannel >(subchannels );
2953- }
2954-
3059+ private class BaseHelper extends LoadBalancer .Helper {
29553060 @ Override
29563061 public ManagedChannel createOobChannel (EquivalentAddressGroup eag , String authority ) {
29573062 return null ;
@@ -2981,6 +3086,14 @@ public ScheduledExecutorService getScheduledExecutorService() {
29813086 public void refreshNameResolution () {
29823087 // noop
29833088 }
3089+ }
3090+
3091+ private class MockHelperImpl extends BaseHelper {
3092+ private final List <Subchannel > subchannels ;
3093+
3094+ public MockHelperImpl (List <? extends Subchannel > subchannels ) {
3095+ this .subchannels = new ArrayList <Subchannel >(subchannels );
3096+ }
29843097
29853098 @ Override
29863099 public Subchannel createSubchannel (CreateSubchannelArgs args ) {
@@ -2997,4 +3110,23 @@ public Subchannel createSubchannel(CreateSubchannelArgs args) {
29973110 throw new IllegalArgumentException ("Unexpected addresses: " + args .getAddresses ());
29983111 }
29993112 }
3113+
3114+ class RecordingHelper extends BaseHelper {
3115+ ConnectivityState state ;
3116+ SubchannelPicker picker ;
3117+ final Queue <FakeSubchannel > subchannels = new ArrayDeque <>();
3118+
3119+ @ Override
3120+ public void updateBalancingState (ConnectivityState newState , SubchannelPicker newPicker ) {
3121+ this .state = newState ;
3122+ this .picker = newPicker ;
3123+ }
3124+
3125+ @ Override
3126+ public Subchannel createSubchannel (CreateSubchannelArgs args ) {
3127+ FakeSubchannel subchannel = new FakeSubchannel (args .getAddresses (), args .getAttributes ());
3128+ subchannels .add (subchannel );
3129+ return subchannel ;
3130+ }
3131+ }
30003132}
0 commit comments