Search in sources :

Example 16 with ConnectivityStateInfo

use of io.grpc.ConnectivityStateInfo in project grpc-java by grpc.

the class GrpclbLoadBalancerTest method grpclbWorking.

@Test
public void grpclbWorking() {
    InOrder inOrder = inOrder(helper, subchannelPool);
    List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
    deliverResolvedAddresses(Collections.<EquivalentAddressGroup>emptyList(), grpclbBalancerList);
    // Fallback timer is started as soon as the addresses are resolved.
    assertEquals(1, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
    verify(helper).createOobChannel(eq(xattr(grpclbBalancerList)), eq(lbAuthority(0) + NO_USE_AUTHORITY_SUFFIX));
    assertEquals(1, fakeOobChannels.size());
    ManagedChannel oobChannel = fakeOobChannels.poll();
    verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
    StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
    assertEquals(1, lbRequestObservers.size());
    StreamObserver<LoadBalanceRequest> lbRequestObserver = lbRequestObservers.poll();
    verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
    // Simulate receiving LB response
    List<ServerEntry> backends1 = Arrays.asList(new ServerEntry("127.0.0.1", 2000, "token0001"), new ServerEntry("127.0.0.1", 2010, "token0002"));
    inOrder.verify(helper, never()).updateBalancingState(any(ConnectivityState.class), any(SubchannelPicker.class));
    logs.clear();
    lbResponseObserver.onNext(buildInitialResponse());
    assertThat(logs).containsExactly("INFO: [grpclb-<api.google.com>] Got an LB initial response: " + buildInitialResponse());
    logs.clear();
    lbResponseObserver.onNext(buildLbResponse(backends1));
    inOrder.verify(subchannelPool).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends1.get(0).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
    inOrder.verify(subchannelPool).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends1.get(1).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
    assertEquals(2, mockSubchannels.size());
    Subchannel subchannel1 = mockSubchannels.poll();
    Subchannel subchannel2 = mockSubchannels.poll();
    verify(subchannel1).requestConnection();
    verify(subchannel2).requestConnection();
    assertEquals(new EquivalentAddressGroup(backends1.get(0).addr, LB_BACKEND_ATTRS), subchannel1.getAddresses());
    assertEquals(new EquivalentAddressGroup(backends1.get(1).addr, LB_BACKEND_ATTRS), subchannel2.getAddresses());
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(CONNECTING));
    deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(CONNECTING));
    inOrder.verify(helper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
    RoundRobinPicker picker0 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker0.dropList).containsExactly(null, null);
    assertThat(picker0.pickList).containsExactly(BUFFER_ENTRY);
    inOrder.verifyNoMoreInteractions();
    assertThat(logs).containsExactly("DEBUG: [grpclb-<api.google.com>] Got an LB response: " + buildLbResponse(backends1)).inOrder();
    logs.clear();
    // Let subchannels be connected
    deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(READY));
    inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
    RoundRobinPicker picker1 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker1.dropList).containsExactly(null, null);
    assertThat(picker1.pickList).containsExactly(new BackendEntry(subchannel2, getLoadRecorder(), "token0002"));
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(READY));
    inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
    RoundRobinPicker picker2 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker2.dropList).containsExactly(null, null);
    assertThat(picker2.pickList).containsExactly(new BackendEntry(subchannel1, getLoadRecorder(), "token0001"), new BackendEntry(subchannel2, getLoadRecorder(), "token0002")).inOrder();
    // Disconnected subchannels
    verify(subchannel1).requestConnection();
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(IDLE));
    verify(subchannel1, times(2)).requestConnection();
    inOrder.verify(helper).refreshNameResolution();
    inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
    RoundRobinPicker picker3 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker3.dropList).containsExactly(null, null);
    assertThat(picker3.pickList).containsExactly(new BackendEntry(subchannel2, getLoadRecorder(), "token0002"));
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(CONNECTING));
    inOrder.verifyNoMoreInteractions();
    // As long as there is at least one READY subchannel, round robin will work.
    ConnectivityStateInfo errorState1 = ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE.withDescription("error1"));
    deliverSubchannelState(subchannel1, errorState1);
    inOrder.verify(helper).refreshNameResolution();
    inOrder.verifyNoMoreInteractions();
    // If no subchannel is READY, some with error and the others are IDLE, will report CONNECTING
    verify(subchannel2).requestConnection();
    deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(IDLE));
    verify(subchannel2, times(2)).requestConnection();
    inOrder.verify(helper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
    RoundRobinPicker picker4 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker4.dropList).containsExactly(null, null);
    assertThat(picker4.pickList).containsExactly(BUFFER_ENTRY);
    // Update backends, with a drop entry
    List<ServerEntry> backends2 = Arrays.asList(// New address
    new ServerEntry("127.0.0.1", 2030, "token0003"), // drop
    new ServerEntry("token0003"), // Existing address with token changed
    new ServerEntry("127.0.0.1", 2010, "token0004"), // New address appearing second time
    new ServerEntry("127.0.0.1", 2030, "token0005"), // drop
    new ServerEntry("token0006"));
    verify(subchannelPool, never()).returnSubchannel(same(subchannel1), any(ConnectivityStateInfo.class));
    lbResponseObserver.onNext(buildLbResponse(backends2));
    assertThat(logs).containsExactly("DEBUG: [grpclb-<api.google.com>] Got an LB response: " + buildLbResponse(backends2)).inOrder();
    logs.clear();
    // not in backends2, closed
    verify(subchannelPool).returnSubchannel(same(subchannel1), same(errorState1));
    // backends2[2], will be kept
    verify(subchannelPool, never()).returnSubchannel(same(subchannel2), any(ConnectivityStateInfo.class));
    inOrder.verify(subchannelPool, never()).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends2.get(2).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
    inOrder.verify(subchannelPool).takeOrCreateSubchannel(eq(new EquivalentAddressGroup(backends2.get(0).addr, LB_BACKEND_ATTRS)), any(Attributes.class));
    ConnectivityStateInfo errorOnCachedSubchannel1 = ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE.withDescription("You can get this error even if you are cached"));
    deliverSubchannelState(subchannel1, errorOnCachedSubchannel1);
    assertEquals(1, mockSubchannels.size());
    Subchannel subchannel3 = mockSubchannels.poll();
    verify(subchannel3).requestConnection();
    assertEquals(new EquivalentAddressGroup(backends2.get(0).addr, LB_BACKEND_ATTRS), subchannel3.getAddresses());
    inOrder.verify(helper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
    RoundRobinPicker picker7 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker7.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0003"), null, null, new DropEntry(getLoadRecorder(), "token0006")).inOrder();
    assertThat(picker7.pickList).containsExactly(BUFFER_ENTRY);
    // State updates on obsolete subchannel1 will only be passed to the pool
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(READY));
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forTransientFailure(Status.UNAVAILABLE));
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(SHUTDOWN));
    deliverSubchannelState(subchannel3, ConnectivityStateInfo.forNonError(READY));
    inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
    RoundRobinPicker picker8 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker8.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0003"), null, null, new DropEntry(getLoadRecorder(), "token0006")).inOrder();
    // subchannel2 is still IDLE, thus not in the active list
    assertThat(picker8.pickList).containsExactly(new BackendEntry(subchannel3, getLoadRecorder(), "token0003"), new BackendEntry(subchannel3, getLoadRecorder(), "token0005")).inOrder();
    // subchannel2 becomes READY and makes it into the list
    deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(READY));
    inOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
    RoundRobinPicker picker9 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker9.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0003"), null, null, new DropEntry(getLoadRecorder(), "token0006")).inOrder();
    assertThat(picker9.pickList).containsExactly(new BackendEntry(subchannel3, getLoadRecorder(), "token0003"), new BackendEntry(subchannel2, getLoadRecorder(), "token0004"), new BackendEntry(subchannel3, getLoadRecorder(), "token0005")).inOrder();
    verify(subchannelPool, never()).returnSubchannel(same(subchannel3), any(ConnectivityStateInfo.class));
    // Update backends, with no entry
    lbResponseObserver.onNext(buildLbResponse(Collections.<ServerEntry>emptyList()));
    verify(subchannelPool).returnSubchannel(same(subchannel2), eq(ConnectivityStateInfo.forNonError(READY)));
    verify(subchannelPool).returnSubchannel(same(subchannel3), eq(ConnectivityStateInfo.forNonError(READY)));
    inOrder.verify(helper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture());
    RoundRobinPicker picker10 = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker10.dropList).isEmpty();
    assertThat(picker10.pickList).containsExactly(new ErrorEntry(GrpclbState.NO_AVAILABLE_BACKENDS_STATUS));
    assertFalse(oobChannel.isShutdown());
    assertEquals(0, lbRequestObservers.size());
    verify(lbRequestObserver, never()).onCompleted();
    verify(lbRequestObserver, never()).onError(any(Throwable.class));
    // Load reporting was not requested, thus never scheduled
    assertEquals(0, fakeClock.numPendingTasks(LOAD_REPORTING_TASK_FILTER));
    verify(subchannelPool, never()).clear();
    balancer.shutdown();
    verify(subchannelPool).clear();
}
Also used : BackendEntry(io.grpc.grpclb.GrpclbState.BackendEntry) InOrder(org.mockito.InOrder) DropEntry(io.grpc.grpclb.GrpclbState.DropEntry) Attributes(io.grpc.Attributes) InitialLoadBalanceResponse(io.grpc.lb.v1.InitialLoadBalanceResponse) LoadBalanceResponse(io.grpc.lb.v1.LoadBalanceResponse) ErrorEntry(io.grpc.grpclb.GrpclbState.ErrorEntry) SubchannelPicker(io.grpc.LoadBalancer.SubchannelPicker) ConnectivityState(io.grpc.ConnectivityState) RoundRobinPicker(io.grpc.grpclb.GrpclbState.RoundRobinPicker) ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) EquivalentAddressGroup(io.grpc.EquivalentAddressGroup) Subchannel(io.grpc.LoadBalancer.Subchannel) LoadBalanceRequest(io.grpc.lb.v1.LoadBalanceRequest) InitialLoadBalanceRequest(io.grpc.lb.v1.InitialLoadBalanceRequest) ManagedChannel(io.grpc.ManagedChannel) Test(org.junit.Test)

Example 17 with ConnectivityStateInfo

use of io.grpc.ConnectivityStateInfo in project grpc-java by grpc.

the class GrpclbState method maybeUpdatePicker.

/**
 * Make and use a picker out of the current lists and the states of subchannels if they have
 * changed since the last picker created.
 */
private void maybeUpdatePicker() {
    List<RoundRobinEntry> pickList;
    ConnectivityState state;
    if (backendList.isEmpty()) {
        // fail. So we should check if currently in fallback first.
        if (usingFallbackBackends) {
            Status error = NO_FALLBACK_BACKENDS_STATUS.withCause(fallbackReason.getCause()).augmentDescription(fallbackReason.getDescription());
            pickList = Collections.<RoundRobinEntry>singletonList(new ErrorEntry(error));
            state = TRANSIENT_FAILURE;
        } else if (balancerWorking) {
            pickList = Collections.<RoundRobinEntry>singletonList(new ErrorEntry(NO_AVAILABLE_BACKENDS_STATUS));
            state = TRANSIENT_FAILURE;
        } else {
            // still waiting for balancer
            pickList = Collections.singletonList(BUFFER_ENTRY);
            state = CONNECTING;
        }
        maybeUpdatePicker(state, new RoundRobinPicker(dropList, pickList));
        return;
    }
    switch(config.getMode()) {
        case ROUND_ROBIN:
            pickList = new ArrayList<>(backendList.size());
            Status error = null;
            boolean hasPending = false;
            for (BackendEntry entry : backendList) {
                Subchannel subchannel = entry.subchannel;
                Attributes attrs = subchannel.getAttributes();
                ConnectivityStateInfo stateInfo = attrs.get(STATE_INFO).get();
                if (stateInfo.getState() == READY) {
                    pickList.add(entry);
                } else if (stateInfo.getState() == TRANSIENT_FAILURE) {
                    error = stateInfo.getStatus();
                } else {
                    hasPending = true;
                }
            }
            if (pickList.isEmpty()) {
                if (hasPending) {
                    pickList.add(BUFFER_ENTRY);
                    state = CONNECTING;
                } else {
                    pickList.add(new ErrorEntry(error));
                    state = TRANSIENT_FAILURE;
                }
            } else {
                state = READY;
            }
            break;
        case PICK_FIRST:
            {
                checkState(backendList.size() == 1, "Excessive backend entries: %s", backendList);
                BackendEntry onlyEntry = backendList.get(0);
                ConnectivityStateInfo stateInfo = onlyEntry.subchannel.getAttributes().get(STATE_INFO).get();
                state = stateInfo.getState();
                switch(state) {
                    case READY:
                        pickList = Collections.<RoundRobinEntry>singletonList(onlyEntry);
                        break;
                    case TRANSIENT_FAILURE:
                        pickList = Collections.<RoundRobinEntry>singletonList(new ErrorEntry(stateInfo.getStatus()));
                        break;
                    case CONNECTING:
                        pickList = Collections.singletonList(BUFFER_ENTRY);
                        break;
                    default:
                        pickList = Collections.<RoundRobinEntry>singletonList(new IdleSubchannelEntry(onlyEntry.subchannel, syncContext));
                }
                break;
            }
        default:
            throw new AssertionError("Missing case for " + config.getMode());
    }
    maybeUpdatePicker(state, new RoundRobinPicker(dropList, pickList));
}
Also used : Status(io.grpc.Status) Attributes(io.grpc.Attributes) ConnectivityState(io.grpc.ConnectivityState) ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) Subchannel(io.grpc.LoadBalancer.Subchannel)

Example 18 with ConnectivityStateInfo

use of io.grpc.ConnectivityStateInfo in project grpc-java by grpc.

the class GrpclbState method maybeUseFallbackBackends.

private void maybeUseFallbackBackends() {
    if (balancerWorking || usingFallbackBackends) {
        return;
    }
    // Balancer RPC should have either been broken or timed out.
    checkState(fallbackReason != null, "no reason to fallback");
    for (Subchannel subchannel : subchannels.values()) {
        ConnectivityStateInfo stateInfo = subchannel.getAttributes().get(STATE_INFO).get();
        if (stateInfo.getState() == READY) {
            return;
        }
        // fail to fallback.
        if (stateInfo.getState() == TRANSIENT_FAILURE) {
            fallbackReason = stateInfo.getStatus();
        }
    }
    // Fallback conditions met
    useFallbackBackends();
}
Also used : ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) Subchannel(io.grpc.LoadBalancer.Subchannel)

Example 19 with ConnectivityStateInfo

use of io.grpc.ConnectivityStateInfo in project grpc-java by grpc.

the class RoundRobinLoadBalancerTest method pickAfterStateChange.

@Test
public void pickAfterStateChange() throws Exception {
    InOrder inOrder = inOrder(mockHelper);
    loadBalancer.handleResolvedAddresses(ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(Attributes.EMPTY).build());
    Subchannel subchannel = loadBalancer.getSubchannels().iterator().next();
    Ref<ConnectivityStateInfo> subchannelStateInfo = subchannel.getAttributes().get(STATE_INFO);
    inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), isA(EmptyPicker.class));
    assertThat(subchannelStateInfo.value).isEqualTo(ConnectivityStateInfo.forNonError(IDLE));
    deliverSubchannelState(subchannel, ConnectivityStateInfo.forNonError(READY));
    inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
    assertThat(pickerCaptor.getValue()).isInstanceOf(ReadyPicker.class);
    assertThat(subchannelStateInfo.value).isEqualTo(ConnectivityStateInfo.forNonError(READY));
    Status error = Status.UNKNOWN.withDescription("¯\\_(ツ)_//¯");
    deliverSubchannelState(subchannel, ConnectivityStateInfo.forTransientFailure(error));
    assertThat(subchannelStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
    assertThat(subchannelStateInfo.value.getStatus()).isEqualTo(error);
    inOrder.verify(mockHelper).refreshNameResolution();
    inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
    assertThat(pickerCaptor.getValue()).isInstanceOf(EmptyPicker.class);
    deliverSubchannelState(subchannel, ConnectivityStateInfo.forNonError(IDLE));
    inOrder.verify(mockHelper).refreshNameResolution();
    assertThat(subchannelStateInfo.value.getState()).isEqualTo(TRANSIENT_FAILURE);
    assertThat(subchannelStateInfo.value.getStatus()).isEqualTo(error);
    verify(subchannel, times(2)).requestConnection();
    verify(mockHelper, times(3)).createSubchannel(any(CreateSubchannelArgs.class));
    verifyNoMoreInteractions(mockHelper);
}
Also used : Status(io.grpc.Status) InOrder(org.mockito.InOrder) ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) EmptyPicker(io.grpc.util.RoundRobinLoadBalancer.EmptyPicker) CreateSubchannelArgs(io.grpc.LoadBalancer.CreateSubchannelArgs) Subchannel(io.grpc.LoadBalancer.Subchannel) Test(org.junit.Test)

Example 20 with ConnectivityStateInfo

use of io.grpc.ConnectivityStateInfo in project grpc-java by grpc.

the class GracefulSwitchLoadBalancerTest method handleSubchannelState_shouldThrow.

@Deprecated
@Test
public void handleSubchannelState_shouldThrow() {
    gracefulSwitchLb.switchTo(lbProviders.get(lbPolicies[0]));
    Subchannel subchannel = mock(Subchannel.class);
    ConnectivityStateInfo connectivityStateInfo = ConnectivityStateInfo.forNonError(READY);
    thrown.expect(UnsupportedOperationException.class);
    gracefulSwitchLb.handleSubchannelState(subchannel, connectivityStateInfo);
}
Also used : ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) Subchannel(io.grpc.LoadBalancer.Subchannel) Test(org.junit.Test)

Aggregations

ConnectivityStateInfo (io.grpc.ConnectivityStateInfo)20 Subchannel (io.grpc.LoadBalancer.Subchannel)14 Test (org.junit.Test)10 Attributes (io.grpc.Attributes)9 Status (io.grpc.Status)9 InOrder (org.mockito.InOrder)7 EquivalentAddressGroup (io.grpc.EquivalentAddressGroup)6 ArrayList (java.util.ArrayList)5 CreateSubchannelArgs (io.grpc.LoadBalancer.CreateSubchannelArgs)4 SubchannelStateListener (io.grpc.LoadBalancer.SubchannelStateListener)4 HashMap (java.util.HashMap)4 Map (java.util.Map)3 ConnectivityState (io.grpc.ConnectivityState)2 ResolvedAddresses (io.grpc.LoadBalancer.ResolvedAddresses)2 EmptyPicker (io.grpc.util.RoundRobinLoadBalancer.EmptyPicker)2 EmptyPicker (io.grpc.xds.LeastRequestLoadBalancer.EmptyPicker)2 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 Helper (io.grpc.LoadBalancer.Helper)1 SubchannelPicker (io.grpc.LoadBalancer.SubchannelPicker)1 ManagedChannel (io.grpc.ManagedChannel)1