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));
}
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);
}
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;
}
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);
}
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);
}
Aggregations