Skip to content

Commit 653d076

Browse files
committed
xds: Avoid PriorityLb re-enabling timer on duplicate CONNECTING (#12289)
Since c4256ad we no longer fabricate a TRANSIENT_FAILURE update from children. However, previously that would have set seenReadyOrIdleSinceTransientFailure = false and prevented future timer creation. If a LB policy gives extraneous updates with state CONNECTING, then it was possible to re-create failOverTimer which would then wait the 10 seconds for the child to finish CONNECTING. We only want to give the child one opportunity after transitioning out of READY/IDLE. grpc/proposal#509
1 parent a5c2b1a commit 653d076

File tree

2 files changed

+53
-1
lines changed

2 files changed

+53
-1
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,13 +320,14 @@ public void updateBalancingState(final ConnectivityState newState,
320320
if (!children.containsKey(priority)) {
321321
return;
322322
}
323+
ConnectivityState oldState = connectivityState;
323324
connectivityState = newState;
324325
picker = newPicker;
325326

326327
if (deletionTimer != null && deletionTimer.isPending()) {
327328
return;
328329
}
329-
if (newState.equals(CONNECTING)) {
330+
if (newState.equals(CONNECTING) && !oldState.equals(newState)) {
330331
if (!failOverTimer.isPending() && seenReadyOrIdleSinceTransientFailure) {
331332
failOverTimer = syncContext.schedule(new FailOverTask(), 10, TimeUnit.SECONDS,
332333
executor);

xds/src/test/java/io/grpc/xds/PriorityLoadBalancerTest.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static org.mockito.Mockito.atLeastOnce;
2929
import static org.mockito.Mockito.clearInvocations;
3030
import static org.mockito.Mockito.doReturn;
31+
import static org.mockito.Mockito.inOrder;
3132
import static org.mockito.Mockito.mock;
3233
import static org.mockito.Mockito.never;
3334
import static org.mockito.Mockito.times;
@@ -70,6 +71,7 @@
7071
import org.junit.runners.JUnit4;
7172
import org.mockito.ArgumentCaptor;
7273
import org.mockito.Captor;
74+
import org.mockito.InOrder;
7375
import org.mockito.Mock;
7476
import org.mockito.junit.MockitoJUnit;
7577
import org.mockito.junit.MockitoRule;
@@ -554,6 +556,55 @@ public void connectingResetFailOverIfSeenReadyOrIdleSinceTransientFailure() {
554556
assertThat(fooHelpers).hasSize(2);
555557
}
556558

559+
@Test
560+
public void failoverTimerNotRestartedOnDupConnecting() {
561+
InOrder inOrder = inOrder(helper);
562+
PriorityChildConfig priorityChildConfig0 =
563+
new PriorityChildConfig(newChildConfig(fooLbProvider, new Object()), true);
564+
PriorityChildConfig priorityChildConfig1 =
565+
new PriorityChildConfig(newChildConfig(fooLbProvider, new Object()), true);
566+
PriorityLbConfig priorityLbConfig =
567+
new PriorityLbConfig(
568+
ImmutableMap.of("p0", priorityChildConfig0, "p1", priorityChildConfig1),
569+
ImmutableList.of("p0", "p1"));
570+
priorityLb.acceptResolvedAddresses(
571+
ResolvedAddresses.newBuilder()
572+
.setAddresses(ImmutableList.<EquivalentAddressGroup>of())
573+
.setLoadBalancingPolicyConfig(priorityLbConfig)
574+
.build());
575+
// Nothing important about this verify, other than to provide a baseline
576+
inOrder.verify(helper)
577+
.updateBalancingState(eq(CONNECTING), pickerReturns(PickResult.withNoResult()));
578+
assertThat(fooBalancers).hasSize(1);
579+
assertThat(fooHelpers).hasSize(1);
580+
Helper helper0 = Iterables.getOnlyElement(fooHelpers);
581+
582+
// Cause seenReadyOrIdleSinceTransientFailure = true
583+
helper0.updateBalancingState(IDLE, EMPTY_PICKER);
584+
inOrder.verify(helper)
585+
.updateBalancingState(eq(IDLE), pickerReturns(PickResult.withNoResult()));
586+
helper0.updateBalancingState(CONNECTING, EMPTY_PICKER);
587+
588+
// p0 keeps repeating CONNECTING, failover happens
589+
fakeClock.forwardTime(5, TimeUnit.SECONDS);
590+
helper0.updateBalancingState(CONNECTING, EMPTY_PICKER);
591+
fakeClock.forwardTime(5, TimeUnit.SECONDS);
592+
assertThat(fooBalancers).hasSize(2);
593+
assertThat(fooHelpers).hasSize(2);
594+
inOrder.verify(helper, times(2))
595+
.updateBalancingState(eq(CONNECTING), pickerReturns(PickResult.withNoResult()));
596+
Helper helper1 = Iterables.getLast(fooHelpers);
597+
598+
// p0 keeps repeating CONNECTING, no reset of failover timer
599+
helper1.updateBalancingState(IDLE, EMPTY_PICKER); // Stop timer for p1
600+
inOrder.verify(helper)
601+
.updateBalancingState(eq(IDLE), pickerReturns(PickResult.withNoResult()));
602+
helper0.updateBalancingState(CONNECTING, EMPTY_PICKER);
603+
fakeClock.forwardTime(10, TimeUnit.SECONDS);
604+
inOrder.verify(helper, never())
605+
.updateBalancingState(eq(CONNECTING), any());
606+
}
607+
557608
@Test
558609
public void readyToConnectDoesNotFailOverButUpdatesPicker() {
559610
PriorityChildConfig priorityChildConfig0 =

0 commit comments

Comments
 (0)