Esempio n. 1
0
        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();
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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 !);
Esempio n. 4
0
        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);
        }
Esempio n. 9
0
        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);
        }