public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { foreach (var endpoint in availableEndpoints) { if (_quanta.TryGetValue(endpoint, out var quantum)) { if (!_counters.TryGetValue(endpoint, out var counter)) { counter = 0; } if (counter < quantum) { _counters[endpoint] = counter + 1; // reset all counters if we're at the end if (endpoint == availableEndpoints.LastOrDefault() && counter == quantum - 1) { _counters.Clear(); } return(endpoint); } } } return(null); }
public void PickEndpoint_DeficitRoundRobin_Works() { var loadBalancer = Create <LoadBalancer>(); var first = new EndpointInfo("1"); var second = new EndpointInfo("2"); var third = new EndpointInfo("3"); var endpoints = new[] { first, second, third }; var quanta = new Dictionary <EndpointInfo, int> { [first] = 1, [second] = 2, [third] = 3 }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.DeficitRoundRobin, deficitRoundRobinQuanta: quanta); // choose first X 1 var next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(first, next); // choose second X 2 next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(second, next); next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(second, next); // choose third X 3 next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(third, next); next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(third, next); next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(third, next); // choose first X 1 next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(first, next); // choose second X 2 next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(second, next); next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(second, next); // choose third X 3 next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(third, next); next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(third, next); next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.Equal(third, next); }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { var matching = _selector(availableEndpoints); return(ThreadLocalRandom.Current.Next(0, 101) <= _variation ? _backingLoadBalancingStrategy.Balance(matching.ToList().AsReadOnly(), loadBalancingOptions) : _backingLoadBalancingStrategy.Balance(availableEndpoints.Except(matching).ToList().AsReadOnly(), loadBalancingOptions)); }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { var exceptPreferred = availableEndpoints .Except(Enumerable.Repeat(_preferredEndpoint, 1)) .ToList(); return(_isAvailable(_preferredEndpoint) ? _preferredEndpoint : _fallBackLoadBalancingStrategy.Balance(exceptPreferred.ToList().AsReadOnly(), loadBalancingOptions)); }
public void PickDestination_WithoutDestinations_Null() { var loadBalancer = Create <LoadBalancer>(); var destinations = new DestinationInfo[0]; var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1)); var result = loadBalancer.PickDestination(destinations, in options); Assert.Null(result); }
public void PickEndpoint_WithoutEndpoints_Null() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new EndpointInfo[0]; var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1)); var result = loadBalancer.PickEndpoint(endpoints, in options); Assert.Null(result); }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { // Pick two, and then return the least busy. This avoids the effort of searching the whole list, but // still avoids overloading a single endpoint. var random1 = _randomFactory.CreateRandomInstance(); var firstEndpoint = availableEndpoints[random1.Next(availableEndpoints.Count)]; var secondEndpoint = availableEndpoints[random1.Next(availableEndpoints.Count)]; return(firstEndpoint.ConcurrencyCounter.Value <= secondEndpoint.ConcurrencyCounter.Value ? firstEndpoint : secondEndpoint); }
public void PickEndpoint_SingleEndpoints_ShortCircuit() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("ep1"), }; var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1)); var result = loadBalancer.PickEndpoint(endpoints, in options); Assert.Equal(result, endpoints[0]); }
public void PickDestination_SingleDestinations_ShortCircuit() { var loadBalancer = Create <LoadBalancer>(); var destinations = new[] { new DestinationInfo("d1"), }; var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1)); var result = loadBalancer.PickDestination(destinations, in options); Assert.Same(destinations[0], result); }
public void PickEndpoint_FirstWithoutEndpoints_Works() { // Arrange var loadBalancer = Create <LoadBalancer>(); var endpoints = new EndpointInfo[0]; var options = new BackendConfig.BackendLoadBalancingOptions(BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode.First); // Act var result = loadBalancer.PickEndpoint(endpoints, endpoints, in options); // Assert result.Should().BeNull(); }
public void PickDestination_FirstWithDestinations_Works() { var loadBalancer = Create <LoadBalancer>(); var destinations = new[] { new DestinationInfo("d1"), new DestinationInfo("d2"), }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.First); var result = loadBalancer.PickDestination(destinations, in options); Assert.Same(destinations[0], result); }
public void PickEndpoint_FirstWithEndpoints_Works() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("ep1"), new EndpointInfo("ep2"), }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.First); var result = loadBalancer.PickEndpoint(endpoints, in options); Assert.Same(endpoints[0], result); }
public void PickEndpoint_UnsupportedMode_Throws() { // Arrange var loadBalancer = Create <LoadBalancer>(); var endpoints = new EndpointInfo[0]; var options = new BackendConfig.BackendLoadBalancingOptions((BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode)(-1)); // Act Action action = () => loadBalancer.PickEndpoint(endpoints, endpoints, in options); // Assert action.Should().ThrowExactly <ReverseProxyException>() .Which.Message.Should().Be("Load balancing mode '-1' is not supported."); }
public void PickEndpoint_UnsupportedMode_Throws() { // Arrange var loadBalancer = Create <LoadBalancer>(); var endpoints = new EndpointInfo[0]; var options = new BackendConfig.BackendLoadBalancingOptions((BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode)(-1)); // Act Action action = () => loadBalancer.PickEndpoint(endpoints, in options); // Assert var ex = Assert.Throws <NotSupportedException>(action); Assert.Equal("Load balancing mode '-1' is not supported.", ex.Message); }
public void PickDestination_Random_Works() { var loadBalancer = Create <LoadBalancer>(); var destinations = new[] { new DestinationInfo("d1"), new DestinationInfo("d2"), }; RandomInstance.Sequence = new[] { 1 }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.Random); var result = loadBalancer.PickDestination(destinations, in options); Assert.Same(result, destinations[1]); }
public void PickDestination_LeastRequests_Works() { var loadBalancer = Create <LoadBalancer>(); var destinations = new[] { new DestinationInfo("d1"), new DestinationInfo("d2"), }; destinations[0].ConcurrencyCounter.Increment(); var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.LeastRequests); var result = loadBalancer.PickDestination(destinations, in options); Assert.Same(result, destinations[1]); }
public void PickEndpoint_LeastRequests_Works() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("ep1"), new EndpointInfo("ep2"), }; endpoints[0].ConcurrencyCounter.Increment(); var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.LeastRequests); var result = loadBalancer.PickEndpoint(endpoints, in options); Assert.Same(result, endpoints[1]); }
public void PickEndpoint_Random_Works() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("ep1"), new EndpointInfo("ep2"), }; RandomInstance.Sequence = new[] { 1 }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.Random); var result = loadBalancer.PickEndpoint(endpoints, in options); Assert.Same(result, endpoints[1]); }
public void PickDestination_UnsupportedMode_Throws() { var loadBalancer = Create <LoadBalancer>(); var destinations = new[] { new DestinationInfo("d1"), new DestinationInfo("d2"), }; var options = new BackendConfig.BackendLoadBalancingOptions((LoadBalancingMode)(-1)); Action action = () => loadBalancer.PickDestination(destinations, in options); // Assert var ex = Assert.Throws <NotSupportedException>(action); Assert.Equal("Load balancing mode '-1' is not supported.", ex.Message); }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { var leastRequestsEndpoint = availableEndpoints[0]; var leastRequestsCount = leastRequestsEndpoint.ConcurrencyCounter.Value; for (var i = 1; i < availableEndpoints.Count; i++) { var endpoint = availableEndpoints[i]; var endpointRequestCount = endpoint.ConcurrencyCounter.Value; if (endpointRequestCount < leastRequestsCount) { leastRequestsEndpoint = endpoint; leastRequestsCount = endpointRequestCount; } } return(leastRequestsEndpoint); }
public void PickEndpoint_PowerOfTwoChoices_Works() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("ep1"), new EndpointInfo("ep2"), }; endpoints[0].ConcurrencyCounter.Increment(); RandomInstance.Sequence = new[] { 1, 0 }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.PowerOfTwoChoices); var result = loadBalancer.PickEndpoint(endpoints, in options); Assert.Same(result, endpoints[1]); }
public void PickDestination_PowerOfTwoChoices_Works() { var loadBalancer = Create <LoadBalancer>(); var destinations = new[] { new DestinationInfo("d1"), new DestinationInfo("d2"), }; destinations[0].ConcurrencyCounter.Increment(); RandomInstance.Sequence = new[] { 1, 0 }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.PowerOfTwoChoices); var result = loadBalancer.PickDestination(destinations, in options); Assert.Same(result, destinations[1]); }
public void PickEndpoint_FirstWithEndpoints_Works() { // Arrange var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("ep1"), new EndpointInfo("ep2"), }; var options = new BackendConfig.BackendLoadBalancingOptions(BackendConfig.BackendLoadBalancingOptions.LoadBalancingMode.First); // Act var result = loadBalancer.PickEndpoint(endpoints, endpoints, in options); // Assert result.Should().BeSameAs(endpoints[0]); }
public void PickEndpoint_Callback_Works() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("1"), new EndpointInfo("2"), new EndpointInfo("3") }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.Callback, (availableEndpoints, _) => availableEndpoints.FirstOrDefault(x => x.EndpointId == "3")); // choose third var next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.NotNull(next); Assert.Equal("3", next.EndpointId); }
public void PickEndpoint_RoundRobin_Works() { var loadBalancer = Create <LoadBalancer>(); var endpoints = new[] { new EndpointInfo("ep1"), new EndpointInfo("ep2"), }; endpoints[0].ConcurrencyCounter.Increment(); var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.RoundRobin); var result0 = loadBalancer.PickEndpoint(endpoints, in options); var result1 = loadBalancer.PickEndpoint(endpoints, in options); var result2 = loadBalancer.PickEndpoint(endpoints, in options); var result3 = loadBalancer.PickEndpoint(endpoints, in options); Assert.Same(result0, endpoints[0]); Assert.Same(result1, endpoints[1]); Assert.Same(result2, endpoints[0]); Assert.Same(result3, endpoints[1]); }
public void PickDestination_RoundRobin_Works() { var loadBalancer = Create <LoadBalancer>(); var destinations = new[] { new DestinationInfo("d1"), new DestinationInfo("d2"), }; destinations[0].ConcurrencyCounter.Increment(); var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.RoundRobin); var result0 = loadBalancer.PickDestination(destinations, in options); var result1 = loadBalancer.PickDestination(destinations, in options); var result2 = loadBalancer.PickDestination(destinations, in options); var result3 = loadBalancer.PickDestination(destinations, in options); Assert.Same(result0, destinations[0]); Assert.Same(result1, destinations[1]); Assert.Same(result2, destinations[0]); Assert.Same(result3, destinations[1]); }
public void PickEndpoint_TrafficAllocation_Works() { var loadBalancer = Create <LoadBalancer>(); var first = new EndpointInfo("1"); var second = new EndpointInfo("2"); var third = new EndpointInfo("3"); var endpoints = new[] { first, second, third }; var variation = .10M; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.TrafficAllocation, trafficAllocationSelector: x => x.Where(item => item == second), trafficAllocationVariation: variation, trafficAllocationBackingLoadBalancingStrategy: () => new RandomLoadBalancingStrategy(new RandomFactory())); var selections = 0; const int Iterations = 100 * 1000; for (var i = 0; i < Iterations; i++) { var result = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); if (result == second) { selections++; } } const int DeviationPercentage = 15; var target = Iterations / (int)(variation * 100); var deviation = target * DeviationPercentage / 100; Assert.InRange(selections, target - deviation, target + deviation); }
public void PickEndpoint_FailOver_DoesntFailOver() { var loadBalancer = Create <LoadBalancer>(); var first = new EndpointInfo("1"); var second = new EndpointInfo("2"); var third = new EndpointInfo("3"); var endpoints = new[] { first, second, third }; var options = new BackendConfig.BackendLoadBalancingOptions(LoadBalancingMode.FailOver, failOverPreferredEndpoint: () => first, failOverIsAvailablePredicate: _ => true, failOverFallBackLoadBalancingStrategy: () => new RandomLoadBalancingStrategy(new RandomFactory())); // choose first var next = loadBalancer.PickEndpoint(endpoints.ToList().AsReadOnly(), options); Assert.NotNull(next); Assert.Equal(first, next); }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { var offset = loadBalancingOptions.RoundRobinState.Increment(); return(availableEndpoints[offset % availableEndpoints.Count]); }
public EndpointInfo Balance(IReadOnlyList <EndpointInfo> availableEndpoints, BackendConfig.BackendLoadBalancingOptions loadBalancingOptions) { return(availableEndpoints[0]); }