Skip to content

Commit c120e36

Browse files
committed
core: PF Index.size() should be number of addresses
This would impact TRANSIENT_FAILURE and refreshNameResolver() logic for dual-stack endpoints.
1 parent 6dbd1b9 commit c120e36

File tree

2 files changed

+63
-6
lines changed

2 files changed

+63
-6
lines changed

core/src/main/java/io/grpc/internal/PickFirstLeafLoadBalancer.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public void handleNameResolutionError(Status error) {
208208
}
209209
subchannels.clear();
210210
if (addressIndex != null) {
211-
addressIndex.updateGroups(null);
211+
addressIndex.updateGroups(ImmutableList.of());
212212
}
213213
rawConnectivityState = TRANSIENT_FAILURE;
214214
updateBalancingState(TRANSIENT_FAILURE, new Picker(PickResult.withError(error)));
@@ -566,11 +566,12 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
566566
@VisibleForTesting
567567
static final class Index {
568568
private List<EquivalentAddressGroup> addressGroups;
569+
private int size;
569570
private int groupIndex;
570571
private int addressIndex;
571572

572573
public Index(List<EquivalentAddressGroup> groups) {
573-
this.addressGroups = groups != null ? groups : Collections.emptyList();
574+
updateGroups(groups);
574575
}
575576

576577
public boolean isValid() {
@@ -629,9 +630,14 @@ public List<EquivalentAddressGroup> getCurrentEagAsList() {
629630
/**
630631
* Update to new groups, resetting the current index.
631632
*/
632-
public void updateGroups(ImmutableList<EquivalentAddressGroup> newGroups) {
633-
addressGroups = newGroups != null ? newGroups : Collections.emptyList();
633+
public void updateGroups(List<EquivalentAddressGroup> newGroups) {
634+
addressGroups = checkNotNull(newGroups, "newGroups");
634635
reset();
636+
int size = 0;
637+
for (EquivalentAddressGroup eag : newGroups) {
638+
size += eag.getAddresses().size();
639+
}
640+
this.size = size;
635641
}
636642

637643
/**
@@ -652,7 +658,7 @@ public boolean seekTo(SocketAddress needle) {
652658
}
653659

654660
public int size() {
655-
return (addressGroups != null) ? addressGroups.size() : 0;
661+
return size;
656662
}
657663
}
658664

core/src/test/java/io/grpc/internal/PickFirstLeafLoadBalancerTest.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ public void nameResolutionError_emptyAddressList() {
662662
}
663663

664664
@Test
665-
public void nameResolutionAfterSufficientTFs() {
665+
public void nameResolutionAfterSufficientTFs_multipleEags() {
666666
InOrder inOrder = inOrder(mockHelper);
667667
acceptXSubchannels(3);
668668
Status error = Status.UNAVAILABLE.withDescription("boom!");
@@ -707,6 +707,57 @@ public void nameResolutionAfterSufficientTFs() {
707707
inOrder.verify(mockHelper).refreshNameResolution();
708708
}
709709

710+
@Test
711+
public void nameResolutionAfterSufficientTFs_singleEag() {
712+
InOrder inOrder = inOrder(mockHelper);
713+
EquivalentAddressGroup eag = new EquivalentAddressGroup(Arrays.asList(
714+
new FakeSocketAddress("server1"),
715+
new FakeSocketAddress("server2"),
716+
new FakeSocketAddress("server3")));
717+
loadBalancer.acceptResolvedAddresses(
718+
ResolvedAddresses.newBuilder().setAddresses(Arrays.asList(eag)).build());
719+
Status error = Status.UNAVAILABLE.withDescription("boom!");
720+
721+
// Initial subchannel gets TF, LB is still in CONNECTING
722+
verify(mockSubchannel1).start(stateListenerCaptor.capture());
723+
SubchannelStateListener stateListener1 = stateListenerCaptor.getValue();
724+
stateListener1.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
725+
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
726+
assertEquals(Status.OK, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
727+
728+
// Second subchannel gets TF, no UpdateBalancingState called
729+
verify(mockSubchannel2).start(stateListenerCaptor.capture());
730+
SubchannelStateListener stateListener2 = stateListenerCaptor.getValue();
731+
stateListener2.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
732+
inOrder.verify(mockHelper, never()).refreshNameResolution();
733+
inOrder.verify(mockHelper, never()).updateBalancingState(any(), any());
734+
735+
// Third subchannel gets TF, LB goes into TRANSIENT_FAILURE and does a refreshNameResolution
736+
verify(mockSubchannel3).start(stateListenerCaptor.capture());
737+
SubchannelStateListener stateListener3 = stateListenerCaptor.getValue();
738+
stateListener3.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
739+
inOrder.verify(mockHelper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture());
740+
inOrder.verify(mockHelper).refreshNameResolution();
741+
assertEquals(error, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
742+
743+
// Only after we have TFs reported for # of subchannels do we call refreshNameResolution
744+
stateListener2.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
745+
inOrder.verify(mockHelper, never()).refreshNameResolution();
746+
stateListener2.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
747+
inOrder.verify(mockHelper, never()).refreshNameResolution();
748+
stateListener2.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
749+
inOrder.verify(mockHelper).refreshNameResolution();
750+
751+
// Now that we have refreshed, the count should have been reset
752+
// Only after we have TFs reported for # of subchannels do we call refreshNameResolution
753+
stateListener1.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
754+
inOrder.verify(mockHelper, never()).refreshNameResolution();
755+
stateListener2.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
756+
inOrder.verify(mockHelper, never()).refreshNameResolution();
757+
stateListener3.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
758+
inOrder.verify(mockHelper).refreshNameResolution();
759+
}
760+
710761
@Test
711762
public void nameResolutionSuccessAfterError() {
712763
loadBalancer.handleNameResolutionError(Status.NOT_FOUND.withDescription("nameResolutionError"));

0 commit comments

Comments
 (0)