use of io.grpc.LoadBalancer.SubchannelStateListener in project grpc-java by grpc.
the class PickFirstLoadBalancerTest method requestConnectionPicker.
@Test
public void requestConnectionPicker() throws Exception {
loadBalancer.handleResolvedAddresses(ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(affinity).build());
InOrder inOrder = inOrder(mockHelper, mockSubchannel);
inOrder.verify(mockSubchannel).start(stateListenerCaptor.capture());
SubchannelStateListener stateListener = stateListenerCaptor.getValue();
inOrder.verify(mockHelper).updateBalancingState(eq(CONNECTING), any(SubchannelPicker.class));
inOrder.verify(mockSubchannel).requestConnection();
stateListener.onSubchannelState(ConnectivityStateInfo.forNonError(IDLE));
inOrder.verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
SubchannelPicker picker = pickerCaptor.getValue();
// Calling pickSubchannel() twice gave the same result
assertEquals(picker.pickSubchannel(mockArgs), picker.pickSubchannel(mockArgs));
// But the picker calls requestConnection() only once
inOrder.verify(mockSubchannel).requestConnection();
verify(mockSubchannel, times(2)).requestConnection();
}
use of io.grpc.LoadBalancer.SubchannelStateListener in project grpc-java by grpc.
the class PickFirstLoadBalancerTest method pickAfterStateChangeAfterResolution.
@Test
public void pickAfterStateChangeAfterResolution() throws Exception {
InOrder inOrder = inOrder(mockHelper);
loadBalancer.handleResolvedAddresses(ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(affinity).build());
inOrder.verify(mockHelper).createSubchannel(createArgsCaptor.capture());
CreateSubchannelArgs args = createArgsCaptor.getValue();
assertThat(args.getAddresses()).isEqualTo(servers);
verify(mockSubchannel).start(stateListenerCaptor.capture());
SubchannelStateListener stateListener = stateListenerCaptor.getValue();
verify(mockHelper).updateBalancingState(eq(CONNECTING), pickerCaptor.capture());
Subchannel subchannel = pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel();
reset(mockHelper);
when(mockHelper.getSynchronizationContext()).thenReturn(syncContext);
Status error = Status.UNAVAILABLE.withDescription("boom!");
stateListener.onSubchannelState(ConnectivityStateInfo.forTransientFailure(error));
inOrder.verify(mockHelper).refreshNameResolution();
inOrder.verify(mockHelper).updateBalancingState(eq(TRANSIENT_FAILURE), pickerCaptor.capture());
assertEquals(error, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
stateListener.onSubchannelState(ConnectivityStateInfo.forNonError(IDLE));
inOrder.verify(mockHelper).refreshNameResolution();
inOrder.verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
assertEquals(Status.OK, pickerCaptor.getValue().pickSubchannel(mockArgs).getStatus());
stateListener.onSubchannelState(ConnectivityStateInfo.forNonError(READY));
inOrder.verify(mockHelper).updateBalancingState(eq(READY), pickerCaptor.capture());
assertEquals(subchannel, pickerCaptor.getValue().pickSubchannel(mockArgs).getSubchannel());
// Don't care
verify(mockHelper, atLeast(0)).getSynchronizationContext();
verifyNoMoreInteractions(mockHelper);
}
use of io.grpc.LoadBalancer.SubchannelStateListener 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.SubchannelStateListener 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.SubchannelStateListener in project grpc-java by grpc.
the class HealthCheckingLoadBalancerFactoryTest method serviceConfigChangesServiceNameWhenRpcActive.
@Test
public void serviceConfigChangesServiceNameWhenRpcActive() {
Attributes resolutionAttrs = attrsWithHealthCheckService("TeeService");
ResolvedAddresses result1 = ResolvedAddresses.newBuilder().setAddresses(resolvedAddressList).setAttributes(resolutionAttrs).build();
hcLbEventDelivery.handleResolvedAddresses(result1);
verify(origLb).handleResolvedAddresses(result1);
verifyNoMoreInteractions(origLb);
Subchannel subchannel = createSubchannel(0, Attributes.EMPTY);
SubchannelStateListener mockListener = mockStateListeners[0];
assertThat(unwrap(subchannel)).isSameInstanceAs(subchannels[0]);
InOrder inOrder = inOrder(origLb, mockListener);
deliverSubchannelState(0, ConnectivityStateInfo.forNonError(READY));
inOrder.verify(mockListener).onSubchannelState(eq(ConnectivityStateInfo.forNonError(CONNECTING)));
HealthImpl healthImpl = healthImpls[0];
assertThat(healthImpl.calls).hasSize(1);
ServerSideCall serverCall = healthImpl.calls.poll();
assertThat(serverCall.cancelled).isFalse();
assertThat(serverCall.request).isEqualTo(makeRequest("TeeService"));
// Health check responded
serverCall.responseObserver.onNext(makeResponse(ServingStatus.SERVING));
inOrder.verify(mockListener).onSubchannelState(eq(ConnectivityStateInfo.forNonError(READY)));
// Service config returns with the same health check name.
hcLbEventDelivery.handleResolvedAddresses(result1);
// It's delivered to origLb, but nothing else happens
inOrder.verify(origLb).handleResolvedAddresses(result1);
verifyNoMoreInteractions(origLb, mockListener);
// Service config returns a different health check name.
resolutionAttrs = attrsWithHealthCheckService("FooService");
ResolvedAddresses result2 = ResolvedAddresses.newBuilder().setAddresses(resolvedAddressList).setAttributes(resolutionAttrs).build();
hcLbEventDelivery.handleResolvedAddresses(result2);
inOrder.verify(origLb).handleResolvedAddresses(result2);
// Current health check RPC cancelled.
assertThat(serverCall.cancelled).isTrue();
// A second RPC is started immediately
assertThat(healthImpl.calls).hasSize(1);
serverCall = healthImpl.calls.poll();
// with the new service name
assertThat(serverCall.request).isEqualTo(makeRequest("FooService"));
// State stays in READY, instead of switching to CONNECTING.
verifyNoMoreInteractions(origLb, mockListener);
}
Aggregations