package netflix.ocelli; import com.google.common.collect.Lists; import netflix.ocelli.InstanceQuarantiner.IncarnationFactory; import netflix.ocelli.functions.Delays; import netflix.ocelli.loadbalancer.ChoiceOfTwoLoadBalancer; import netflix.ocelli.loadbalancer.RoundRobinLoadBalancer; import org.hamcrest.MatcherAssert; import org.junit.Test; import rx.Observable; import rx.Subscription; import rx.functions.Func0; import rx.functions.Func1; import rx.schedulers.TestScheduler; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import static org.hamcrest.Matchers.*; public class LoadBalancerTest { private static final Comparator<ClientWithWeight> COMPARE_BY_WEIGHT = new Comparator<ClientWithWeight>() { @Override public int compare(ClientWithWeight o1, ClientWithWeight o2) { return o1.weight.compareTo(o2.weight); } }; private static final Func1<Instance<String>, Instance<ClientWithWeight>> CLIENT_FROM_ADDRESS = new Func1<Instance<String>, Instance<ClientWithWeight>>() { @Override public Instance<ClientWithWeight> call(Instance<String> t1) { return Instance.create(new ClientWithWeight(t1.getValue(), 1), t1.getLifecycle()); } }; @Test public void createFromFixedList() { LoadBalancer<String> lb = LoadBalancer .fromFixedSource(Lists.newArrayList("host1:8080", "host2:8080")) .build(RoundRobinLoadBalancer.<String>create(0), InstanceCollector.create(new Func0<Map<String, Subscription>>() { @Override public Map<String, Subscription> call() { return new LinkedHashMap<String, Subscription>(); } })) ; MatcherAssert.assertThat("Unexpected first host chosen.", lb.next(), is("host2:8080")); MatcherAssert.assertThat("Unexpected second host chosen.", lb.next(), is("host1:8080")); } @Test public void createFromFixedListAndConvertToDifferentType() { LoadBalancer<ClientWithWeight> lb = LoadBalancer .fromFixedSource(Lists.newArrayList("host1:8080", "host2:8080")) .convertTo(CLIENT_FROM_ADDRESS) .build(RoundRobinLoadBalancer.<ClientWithWeight>create(0), InstanceCollector.create(new Func0<Map<ClientWithWeight, Subscription>>() { @Override public Map<ClientWithWeight, Subscription> call() { return new LinkedHashMap<ClientWithWeight, Subscription>(); } })) ; MatcherAssert.assertThat("Unexpected first host chosen.", lb.next().address, equalTo("host2:8080")); MatcherAssert.assertThat("Unexpected second host chosen.", lb.next().address, equalTo("host1:8080")); } @Test public void createFromFixedListWithAdvancedAlgorithm() { LoadBalancer<ClientWithWeight> lb = LoadBalancer .fromFixedSource( Lists.newArrayList(new ClientWithWeight("host1:8080", 1), new ClientWithWeight("host2:8080", 2))) .build(ChoiceOfTwoLoadBalancer.create(COMPARE_BY_WEIGHT), InstanceCollector.create(new Func0<Map<ClientWithWeight, Subscription>>() { @Override public Map<ClientWithWeight, Subscription> call() { return new LinkedHashMap<ClientWithWeight, Subscription>(); } })) ; MatcherAssert.assertThat("Unexpected first host chosen.", lb.next().address, equalTo("host2:8080")); MatcherAssert.assertThat("Unexpected second host chosen.", lb.next().address, equalTo("host2:8080")); } @Test public void createFromFixedAndUseQuaratiner() { TestScheduler scheduler = new TestScheduler(); LoadBalancer<ClientWithLifecycle> lb = LoadBalancer .fromFixedSource(Lists.newArrayList(new ClientWithLifecycle("host1:8080"), new ClientWithLifecycle("host2:8080"))) .withQuarantiner(new IncarnationFactory<ClientWithLifecycle>() { @Override public ClientWithLifecycle create(ClientWithLifecycle client, InstanceEventListener listener, Observable<Void> lifecycle) { return new ClientWithLifecycle(client, listener); } }, Delays.fixed(10, TimeUnit.SECONDS), scheduler) .build(RoundRobinLoadBalancer.<ClientWithLifecycle>create(0), InstanceCollector.create(new Func0<Map<ClientWithLifecycle, Subscription>>() { @Override public Map<ClientWithLifecycle, Subscription> call() { return new LinkedHashMap<ClientWithLifecycle, Subscription>(); } })); ClientWithLifecycle clientToFail = lb.next(); MatcherAssert.assertThat("Unexpected host chosen before failure.", clientToFail.address, equalTo("host2:8080")); clientToFail.forceFail(); MatcherAssert.assertThat("Unexpected first host chosen post failure.", lb.next().address, equalTo("host1:8080")); MatcherAssert.assertThat("Unexpected second host chosen post failure.", lb.next().address, equalTo("host1:8080")); } static class ClientWithLifecycle { private final String address; private final InstanceEventListener listener; public ClientWithLifecycle(String address) { this.address = address; listener = null; } public ClientWithLifecycle(ClientWithLifecycle parent, InstanceEventListener listener) { address = parent.address; this.listener = listener; } public void forceFail() { listener.onEvent(InstanceEvent.EXECUTION_FAILED, 0, TimeUnit.MILLISECONDS, new Throwable("Failed"), null); } } static class ClientWithWeight { private final String address; private final Integer weight; public ClientWithWeight(String address, int weight) { this.address = address; this.weight = weight; } } }