Search in sources :

Example 36 with Subchannel

use of io.grpc.LoadBalancer.Subchannel in project grpc-java by grpc.

the class GrpclbLoadBalancerTest method pickFirstMode_fallback.

@Test
public void pickFirstMode_fallback() throws Exception {
    InOrder inOrder = inOrder(helper);
    // Name resolver returns balancer and backend addresses
    List<EquivalentAddressGroup> backendList = createResolvedBackendAddresses(2);
    List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
    deliverResolvedAddresses(backendList, grpclbBalancerList, GrpclbConfig.create(Mode.PICK_FIRST));
    // Attempted to connect to balancer
    assertEquals(1, fakeOobChannels.size());
    verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
    StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
    assertEquals(1, lbRequestObservers.size());
    // Fallback timer expires with no response
    fakeClock.forwardTime(GrpclbState.FALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    // Entering fallback mode
    inOrder.verify(helper).createSubchannel(createSubchannelArgsCaptor.capture());
    CreateSubchannelArgs createSubchannelArgs = createSubchannelArgsCaptor.getValue();
    assertThat(createSubchannelArgs.getAddresses()).containsExactly(backendList.get(0), backendList.get(1));
    assertThat(mockSubchannels).hasSize(1);
    Subchannel subchannel = mockSubchannels.poll();
    // Initially IDLE
    inOrder.verify(helper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
    RoundRobinPicker picker0 = (RoundRobinPicker) pickerCaptor.getValue();
    // READY
    deliverSubchannelState(subchannel, 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(subchannel, new TokenAttachingTracerFactory(null)));
    assertThat(picker0.dropList).containsExactly(null, null);
    assertThat(picker0.pickList).containsExactly(new IdleSubchannelEntry(subchannel, syncContext));
    // Finally, an LB response, which brings us out of fallback
    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));
    lbResponseObserver.onNext(buildInitialResponse());
    lbResponseObserver.onNext(buildLbResponse(backends1));
    // new addresses will be updated to the existing subchannel
    // createSubchannel() has ever been called only once
    inOrder.verify(helper, never()).createSubchannel(any(CreateSubchannelArgs.class));
    assertThat(mockSubchannels).isEmpty();
    verify(subchannel).updateAddresses(eq(Arrays.asList(new EquivalentAddressGroup(backends1.get(0).addr, eagAttrsWithToken("token0001")), new EquivalentAddressGroup(backends1.get(1).addr, eagAttrsWithToken("token0002")))));
    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(subchannel, new TokenAttachingTracerFactory(getLoadRecorder())));
    // PICK_FIRST doesn't use subchannelPool
    verify(subchannelPool, never()).takeOrCreateSubchannel(any(EquivalentAddressGroup.class), any(Attributes.class));
    verify(subchannelPool, never()).returnSubchannel(any(Subchannel.class), any(ConnectivityStateInfo.class));
}
Also used : BackendEntry(io.grpc.grpclb.GrpclbState.BackendEntry) InOrder(org.mockito.InOrder) Attributes(io.grpc.Attributes) InitialLoadBalanceResponse(io.grpc.lb.v1.InitialLoadBalanceResponse) LoadBalanceResponse(io.grpc.lb.v1.LoadBalanceResponse) IdleSubchannelEntry(io.grpc.grpclb.GrpclbState.IdleSubchannelEntry) SubchannelPicker(io.grpc.LoadBalancer.SubchannelPicker) RoundRobinPicker(io.grpc.grpclb.GrpclbState.RoundRobinPicker) ConnectivityState(io.grpc.ConnectivityState) ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) CreateSubchannelArgs(io.grpc.LoadBalancer.CreateSubchannelArgs) EquivalentAddressGroup(io.grpc.EquivalentAddressGroup) Subchannel(io.grpc.LoadBalancer.Subchannel) Test(org.junit.Test)

Example 37 with Subchannel

use of io.grpc.LoadBalancer.Subchannel in project grpc-java by grpc.

the class GrpclbLoadBalancerTest method loadReporting.

@Test
public void loadReporting() {
    Metadata headers = new Metadata();
    PickSubchannelArgs args = mock(PickSubchannelArgs.class);
    when(args.getHeaders()).thenReturn(headers);
    long loadReportIntervalMillis = 1983;
    List<EquivalentAddressGroup> grpclbBalancerList = createResolvedBalancerAddresses(1);
    deliverResolvedAddresses(Collections.<EquivalentAddressGroup>emptyList(), grpclbBalancerList);
    // Fallback timer is started as soon as address is resolved.
    assertEquals(1, fakeClock.numPendingTasks(FALLBACK_MODE_TASK_FILTER));
    assertEquals(1, fakeOobChannels.size());
    verify(mockLbService).balanceLoad(lbResponseObserverCaptor.capture());
    StreamObserver<LoadBalanceResponse> lbResponseObserver = lbResponseObserverCaptor.getValue();
    assertEquals(1, lbRequestObservers.size());
    StreamObserver<LoadBalanceRequest> lbRequestObserver = lbRequestObservers.poll();
    InOrder inOrder = inOrder(lbRequestObserver);
    InOrder helperInOrder = inOrder(helper, subchannelPool);
    inOrder.verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
    // Simulate receiving LB response
    assertEquals(0, fakeClock.numPendingTasks(LOAD_REPORTING_TASK_FILTER));
    lbResponseObserver.onNext(buildInitialResponse(loadReportIntervalMillis));
    // Load reporting task is scheduled
    assertEquals(1, fakeClock.numPendingTasks(LOAD_REPORTING_TASK_FILTER));
    assertEquals(0, fakeClock.runDueTasks());
    List<ServerEntry> backends = Arrays.asList(new ServerEntry("127.0.0.1", 2000, "token0001"), // drop
    new ServerEntry("token0001"), new ServerEntry("127.0.0.1", 2010, "token0002"), // drop
    new ServerEntry("token0003"));
    lbResponseObserver.onNext(buildLbResponse(backends));
    assertEquals(2, mockSubchannels.size());
    Subchannel subchannel1 = mockSubchannels.poll();
    Subchannel subchannel2 = mockSubchannels.poll();
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(CONNECTING));
    deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(CONNECTING));
    deliverSubchannelState(subchannel1, ConnectivityStateInfo.forNonError(READY));
    deliverSubchannelState(subchannel2, ConnectivityStateInfo.forNonError(READY));
    helperInOrder.verify(helper, atLeast(1)).updateBalancingState(eq(READY), pickerCaptor.capture());
    RoundRobinPicker picker = (RoundRobinPicker) pickerCaptor.getValue();
    assertThat(picker.dropList).containsExactly(null, new DropEntry(getLoadRecorder(), "token0001"), null, new DropEntry(getLoadRecorder(), "token0003")).inOrder();
    assertThat(picker.pickList).containsExactly(new BackendEntry(subchannel1, getLoadRecorder(), "token0001"), new BackendEntry(subchannel2, getLoadRecorder(), "token0002")).inOrder();
    // Report, no data
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().build());
    PickResult pick1 = picker.pickSubchannel(args);
    assertSame(subchannel1, pick1.getSubchannel());
    assertSame(getLoadRecorder(), pick1.getStreamTracerFactory());
    // Merely the pick will not be recorded as upstart.
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().build());
    ClientStreamTracer tracer1 = pick1.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
    tracer1.streamCreated(Attributes.EMPTY, new Metadata());
    PickResult pick2 = picker.pickSubchannel(args);
    assertNull(pick2.getSubchannel());
    assertSame(DROP_PICK_RESULT, pick2);
    // Report includes upstart of pick1 and the drop of pick2
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(2).setNumCallsFinished(// pick2
    1).addCallsFinishedWithDrop(ClientStatsPerToken.newBuilder().setLoadBalanceToken("token0001").setNumCalls(// pick2
    1).build()).build());
    PickResult pick3 = picker.pickSubchannel(args);
    assertSame(subchannel2, pick3.getSubchannel());
    assertSame(getLoadRecorder(), pick3.getStreamTracerFactory());
    ClientStreamTracer tracer3 = pick3.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
    tracer3.streamCreated(Attributes.EMPTY, new Metadata());
    // pick3 has sent out headers
    tracer3.outboundHeaders();
    // 3rd report includes pick3's upstart
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(1).build());
    PickResult pick4 = picker.pickSubchannel(args);
    assertNull(pick4.getSubchannel());
    assertSame(DROP_PICK_RESULT, pick4);
    // pick1 ended without sending anything
    tracer1.streamClosed(Status.CANCELLED);
    // 4th report includes end of pick1 and drop of pick4
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(// pick4
    1).setNumCallsFinished(2).setNumCallsFinishedWithClientFailedToSend(// pick1
    1).addCallsFinishedWithDrop(ClientStatsPerToken.newBuilder().setLoadBalanceToken("token0003").setNumCalls(// pick4
    1).build()).build());
    PickResult pick5 = picker.pickSubchannel(args);
    assertSame(subchannel1, pick1.getSubchannel());
    assertSame(getLoadRecorder(), pick5.getStreamTracerFactory());
    ClientStreamTracer tracer5 = pick5.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
    tracer5.streamCreated(Attributes.EMPTY, new Metadata());
    // pick3 ended without receiving response headers
    tracer3.streamClosed(Status.DEADLINE_EXCEEDED);
    // pick5 sent and received headers
    tracer5.outboundHeaders();
    tracer5.inboundHeaders();
    // 5th report includes pick3's end and pick5's upstart
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(// pick5
    1).setNumCallsFinished(// pick3
    1).build());
    // pick5 ends
    tracer5.streamClosed(Status.OK);
    // 6th report includes pick5's end
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsFinished(1).setNumCallsFinishedKnownReceived(1).build());
    assertEquals(1, fakeClock.numPendingTasks());
    // Balancer closes the stream, scheduled reporting task cancelled
    lbResponseObserver.onError(Status.UNAVAILABLE.asException());
    assertEquals(0, fakeClock.numPendingTasks());
    // New stream created
    verify(mockLbService, times(2)).balanceLoad(lbResponseObserverCaptor.capture());
    lbResponseObserver = lbResponseObserverCaptor.getValue();
    assertEquals(1, lbRequestObservers.size());
    lbRequestObserver = lbRequestObservers.poll();
    inOrder = inOrder(lbRequestObserver);
    inOrder.verify(lbRequestObserver).onNext(eq(LoadBalanceRequest.newBuilder().setInitialRequest(InitialLoadBalanceRequest.newBuilder().setName(SERVICE_AUTHORITY).build()).build()));
    // Load reporting is also requested
    lbResponseObserver.onNext(buildInitialResponse(loadReportIntervalMillis));
    // No picker created because balancer is still using the results from the last stream
    helperInOrder.verify(helper, never()).updateBalancingState(any(ConnectivityState.class), any(SubchannelPicker.class));
    // Make a new pick on that picker.  It will not show up on the report of the new stream, because
    // that picker is associated with the previous stream.
    PickResult pick6 = picker.pickSubchannel(args);
    assertNull(pick6.getSubchannel());
    assertSame(DROP_PICK_RESULT, pick6);
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().build());
    // New stream got the list update
    lbResponseObserver.onNext(buildLbResponse(backends));
    // Same backends, thus no new subchannels
    helperInOrder.verify(subchannelPool, never()).takeOrCreateSubchannel(any(EquivalentAddressGroup.class), any(Attributes.class));
    // But the new RoundRobinEntries have a new loadRecorder, thus considered different from
    // the previous list, thus a new picker is created
    helperInOrder.verify(helper).updateBalancingState(eq(READY), pickerCaptor.capture());
    picker = (RoundRobinPicker) pickerCaptor.getValue();
    PickResult pick1p = picker.pickSubchannel(args);
    assertSame(subchannel1, pick1p.getSubchannel());
    assertSame(getLoadRecorder(), pick1p.getStreamTracerFactory());
    pick1p.getStreamTracerFactory().newClientStreamTracer(STREAM_INFO, new Metadata());
    // The pick from the new stream will be included in the report
    assertNextReport(inOrder, lbRequestObserver, loadReportIntervalMillis, ClientStats.newBuilder().setNumCallsStarted(1).build());
    verify(args, atLeast(0)).getHeaders();
    verifyNoMoreInteractions(args);
}
Also used : BackendEntry(io.grpc.grpclb.GrpclbState.BackendEntry) ClientStreamTracer(io.grpc.ClientStreamTracer) InOrder(org.mockito.InOrder) DropEntry(io.grpc.grpclb.GrpclbState.DropEntry) Metadata(io.grpc.Metadata) Attributes(io.grpc.Attributes) InitialLoadBalanceResponse(io.grpc.lb.v1.InitialLoadBalanceResponse) LoadBalanceResponse(io.grpc.lb.v1.LoadBalanceResponse) SubchannelPicker(io.grpc.LoadBalancer.SubchannelPicker) RoundRobinPicker(io.grpc.grpclb.GrpclbState.RoundRobinPicker) ConnectivityState(io.grpc.ConnectivityState) EquivalentAddressGroup(io.grpc.EquivalentAddressGroup) Subchannel(io.grpc.LoadBalancer.Subchannel) LoadBalanceRequest(io.grpc.lb.v1.LoadBalanceRequest) InitialLoadBalanceRequest(io.grpc.lb.v1.InitialLoadBalanceRequest) PickResult(io.grpc.LoadBalancer.PickResult) PickSubchannelArgs(io.grpc.LoadBalancer.PickSubchannelArgs) Test(org.junit.Test)

Example 38 with Subchannel

use of io.grpc.LoadBalancer.Subchannel in project grpc-java by grpc.

the class CachedSubchannelPool method takeOrCreateSubchannel.

@Override
public Subchannel takeOrCreateSubchannel(EquivalentAddressGroup eag, Attributes defaultAttributes) {
    final CacheEntry entry = cache.remove(eag);
    final Subchannel subchannel;
    if (entry == null) {
        subchannel = helper.createSubchannel(CreateSubchannelArgs.newBuilder().setAddresses(eag).setAttributes(defaultAttributes).build());
        subchannel.start(new SubchannelStateListener() {

            @Override
            public void onSubchannelState(ConnectivityStateInfo newState) {
                updateCachedSubchannelState(subchannel, newState);
                listener.onSubchannelState(subchannel, newState);
            }
        });
    } else {
        subchannel = entry.subchannel;
        entry.shutdownTimer.cancel();
        // Make the balancer up-to-date with the latest state in case it has changed while it's
        // in the cache.
        helper.getSynchronizationContext().execute(new Runnable() {

            @Override
            public void run() {
                listener.onSubchannelState(subchannel, entry.state);
            }
        });
    }
    return subchannel;
}
Also used : SubchannelStateListener(io.grpc.LoadBalancer.SubchannelStateListener) ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) Subchannel(io.grpc.LoadBalancer.Subchannel)

Example 39 with Subchannel

use of io.grpc.LoadBalancer.Subchannel in project grpc-java by grpc.

the class GrpclbState method updateServerList.

/**
 * Populate backend servers to be used based on the given list of addresses.
 */
private void updateServerList(List<DropEntry> newDropList, List<BackendAddressGroup> newBackendAddrList, @Nullable GrpclbClientLoadRecorder loadRecorder) {
    HashMap<List<EquivalentAddressGroup>, Subchannel> newSubchannelMap = new HashMap<>();
    List<BackendEntry> newBackendList = new ArrayList<>();
    switch(config.getMode()) {
        case ROUND_ROBIN:
            for (BackendAddressGroup backendAddr : newBackendAddrList) {
                EquivalentAddressGroup eag = backendAddr.getAddresses();
                List<EquivalentAddressGroup> eagAsList = Collections.singletonList(eag);
                Subchannel subchannel = newSubchannelMap.get(eagAsList);
                if (subchannel == null) {
                    subchannel = subchannels.get(eagAsList);
                    if (subchannel == null) {
                        subchannel = subchannelPool.takeOrCreateSubchannel(eag, createSubchannelAttrs());
                        subchannel.requestConnection();
                    }
                    newSubchannelMap.put(eagAsList, subchannel);
                }
                BackendEntry entry;
                // Only picks with tokens are reported to LoadRecorder
                if (backendAddr.getToken() == null) {
                    entry = new BackendEntry(subchannel);
                } else {
                    entry = new BackendEntry(subchannel, loadRecorder, backendAddr.getToken());
                }
                newBackendList.add(entry);
            }
            // Close Subchannels whose addresses have been delisted
            for (Map.Entry<List<EquivalentAddressGroup>, Subchannel> entry : subchannels.entrySet()) {
                List<EquivalentAddressGroup> eagList = entry.getKey();
                if (!newSubchannelMap.containsKey(eagList)) {
                    returnSubchannelToPool(entry.getValue());
                }
            }
            subchannels = Collections.unmodifiableMap(newSubchannelMap);
            break;
        case PICK_FIRST:
            checkState(subchannels.size() <= 1, "Unexpected Subchannel count: %s", subchannels);
            final Subchannel subchannel;
            if (newBackendAddrList.isEmpty()) {
                if (subchannels.size() == 1) {
                    subchannel = subchannels.values().iterator().next();
                    subchannel.shutdown();
                    subchannels = Collections.emptyMap();
                }
                break;
            }
            List<EquivalentAddressGroup> eagList = new ArrayList<>();
            // The PICK_FIRST code path doesn't cache Subchannels.
            for (BackendAddressGroup bag : newBackendAddrList) {
                EquivalentAddressGroup origEag = bag.getAddresses();
                Attributes eagAttrs = origEag.getAttributes();
                if (bag.getToken() != null) {
                    eagAttrs = eagAttrs.toBuilder().set(GrpclbConstants.TOKEN_ATTRIBUTE_KEY, bag.getToken()).build();
                }
                eagList.add(new EquivalentAddressGroup(origEag.getAddresses(), eagAttrs));
            }
            if (subchannels.isEmpty()) {
                subchannel = helper.createSubchannel(CreateSubchannelArgs.newBuilder().setAddresses(eagList).setAttributes(createSubchannelAttrs()).build());
                subchannel.start(new SubchannelStateListener() {

                    @Override
                    public void onSubchannelState(ConnectivityStateInfo newState) {
                        handleSubchannelState(subchannel, newState);
                    }
                });
                if (requestConnectionPending) {
                    subchannel.requestConnection();
                    requestConnectionPending = false;
                }
            } else {
                subchannel = subchannels.values().iterator().next();
                subchannel.updateAddresses(eagList);
            }
            subchannels = Collections.singletonMap(eagList, subchannel);
            newBackendList.add(new BackendEntry(subchannel, new TokenAttachingTracerFactory(loadRecorder)));
            break;
        default:
            throw new AssertionError("Missing case for " + config.getMode());
    }
    dropList = Collections.unmodifiableList(newDropList);
    backendList = Collections.unmodifiableList(newBackendList);
}
Also used : SubchannelStateListener(io.grpc.LoadBalancer.SubchannelStateListener) PooledSubchannelStateListener(io.grpc.grpclb.SubchannelPool.PooledSubchannelStateListener) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Attributes(io.grpc.Attributes) ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) Subchannel(io.grpc.LoadBalancer.Subchannel) EquivalentAddressGroup(io.grpc.EquivalentAddressGroup) ServerList(io.grpc.lb.v1.ServerList) List(java.util.List) ArrayList(java.util.ArrayList) Map(java.util.Map) HashMap(java.util.HashMap)

Example 40 with Subchannel

use of io.grpc.LoadBalancer.Subchannel in project grpc-java by grpc.

the class CachedSubchannelPoolTest method wrapUp.

@After
public void wrapUp() {
    if (mockSubchannels.isEmpty()) {
        return;
    }
    // Sanity checks
    for (Subchannel subchannel : mockSubchannels) {
        verify(subchannel, atMost(1)).shutdown();
    }
    verify(listener, atLeast(0)).onSubchannelState(any(Subchannel.class), any(ConnectivityStateInfo.class));
    verifyNoMoreInteractions(listener);
}
Also used : ConnectivityStateInfo(io.grpc.ConnectivityStateInfo) Subchannel(io.grpc.LoadBalancer.Subchannel) After(org.junit.After)

Aggregations

Subchannel (io.grpc.LoadBalancer.Subchannel)125 Test (org.junit.Test)108 EquivalentAddressGroup (io.grpc.EquivalentAddressGroup)54 InOrder (org.mockito.InOrder)43 PickSubchannelArgs (io.grpc.LoadBalancer.PickSubchannelArgs)42 Metadata (io.grpc.Metadata)40 SubchannelPicker (io.grpc.LoadBalancer.SubchannelPicker)35 CreateSubchannelArgs (io.grpc.LoadBalancer.CreateSubchannelArgs)33 ForwardingSubchannel (io.grpc.util.ForwardingSubchannel)32 MockClientTransportInfo (io.grpc.internal.TestUtils.MockClientTransportInfo)30 Attributes (io.grpc.Attributes)26 ConnectivityStateInfo (io.grpc.ConnectivityStateInfo)25 ClientStreamTracer (io.grpc.ClientStreamTracer)24 Status (io.grpc.Status)23 PickResult (io.grpc.LoadBalancer.PickResult)19 CallOptions (io.grpc.CallOptions)18 ConnectivityState (io.grpc.ConnectivityState)18 Helper (io.grpc.LoadBalancer.Helper)18 SocketAddress (java.net.SocketAddress)18 ChannelLogger (io.grpc.ChannelLogger)14