/** * Copyright 2013 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.netflix.loadbalancer; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import com.google.common.util.concurrent.Uninterruptibles; import org.apache.commons.configuration.Configuration; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import com.google.common.collect.Lists; import com.netflix.client.ClientFactory; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.config.ConfigurationManager; public class SubsetFilterTest { @BeforeClass public static void init() { Configuration config = ConfigurationManager.getConfigInstance(); config.setProperty("SubsetFilerTest.ribbon.NFLoadBalancerClassName", com.netflix.loadbalancer.DynamicServerListLoadBalancer.class.getName()); config.setProperty("SubsetFilerTest.ribbon.NIWSServerListClassName", MockServerList.class.getName()); config.setProperty("SubsetFilerTest.ribbon.NIWSServerListFilterClassName", ServerListSubsetFilter.class.getName()); // turn off auto refresh config.setProperty("SubsetFilerTest.ribbon.ServerListRefreshInterval", String.valueOf(Integer.MAX_VALUE)); config.setProperty("SubsetFilerTest.ribbon.ServerListSubsetFilter.forceEliminatePercent", "0.6"); config.setProperty("SubsetFilerTest.ribbon.ServerListSubsetFilter.eliminationFailureThresold", 2); config.setProperty("SubsetFilerTest.ribbon.ServerListSubsetFilter.eliminationConnectionThresold", 2); config.setProperty("SubsetFilerTest.ribbon.ServerListSubsetFilter.size", "5"); } List getServersAndStats(LoadBalancerStats lbStats, Object[][] stats) { List list = Lists.newArrayList(); for (Object[] serverStats: stats) { Server server = new Server((String) serverStats[0]); list.add(server); int failureCount = (Integer) serverStats[1]; int connectionCount = (Integer) serverStats[2]; lbStats.getServerStats().put(server, new DummyServerStats(connectionCount, failureCount)); } return list; } @Test public void testSorting() { ServerListSubsetFilter filter = new ServerListSubsetFilter(); LoadBalancerStats stats = new LoadBalancerStats("default"); filter.setLoadBalancerStats(stats); Object[][] serverStats = { {"server0", 0, 0}, {"server1", 1, 0}, {"server2", 1, 1}, {"server3", 0, 1}, {"server4", 2, 0} }; List servers = getServersAndStats(stats, serverStats); Collections.sort(servers, filter); List expected = Lists.newArrayList("server4", "server2", "server1", "server3", "server0"); for (int i = 0; i < servers.size(); i++) { assertEquals(expected.get(i), servers.get(i).getHost()); } } @Test public void testFiltering() { DefaultClientConfigImpl config = new DefaultClientConfigImpl(); config.loadProperties("SubsetFilerTest"); ServerListSubsetFilter filter = new ServerListSubsetFilter(config); LoadBalancerStats stats = new LoadBalancerStats("default"); stats.initWithNiwsConfig(config); filter.setLoadBalancerStats(stats); Object[][] serverStats = { {"server0", 0, 0}, {"server1", 0, 0}, {"server2", 0, 0}, {"server3", 0, 0}, {"server4", 0, 0}, {"server5", 0, 0}, {"server6", 0, 0}, {"server7", 0, 0}, {"server8", 0, 0}, {"server9", 0, 0} }; List list = getServersAndStats(stats, serverStats); List filtered = filter.getFilteredListOfServers(list); // first filtering, should get 5 servers assertEquals(filtered.size(), 5); Server s1 = filtered.get(0); Server s2 = filtered.get(1); Server s3 = filtered.get(2); Server s4 = filtered.get(3); Server s5 = filtered.get(4); // failure count > threshold DummyServerStats stats1 = (DummyServerStats) stats.getSingleServerStat(s1); stats1.setConnectionFailureCount(3); // active requests count > threshold DummyServerStats stats2 = (DummyServerStats) stats.getSingleServerStat(s2); stats2.setActiveRequestsCount(3); // will be forced eliminated after sorting DummyServerStats stats3 = (DummyServerStats) stats.getSingleServerStat(s3); stats3.setActiveRequestsCount(2); // will be forced eliminated after sorting DummyServerStats stats4 = (DummyServerStats) stats.getSingleServerStat(s4); stats4.setConnectionFailureCount(1); // filter again, this time some servers will be eliminated filtered = filter.getFilteredListOfServers(list); assertEquals(5, filtered.size()); assertTrue(!filtered.contains(s1)); assertTrue(!filtered.contains(s2)); assertTrue(filtered.contains(s3)); assertTrue(!filtered.contains(s4)); assertTrue(filtered.contains(s5)); // Not enough healthy servers, just get whatever is available List lastFiltered = filter.getFilteredListOfServers(Lists.newArrayList(filtered)); assertEquals(5, lastFiltered.size()); } @Test public void testWithLoadBalancer() { DynamicServerListLoadBalancer lb = (DynamicServerListLoadBalancer) ClientFactory.getNamedLoadBalancer("SubsetFilerTest"); MockServerList serverList = (MockServerList) lb.getServerListImpl(); Object[][] serverStats = { {"server0", 0, 0}, {"server1", 0, 0}, {"server2", 0, 0}, {"server3", 0, 0}, {"server4", 0, 0}, {"server5", 0, 0}, {"server6", 0, 0}, {"server7", 0, 0}, {"server8", 0, 0}, {"server9", 0, 0} }; LoadBalancerStats stats = lb.getLoadBalancerStats(); List list = getServersAndStats(stats, serverStats); serverList.setServerList(list); Uninterruptibles.sleepUninterruptibly(2, TimeUnit.SECONDS); lb.updateListOfServers(); List filtered = lb.getAllServers(); // first filtering, should get 5 servers assertEquals(filtered.size(), 5); Server s1 = filtered.get(0); Server s2 = filtered.get(1); Server s3 = filtered.get(2); Server s4 = filtered.get(3); Server s5 = filtered.get(4); // failure count > threshold DummyServerStats stats1 = (DummyServerStats) stats.getSingleServerStat(s1); stats1.setConnectionFailureCount(3); // active requests count > threshold DummyServerStats stats2 = (DummyServerStats) stats.getSingleServerStat(s2); stats2.setActiveRequestsCount(3); // will be forced eliminated after sorting DummyServerStats stats3 = (DummyServerStats) stats.getSingleServerStat(s3); stats3.setActiveRequestsCount(2); // will be forced eliminated after sorting DummyServerStats stats4 = (DummyServerStats) stats.getSingleServerStat(s4); stats4.setConnectionFailureCount(1); // filter again, this time some servers will be eliminated serverList.setServerList(list); lb.updateListOfServers(); filtered = lb.getAllServers(); assertEquals(5, filtered.size()); assertTrue(!filtered.contains(s1)); assertTrue(!filtered.contains(s2)); assertTrue(filtered.contains(s3)); assertTrue(!filtered.contains(s4)); assertTrue(filtered.contains(s5)); // Not enough healthy servers, just get whatever is available serverList.setServerList(Lists.newArrayList(filtered)); lb.updateListOfServers(); List lastFiltered = lb.getAllServers(); assertEquals(5, lastFiltered.size()); } } class DummyServerStats extends ServerStats { int activeRequestsCount; int connectionFailureCount; public DummyServerStats(int activeRequestsCount, int connectionFailureCount) { this.activeRequestsCount = activeRequestsCount; this.connectionFailureCount = connectionFailureCount; } public final void setActiveRequestsCount(int activeRequestsCount) { this.activeRequestsCount = activeRequestsCount; } public final void setConnectionFailureCount(int connectionFailureCount) { this.connectionFailureCount = connectionFailureCount; } @Override public int getActiveRequestsCount() { return activeRequestsCount; } @Override public long getFailureCount() { return connectionFailureCount; } }