use of io.grpc.ResolvedServerInfoGroup in project grpc-java by grpc.
the class ManagedChannelImplTest method informationPropagatedToNewStreamAndCallCredentials.
/**
* Test that information such as the Call's context, MethodDescriptor, authority, executor are
* propagated to newStream() and applyRequestMetadata().
*/
@Test
public void informationPropagatedToNewStreamAndCallCredentials() {
ResolvedServerInfoGroup serverInfoGroup = ResolvedServerInfoGroup.builder().add(server).build();
createChannel(new FakeNameResolverFactory(true), NO_INTERCEPTOR);
CallOptions callOptions = CallOptions.DEFAULT.withCallCredentials(creds);
final Context.Key<String> testKey = Context.key("testing");
Context ctx = Context.current().withValue(testKey, "testValue");
final LinkedList<Context> credsApplyContexts = new LinkedList<Context>();
final LinkedList<Context> newStreamContexts = new LinkedList<Context>();
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock in) throws Throwable {
credsApplyContexts.add(Context.current());
return null;
}
}).when(creds).applyRequestMetadata(any(MethodDescriptor.class), any(Attributes.class), any(Executor.class), any(MetadataApplier.class));
// First call will be on delayed transport. Only newCall() is run within the expected context,
// so that we can verify that the context is explicitly attached before calling newStream() and
// applyRequestMetadata(), which happens after we detach the context from the thread.
Context origCtx = ctx.attach();
assertEquals("testValue", testKey.get());
ClientCall<String, Integer> call = channel.newCall(method, callOptions);
ctx.detach(origCtx);
assertNull(testKey.get());
call.start(mockCallListener, new Metadata());
// Simulate name resolution results
Subchannel subchannel = helper.createSubchannel(serverInfoGroup.toEquivalentAddressGroup(), Attributes.EMPTY);
subchannel.requestConnection();
verify(mockTransportFactory).newClientTransport(same(socketAddress), eq(authority), eq(userAgent));
MockClientTransportInfo transportInfo = transports.poll();
final ConnectionClientTransport transport = transportInfo.transport;
when(transport.getAttributes()).thenReturn(Attributes.EMPTY);
doAnswer(new Answer<ClientStream>() {
@Override
public ClientStream answer(InvocationOnMock in) throws Throwable {
newStreamContexts.add(Context.current());
return mock(ClientStream.class);
}
}).when(transport).newStream(any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), any(StatsTraceContext.class));
verify(creds, never()).applyRequestMetadata(any(MethodDescriptor.class), any(Attributes.class), any(Executor.class), any(MetadataApplier.class));
// applyRequestMetadata() is called after the transport becomes ready.
transportInfo.listener.transportReady();
when(mockPicker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(PickResult.withSubchannel(subchannel));
helper.updatePicker(mockPicker);
executor.runDueTasks();
ArgumentCaptor<Attributes> attrsCaptor = ArgumentCaptor.forClass(Attributes.class);
ArgumentCaptor<MetadataApplier> applierCaptor = ArgumentCaptor.forClass(MetadataApplier.class);
verify(creds).applyRequestMetadata(same(method), attrsCaptor.capture(), same(executor.getScheduledExecutorService()), applierCaptor.capture());
assertEquals("testValue", testKey.get(credsApplyContexts.poll()));
assertEquals(authority, attrsCaptor.getValue().get(CallCredentials.ATTR_AUTHORITY));
assertEquals(SecurityLevel.NONE, attrsCaptor.getValue().get(CallCredentials.ATTR_SECURITY_LEVEL));
verify(transport, never()).newStream(any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), any(StatsTraceContext.class));
// newStream() is called after apply() is called
applierCaptor.getValue().apply(new Metadata());
verify(transport).newStream(same(method), any(Metadata.class), same(callOptions), any(StatsTraceContext.class));
assertEquals("testValue", testKey.get(newStreamContexts.poll()));
// The context should not live beyond the scope of newStream() and applyRequestMetadata()
assertNull(testKey.get());
// Second call will not be on delayed transport
origCtx = ctx.attach();
call = channel.newCall(method, callOptions);
ctx.detach(origCtx);
call.start(mockCallListener, new Metadata());
verify(creds, times(2)).applyRequestMetadata(same(method), attrsCaptor.capture(), same(executor.getScheduledExecutorService()), applierCaptor.capture());
assertEquals("testValue", testKey.get(credsApplyContexts.poll()));
assertEquals(authority, attrsCaptor.getValue().get(CallCredentials.ATTR_AUTHORITY));
assertEquals(SecurityLevel.NONE, attrsCaptor.getValue().get(CallCredentials.ATTR_SECURITY_LEVEL));
// This is from the first call
verify(transport).newStream(any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), any(StatsTraceContext.class));
// Still, newStream() is called after apply() is called
applierCaptor.getValue().apply(new Metadata());
verify(transport, times(2)).newStream(same(method), any(Metadata.class), same(callOptions), any(StatsTraceContext.class));
assertEquals("testValue", testKey.get(newStreamContexts.poll()));
assertNull(testKey.get());
}
use of io.grpc.ResolvedServerInfoGroup in project grpc-java by grpc.
the class ManagedChannelImplTest method firstResolvedServerFailedToConnect.
/**
* Verify that if the first resolved address points to a server that cannot be connected, the call
* will end up with the second address which works.
*/
@Test
public void firstResolvedServerFailedToConnect() throws Exception {
final SocketAddress goodAddress = new SocketAddress() {
@Override
public String toString() {
return "goodAddress";
}
};
final SocketAddress badAddress = new SocketAddress() {
@Override
public String toString() {
return "badAddress";
}
};
final ResolvedServerInfo goodServer = new ResolvedServerInfo(goodAddress, Attributes.EMPTY);
final ResolvedServerInfo badServer = new ResolvedServerInfo(badAddress, Attributes.EMPTY);
InOrder inOrder = inOrder(mockLoadBalancer);
ResolvedServerInfoGroup serverInfoGroup = ResolvedServerInfoGroup.builder().add(badServer).add(goodServer).build();
FakeNameResolverFactory nameResolverFactory = new FakeNameResolverFactory(serverInfoGroup.getResolvedServerInfoList());
createChannel(nameResolverFactory, NO_INTERCEPTOR);
// Start the call
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT);
Metadata headers = new Metadata();
call.start(mockCallListener, headers);
executor.runDueTasks();
// Simulate name resolution results
inOrder.verify(mockLoadBalancer).handleResolvedAddresses(eq(Arrays.asList(serverInfoGroup)), eq(Attributes.EMPTY));
Subchannel subchannel = helper.createSubchannel(serverInfoGroup.toEquivalentAddressGroup(), Attributes.EMPTY);
when(mockPicker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(PickResult.withSubchannel(subchannel));
subchannel.requestConnection();
inOrder.verify(mockLoadBalancer).handleSubchannelState(same(subchannel), stateInfoCaptor.capture());
assertEquals(CONNECTING, stateInfoCaptor.getValue().getState());
// The channel will starts with the first address (badAddress)
verify(mockTransportFactory).newClientTransport(same(badAddress), any(String.class), any(String.class));
verify(mockTransportFactory, times(0)).newClientTransport(same(goodAddress), any(String.class), any(String.class));
MockClientTransportInfo badTransportInfo = transports.poll();
// Which failed to connect
badTransportInfo.listener.transportShutdown(Status.UNAVAILABLE);
inOrder.verifyNoMoreInteractions();
// The channel then try the second address (goodAddress)
verify(mockTransportFactory).newClientTransport(same(goodAddress), any(String.class), any(String.class));
MockClientTransportInfo goodTransportInfo = transports.poll();
when(goodTransportInfo.transport.newStream(any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), any(StatsTraceContext.class))).thenReturn(mock(ClientStream.class));
goodTransportInfo.listener.transportReady();
inOrder.verify(mockLoadBalancer).handleSubchannelState(same(subchannel), stateInfoCaptor.capture());
assertEquals(READY, stateInfoCaptor.getValue().getState());
// A typical LoadBalancer will call this once the subchannel becomes READY
helper.updatePicker(mockPicker);
// Delayed transport uses the app executor to create real streams.
executor.runDueTasks();
verify(goodTransportInfo.transport).newStream(same(method), same(headers), same(CallOptions.DEFAULT), any(StatsTraceContext.class));
// The bad transport was never used.
verify(badTransportInfo.transport, times(0)).newStream(any(MethodDescriptor.class), any(Metadata.class), any(CallOptions.class), any(StatsTraceContext.class));
}
use of io.grpc.ResolvedServerInfoGroup in project grpc-java by grpc.
the class ManagedChannelImplTest method loadBalancerThrowsInHandleResolvedAddresses.
@Test
public void loadBalancerThrowsInHandleResolvedAddresses() {
RuntimeException ex = new RuntimeException("simulated");
// Delay the success of name resolution until allResolved() is called
FakeNameResolverFactory nameResolverFactory = new FakeNameResolverFactory(false);
createChannel(nameResolverFactory, NO_INTERCEPTOR);
verify(mockLoadBalancerFactory).newLoadBalancer(any(Helper.class));
doThrow(ex).when(mockLoadBalancer).handleResolvedAddresses(Matchers.<List<ResolvedServerInfoGroup>>anyObject(), any(Attributes.class));
// NameResolver returns addresses.
nameResolverFactory.allResolved();
// The LoadBalancer will receive the error that it has thrown.
verify(mockLoadBalancer).handleNameResolutionError(statusCaptor.capture());
Status status = statusCaptor.getValue();
assertSame(Status.Code.INTERNAL, status.getCode());
assertSame(ex, status.getCause());
}
use of io.grpc.ResolvedServerInfoGroup in project grpc-java by grpc.
the class ManagedChannelImplTest method allServersFailedToConnect.
/**
* Verify that if all resolved addresses failed to connect, a fail-fast call will fail, while a
* wait-for-ready call will still be buffered.
*/
@Test
public void allServersFailedToConnect() throws Exception {
final SocketAddress addr1 = new SocketAddress() {
@Override
public String toString() {
return "addr1";
}
};
final SocketAddress addr2 = new SocketAddress() {
@Override
public String toString() {
return "addr2";
}
};
final ResolvedServerInfo server1 = new ResolvedServerInfo(addr1, Attributes.EMPTY);
final ResolvedServerInfo server2 = new ResolvedServerInfo(addr2, Attributes.EMPTY);
InOrder inOrder = inOrder(mockLoadBalancer);
ResolvedServerInfoGroup serverInfoGroup = ResolvedServerInfoGroup.builder().add(server1).add(server2).build();
FakeNameResolverFactory nameResolverFactory = new FakeNameResolverFactory(serverInfoGroup.getResolvedServerInfoList());
createChannel(nameResolverFactory, NO_INTERCEPTOR);
// Start a wait-for-ready call
ClientCall<String, Integer> call = channel.newCall(method, CallOptions.DEFAULT.withWaitForReady());
Metadata headers = new Metadata();
call.start(mockCallListener, headers);
// ... and a fail-fast call
ClientCall<String, Integer> call2 = channel.newCall(method, CallOptions.DEFAULT.withoutWaitForReady());
call2.start(mockCallListener2, headers);
executor.runDueTasks();
// Simulate name resolution results
inOrder.verify(mockLoadBalancer).handleResolvedAddresses(eq(Arrays.asList(serverInfoGroup)), eq(Attributes.EMPTY));
Subchannel subchannel = helper.createSubchannel(serverInfoGroup.toEquivalentAddressGroup(), Attributes.EMPTY);
when(mockPicker.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(PickResult.withSubchannel(subchannel));
subchannel.requestConnection();
inOrder.verify(mockLoadBalancer).handleSubchannelState(same(subchannel), stateInfoCaptor.capture());
assertEquals(CONNECTING, stateInfoCaptor.getValue().getState());
// Connecting to server1, which will fail
verify(mockTransportFactory).newClientTransport(same(addr1), any(String.class), any(String.class));
verify(mockTransportFactory, times(0)).newClientTransport(same(addr2), any(String.class), any(String.class));
MockClientTransportInfo transportInfo1 = transports.poll();
transportInfo1.listener.transportShutdown(Status.UNAVAILABLE);
// Connecting to server2, which will fail too
verify(mockTransportFactory).newClientTransport(same(addr2), any(String.class), any(String.class));
MockClientTransportInfo transportInfo2 = transports.poll();
Status server2Error = Status.UNAVAILABLE.withDescription("Server2 failed to connect");
transportInfo2.listener.transportShutdown(server2Error);
// ... which makes the subchannel enter TRANSIENT_FAILURE. The last error Status is propagated
// to LoadBalancer.
inOrder.verify(mockLoadBalancer).handleSubchannelState(same(subchannel), stateInfoCaptor.capture());
assertEquals(TRANSIENT_FAILURE, stateInfoCaptor.getValue().getState());
assertSame(server2Error, stateInfoCaptor.getValue().getStatus());
// A typical LoadBalancer would create a picker with error
SubchannelPicker picker2 = mock(SubchannelPicker.class);
when(picker2.pickSubchannel(any(PickSubchannelArgs.class))).thenReturn(PickResult.withError(server2Error));
helper.updatePicker(picker2);
executor.runDueTasks();
// ... which fails the fail-fast call
verify(mockCallListener2).onClose(same(server2Error), any(Metadata.class));
// ... while the wait-for-ready call stays
verifyNoMoreInteractions(mockCallListener);
// No real stream was ever created
verify(transportInfo1.transport, times(0)).newStream(any(MethodDescriptor.class), any(Metadata.class));
verify(transportInfo2.transport, times(0)).newStream(any(MethodDescriptor.class), any(Metadata.class));
}
use of io.grpc.ResolvedServerInfoGroup in project grpc-java by grpc.
the class GrpclbLoadBalancerTest method delegatingPickFirstThenNameResolutionFails.
@Test
public void delegatingPickFirstThenNameResolutionFails() {
List<ResolvedServerInfoGroup> resolvedServers = createResolvedServerInfoGroupList(false);
Attributes resolutionAttrs = Attributes.newBuilder().set(RESOLUTION_ATTR, "yeah").build();
deliverResolvedAddresses(resolvedServers, resolutionAttrs);
verify(pickFirstBalancerFactory).newLoadBalancer(helper);
verify(pickFirstBalancer).handleResolvedAddresses(eq(resolvedServers), eq(resolutionAttrs));
// Then let name resolution fail. The error will be passed directly to the delegate.
Status error = Status.NOT_FOUND.withDescription("www.google.com not found");
deliverNameResolutionError(error);
verify(pickFirstBalancer).handleNameResolutionError(error);
verify(helper, never()).updatePicker(any(SubchannelPicker.class));
verifyNoMoreInteractions(roundRobinBalancerFactory);
verifyNoMoreInteractions(roundRobinBalancer);
}
Aggregations