34
34
import io .grpc .LoadBalancer ;
35
35
import io .grpc .Status ;
36
36
import io .grpc .SynchronizationContext .ScheduledHandle ;
37
+ import java .net .Inet4Address ;
38
+ import java .net .InetSocketAddress ;
37
39
import java .net .SocketAddress ;
38
40
import java .util .ArrayList ;
39
41
import java .util .Collections ;
@@ -58,17 +60,17 @@ final class PickFirstLeafLoadBalancer extends LoadBalancer {
58
60
private static final Logger log = Logger .getLogger (PickFirstLeafLoadBalancer .class .getName ());
59
61
@ VisibleForTesting
60
62
static final int CONNECTION_DELAY_INTERVAL_MS = 250 ;
63
+ private final boolean enableHappyEyeballs = !isSerializingRetries ()
64
+ && PickFirstLoadBalancerProvider .isEnabledHappyEyeballs ();
61
65
private final Helper helper ;
62
66
private final Map <SocketAddress , SubchannelData > subchannels = new HashMap <>();
63
- private final Index addressIndex = new Index (ImmutableList .of ());
67
+ private final Index addressIndex = new Index (ImmutableList .of (), this . enableHappyEyeballs );
64
68
private int numTf = 0 ;
65
69
private boolean firstPass = true ;
66
70
@ Nullable
67
71
private ScheduledHandle scheduleConnectionTask = null ;
68
72
private ConnectivityState rawConnectivityState = IDLE ;
69
73
private ConnectivityState concludedState = IDLE ;
70
- private final boolean enableHappyEyeballs = !isSerializingRetries ()
71
- && PickFirstLoadBalancerProvider .isEnabledHappyEyeballs ();
72
74
private boolean notAPetiolePolicy = true ; // means not under a petiole policy
73
75
private final BackoffPolicy .Provider bkoffPolProvider = new ExponentialBackoffPolicy .Provider ();
74
76
private BackoffPolicy reconnectPolicy ;
@@ -610,27 +612,26 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
610
612
}
611
613
612
614
/**
613
- * Index as in 'i', the pointer to an entry. Not a "search index."
615
+ * This contains both an ordered list of addresses and a pointer(i.e. index) to the current entry.
614
616
* All updates should be done in a synchronization context.
615
617
*/
616
618
@ VisibleForTesting
617
619
static final class Index {
618
- private List <EquivalentAddressGroup > addressGroups ;
619
- private int size ;
620
- private int groupIndex ;
621
- private int addressIndex ;
620
+ private List <UnwrappedEag > orderedAddresses ;
621
+ private int activeElement = 0 ;
622
+ private boolean enableHappyEyeballs ;
622
623
623
- public Index (List <EquivalentAddressGroup > groups ) {
624
+ Index (List <EquivalentAddressGroup > groups , boolean enableHappyEyeballs ) {
625
+ this .enableHappyEyeballs = enableHappyEyeballs ;
624
626
updateGroups (groups );
625
627
}
626
628
627
629
public boolean isValid () {
628
- // Is invalid if empty or has incremented off the end
629
- return groupIndex < addressGroups .size ();
630
+ return activeElement < orderedAddresses .size ();
630
631
}
631
632
632
633
public boolean isAtBeginning () {
633
- return groupIndex == 0 && addressIndex == 0 ;
634
+ return activeElement == 0 ;
634
635
}
635
636
636
637
/**
@@ -642,79 +643,150 @@ public boolean increment() {
642
643
return false ;
643
644
}
644
645
645
- EquivalentAddressGroup group = addressGroups .get (groupIndex );
646
- addressIndex ++;
647
- if (addressIndex >= group .getAddresses ().size ()) {
648
- groupIndex ++;
649
- addressIndex = 0 ;
650
- return groupIndex < addressGroups .size ();
651
- }
646
+ activeElement ++;
652
647
653
- return true ;
648
+ return isValid () ;
654
649
}
655
650
656
651
public void reset () {
657
- groupIndex = 0 ;
658
- addressIndex = 0 ;
652
+ activeElement = 0 ;
659
653
}
660
654
661
655
public SocketAddress getCurrentAddress () {
662
656
if (!isValid ()) {
663
657
throw new IllegalStateException ("Index is past the end of the address group list" );
664
658
}
665
- return addressGroups .get (groupIndex ). getAddresses (). get ( addressIndex ) ;
659
+ return orderedAddresses .get (activeElement ). address ;
666
660
}
667
661
668
662
public Attributes getCurrentEagAttributes () {
669
663
if (!isValid ()) {
670
664
throw new IllegalStateException ("Index is off the end of the address group list" );
671
665
}
672
- return addressGroups .get (groupIndex ). getAttributes () ;
666
+ return orderedAddresses .get (activeElement ). attributes ;
673
667
}
674
668
675
669
public List <EquivalentAddressGroup > getCurrentEagAsList () {
676
- return Collections .singletonList (
677
- new EquivalentAddressGroup (getCurrentAddress (), getCurrentEagAttributes ()));
670
+ return Collections .singletonList (getCurrentEag ());
671
+ }
672
+
673
+ private EquivalentAddressGroup getCurrentEag () {
674
+ if (!isValid ()) {
675
+ throw new IllegalStateException ("Index is past the end of the address group list" );
676
+ }
677
+ return orderedAddresses .get (activeElement ).asEag ();
678
678
}
679
679
680
680
/**
681
681
* Update to new groups, resetting the current index.
682
682
*/
683
683
public void updateGroups (List <EquivalentAddressGroup > newGroups ) {
684
- addressGroups = checkNotNull (newGroups , "newGroups" );
684
+ checkNotNull (newGroups , "newGroups" );
685
+ orderedAddresses = enableHappyEyeballs
686
+ ? updateGroupsHE (newGroups )
687
+ : updateGroupsNonHE (newGroups );
685
688
reset ();
686
- int size = 0 ;
687
- for (EquivalentAddressGroup eag : newGroups ) {
688
- size += eag .getAddresses ().size ();
689
- }
690
- this .size = size ;
691
689
}
692
690
693
691
/**
694
692
* Returns false if the needle was not found and the current index was left unchanged.
695
693
*/
696
694
public boolean seekTo (SocketAddress needle ) {
697
- for ( int i = 0 ; i < addressGroups . size (); i ++) {
698
- EquivalentAddressGroup group = addressGroups . get ( i );
699
- int j = group . getAddresses (). indexOf (needle );
700
- if ( j == - 1 ) {
701
- continue ;
695
+ checkNotNull ( needle , "needle" );
696
+ for ( int i = 0 ; i < orderedAddresses . size (); i ++) {
697
+ if ( orderedAddresses . get ( i ). address . equals (needle )) {
698
+ this . activeElement = i ;
699
+ return true ;
702
700
}
703
- this .groupIndex = i ;
704
- this .addressIndex = j ;
705
- return true ;
706
701
}
707
702
return false ;
708
703
}
709
704
710
705
public int size () {
711
- return size ;
706
+ return orderedAddresses .size ();
707
+ }
708
+
709
+ private List <UnwrappedEag > updateGroupsNonHE (List <EquivalentAddressGroup > newGroups ) {
710
+ List <UnwrappedEag > entries = new ArrayList <>();
711
+ for (int g = 0 ; g < newGroups .size (); g ++) {
712
+ EquivalentAddressGroup eag = newGroups .get (g );
713
+ for (int a = 0 ; a < eag .getAddresses ().size (); a ++) {
714
+ SocketAddress addr = eag .getAddresses ().get (a );
715
+ entries .add (new UnwrappedEag (eag .getAttributes (), addr ));
716
+ }
717
+ }
718
+
719
+ return entries ;
720
+ }
721
+
722
+ private List <UnwrappedEag > updateGroupsHE (List <EquivalentAddressGroup > newGroups ) {
723
+ Boolean firstIsV6 = null ;
724
+ List <UnwrappedEag > v4Entries = new ArrayList <>();
725
+ List <UnwrappedEag > v6Entries = new ArrayList <>();
726
+ for (int g = 0 ; g < newGroups .size (); g ++) {
727
+ EquivalentAddressGroup eag = newGroups .get (g );
728
+ for (int a = 0 ; a < eag .getAddresses ().size (); a ++) {
729
+ SocketAddress addr = eag .getAddresses ().get (a );
730
+ boolean isIpV4 = addr instanceof InetSocketAddress
731
+ && ((InetSocketAddress ) addr ).getAddress () instanceof Inet4Address ;
732
+ if (isIpV4 ) {
733
+ if (firstIsV6 == null ) {
734
+ firstIsV6 = false ;
735
+ }
736
+ v4Entries .add (new UnwrappedEag (eag .getAttributes (), addr ));
737
+ } else {
738
+ if (firstIsV6 == null ) {
739
+ firstIsV6 = true ;
740
+ }
741
+ v6Entries .add (new UnwrappedEag (eag .getAttributes (), addr ));
742
+ }
743
+ }
744
+ }
745
+
746
+ return firstIsV6 != null && firstIsV6
747
+ ? interleave (v6Entries , v4Entries )
748
+ : interleave (v4Entries , v6Entries );
749
+ }
750
+
751
+ private List <UnwrappedEag > interleave (List <UnwrappedEag > firstFamily ,
752
+ List <UnwrappedEag > secondFamily ) {
753
+ if (firstFamily .isEmpty ()) {
754
+ return secondFamily ;
755
+ }
756
+ if (secondFamily .isEmpty ()) {
757
+ return firstFamily ;
758
+ }
759
+
760
+ List <UnwrappedEag > result = new ArrayList <>(firstFamily .size () + secondFamily .size ());
761
+ for (int i = 0 ; i < Math .max (firstFamily .size (), secondFamily .size ()); i ++) {
762
+ if (i < firstFamily .size ()) {
763
+ result .add (firstFamily .get (i ));
764
+ }
765
+ if (i < secondFamily .size ()) {
766
+ result .add (secondFamily .get (i ));
767
+ }
768
+ }
769
+ return result ;
770
+ }
771
+
772
+ private static final class UnwrappedEag {
773
+ private final Attributes attributes ;
774
+ private final SocketAddress address ;
775
+
776
+ public UnwrappedEag (Attributes attributes , SocketAddress address ) {
777
+ this .attributes = attributes ;
778
+ this .address = address ;
779
+ }
780
+
781
+ private EquivalentAddressGroup asEag () {
782
+ return new EquivalentAddressGroup (address , attributes );
783
+ }
712
784
}
713
785
}
714
786
715
787
@ VisibleForTesting
716
- int getGroupIndex () {
717
- return addressIndex .groupIndex ;
788
+ int getIndexLocation () {
789
+ return addressIndex .activeElement ;
718
790
}
719
791
720
792
@ VisibleForTesting
@@ -778,4 +850,5 @@ public PickFirstLeafLoadBalancerConfig(@Nullable Boolean shuffleAddressList) {
778
850
this .randomSeed = randomSeed ;
779
851
}
780
852
}
853
+
781
854
}
0 commit comments