public async Task Resolver_SubchannelTransientFailure_ResolverRefreshed() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); SyncPoint?syncPoint = new SyncPoint(runContinuationsAsynchronously: true); syncPoint.Continue(); var resolver = new TestResolver(async() => { await syncPoint.WaitToContinue().DefaultTimeout(); syncPoint = new SyncPoint(runContinuationsAsynchronously: true); }); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port), new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), resolver, connect : true); await BalancerHelpers.WaitForSubChannelsToBeReadyAsync(Logger, channel, 2).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint1.Method); var waitForRefreshTask = syncPoint.WaitForSyncPoint(); endpoint1.Dispose(); await waitForRefreshTask.DefaultTimeout(); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); syncPoint.Continue(); await BalancerHelpers.WaitForSubChannelsToBeReadyAsync(Logger, channel, 1).DefaultTimeout(); }
public async Task Subchannel_ResolveRemovesSubchannel_SubchannelCleanedUp() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); var resolver = new TestResolver(LoggerFactory); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port) }); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), resolver, connect : true); var disposedSubchannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); Assert.IsNotNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); Assert.IsNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); }
public async Task Resolver_ResolveNameFromServices_Success() { // Arrange var services = new ServiceCollection(); var resolver = new TestResolver(); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 80) }); services.AddSingleton <ResolverFactory>(new TestResolverFactory(resolver)); services.AddSingleton <ISubchannelTransportFactory>(new TestSubchannelTransportFactory()); var handler = new TestHttpMessageHandler((r, ct) => default !);
public async Task ResolverReturnsNoAddresses_CallWithWaitForReady_Wait() { // Arrange string?authority = null; var testMessageHandler = TestHttpMessageHandler.Create(async request => { authority = request.RequestUri !.Authority; var reply = new HelloReply { Message = "Hello world" }; var streamContent = await ClientTestHelpers.CreateResponseContent(reply).DefaultTimeout(); return(ResponseUtils.CreateResponse(HttpStatusCode.OK, streamContent)); }); var services = new ServiceCollection(); var resolver = new TestResolver(); services.AddSingleton <ResolverFactory>(new TestResolverFactory(resolver)); services.AddSingleton <ISubchannelTransportFactory>(new TestSubchannelTransportFactory()); var invoker = HttpClientCallInvokerFactory.Create(testMessageHandler, "test:///localhost", configure: o => { o.Credentials = ChannelCredentials.Insecure; o.ServiceProvider = services.BuildServiceProvider(); }); // Act var call = invoker.AsyncUnaryCall <HelloRequest, HelloReply>(ClientTestHelpers.ServiceMethod, string.Empty, new CallOptions().WithWaitForReady(), new HelloRequest()); var responseTask = call.ResponseAsync; Assert.IsFalse(responseTask.IsCompleted); Assert.IsNull(authority); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 81) }); await responseTask.DefaultTimeout(); Assert.AreEqual("localhost:81", authority); }
public async Task ChangeAddresses_HasReadySubchannel_OldSubchannelShutdown() { // Arrange var services = new ServiceCollection(); services.AddNUnitLogger(); var resolver = new TestResolver(); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 80) }); services.AddSingleton <ResolverFactory>(new TestResolverFactory(resolver)); services.AddSingleton <ISubchannelTransportFactory>(new TestSubchannelTransportFactory()); var handler = new TestHttpMessageHandler((r, ct) => default !);
public async Task PickAsync_WaitForReadyWithDrop_ThrowsError() { // Arrange var services = new ServiceCollection(); services.AddNUnitLogger(); var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService <ILoggerFactory>(); var resolver = new TestResolver(loggerFactory); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 80) }); var transportFactory = new TestSubchannelTransportFactory(); var clientChannel = new ConnectionManager(resolver, disableResolverServiceConfig: false, loggerFactory, transportFactory, Array.Empty <LoadBalancerFactory>()); clientChannel.ConfigureBalancer(c => new DropLoadBalancer(c)); // Act _ = clientChannel.ConnectAsync(waitForReady: true, CancellationToken.None).ConfigureAwait(false); var pickTask = clientChannel.PickAsync( new PickContext { Request = new HttpRequestMessage() }, waitForReady: true, CancellationToken.None).AsTask(); // Assert var ex = await ExceptionAssert.ThrowsAsync <RpcException>(() => pickTask).DefaultTimeout(); Assert.AreEqual(StatusCode.DataLoss, ex.StatusCode); }
public async Task PickAsync_ChannelStateChangesWithWaitForReady_WaitsForCorrectEndpoint() { // Arrange var services = new ServiceCollection(); services.AddNUnitLogger(); var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService <ILoggerFactory>(); var resolver = new TestResolver(loggerFactory); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 80) }); var transportFactory = new TestSubchannelTransportFactory(); var clientChannel = new ConnectionManager(resolver, disableResolverServiceConfig: false, loggerFactory, transportFactory, Array.Empty <LoadBalancerFactory>()); clientChannel.ConfigureBalancer(c => new RoundRobinBalancer(c, loggerFactory)); // Act var pickTask1 = clientChannel.PickAsync( new PickContext { Request = new HttpRequestMessage() }, waitForReady: true, CancellationToken.None).AsTask(); await clientChannel.ConnectAsync(waitForReady : true, CancellationToken.None).DefaultTimeout(); var result1 = await pickTask1.DefaultTimeout(); // Assert Assert.AreEqual(new DnsEndPoint("localhost", 80), result1.Address !.EndPoint); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 80), new BalancerAddress("localhost", 81) }); for (var i = 0; i < transportFactory.Transports.Count; i++) { transportFactory.Transports[i].UpdateState(ConnectivityState.TransientFailure); } var pickTask2 = clientChannel.PickAsync( new PickContext { Request = new HttpRequestMessage() }, waitForReady: true, CancellationToken.None).AsTask().DefaultTimeout(); Assert.IsFalse(pickTask2.IsCompleted); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 82) }); var result2 = await pickTask2.DefaultTimeout(); Assert.AreEqual(new DnsEndPoint("localhost", 82), result2.Address !.EndPoint); }
public async Task UpdateAddresses_ConnectIsInProgress_InProgressConnectIsCanceledAndRestarted() { // Arrange var services = new ServiceCollection(); services.AddNUnitLogger(); var serviceProvider = services.BuildServiceProvider(); var loggerFactory = serviceProvider.GetRequiredService <ILoggerFactory>(); var testLogger = loggerFactory.CreateLogger(GetType()); var resolver = new TestResolver(loggerFactory); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 80) }); var connectAddressesChannel = System.Threading.Channels.Channel.CreateUnbounded <DnsEndPoint>(); var syncPoint = new SyncPoint(runContinuationsAsynchronously: true); var transportFactory = new TestSubchannelTransportFactory(async(s, c) => { c.Register(state => ((SyncPoint)state !).Continue(), syncPoint); var connectAddress = s.GetAddresses().Single(); testLogger.LogInformation("Writing connect address " + connectAddress); await connectAddressesChannel.Writer.WriteAsync(connectAddress.EndPoint, c); await syncPoint.WaitToContinue(); c.ThrowIfCancellationRequested(); return(ConnectivityState.Ready); }); var clientChannel = new ConnectionManager(resolver, disableResolverServiceConfig: false, loggerFactory, transportFactory, Array.Empty <LoadBalancerFactory>()); clientChannel.ConfigureBalancer(c => new PickFirstBalancer(c, loggerFactory)); // Act _ = clientChannel.ConnectAsync(waitForReady: true, CancellationToken.None).ConfigureAwait(false); var connectAddress1 = await connectAddressesChannel.Reader.ReadAsync().AsTask().DefaultTimeout(); Assert.AreEqual(80, connectAddress1.Port); // Endpoints are unchanged so continue connecting... resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 80) }); Assert.IsFalse(syncPoint.WaitToContinue().IsCompleted); // Endpoints change so cancellation + reconnect triggered resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress("localhost", 81) }); await syncPoint.WaitToContinue().DefaultTimeout(); var connectAddress2 = await connectAddressesChannel.Reader.ReadAsync().AsTask().DefaultTimeout(); Assert.AreEqual(81, connectAddress2.Port); }
public async Task Subchannel_ResolveRemovesSubchannelAfterRequest_SubchannelCleanedUp() { // Ignore errors SetExpectedErrorsFilter(writeContext => { return(true); }); string?host = null; Task <HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context) { host = context.Host; return(Task.FromResult(new HelloReply { Message = request.Name })); } // Arrange using var endpoint1 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50051, UnaryMethod, nameof(UnaryMethod)); using var endpoint2 = BalancerHelpers.CreateGrpcEndpoint <HelloRequest, HelloReply>(50052, UnaryMethod, nameof(UnaryMethod)); var resolver = new TestResolver(LoggerFactory); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint1.Address.Host, endpoint1.Address.Port) }); var channel = await BalancerHelpers.CreateChannel(LoggerFactory, new RoundRobinConfig(), resolver, connect : true); var disposedSubchannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); var client = TestClientFactory.Create(channel, endpoint1.Method); var reply1 = await client.UnaryCall(new HelloRequest { Name = "Balancer1" }); Assert.AreEqual("Balancer1", reply1.Message); Assert.AreEqual("127.0.0.1:50051", host !); resolver.UpdateAddresses(new List <BalancerAddress> { new BalancerAddress(endpoint2.Address.Host, endpoint2.Address.Port) }); var activeStreams = ((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport).GetActiveStreams(); Assert.AreEqual(1, activeStreams.Count); Assert.AreEqual("127.0.0.1", activeStreams[0].Address.EndPoint.Host); Assert.AreEqual(50051, activeStreams[0].Address.EndPoint.Port); // Wait until connected to new endpoint Subchannel?newSubchannel = null; while (true) { newSubchannel = await BalancerHelpers.WaitForSubchannelToBeReadyAsync(Logger, channel).DefaultTimeout(); if (newSubchannel.CurrentAddress?.EndPoint.Equals(endpoint2.EndPoint) ?? false) { break; } } // Subchannel has a socket until a request is made. Assert.IsNotNull(((SocketConnectivitySubchannelTransport)newSubchannel.Transport)._initialSocket); endpoint1.Dispose(); var reply2 = await client.UnaryCall(new HelloRequest { Name = "Balancer2" }); Assert.AreEqual("Balancer2", reply2.Message); Assert.AreEqual("127.0.0.1:50052", host !); // Disposed subchannel stream removed when endpoint disposed. activeStreams = ((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport).GetActiveStreams(); Assert.AreEqual(0, activeStreams.Count); Assert.IsNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); // New subchannel stream created with request. activeStreams = ((SocketConnectivitySubchannelTransport)newSubchannel.Transport).GetActiveStreams(); Assert.AreEqual(1, activeStreams.Count); Assert.AreEqual("127.0.0.1", activeStreams[0].Address.EndPoint.Host); Assert.AreEqual(50052, activeStreams[0].Address.EndPoint.Port); Assert.IsNull(((SocketConnectivitySubchannelTransport)disposedSubchannel.Transport)._initialSocket); }