/** * Copyright 2015-2016 The OpenZipkin Authors * * 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 zipkin.collector.zookeeper; import com.google.common.io.Closer; import java.io.IOException; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Supplier; import org.apache.curator.framework.recipes.nodes.GroupMember; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; public class SampleRateUpdaterTest { static final String PREFIX = "/" + SampleRateUpdaterTest.class.getSimpleName(); static final String storeRatePath = PREFIX + "/storeRates"; @Rule public ZooKeeperRule zookeeper = new ZooKeeperRule(); GroupMember local; GroupMember peer; @Test public void readsAllChildren() throws Exception { Map<String, Integer> inputs = new ConcurrentHashMap<>(); Function<Map<String, Integer>, Optional<Float>> calculator = (input) -> { inputs.putAll(input); return Optional.empty(); }; updater(calculator, () -> Boolean.TRUE); local.setThisData("100".getBytes()); peer.setThisData("200".getBytes()); zookeeper.sleepABit(); assertThat(inputs).containsOnly( entry("zipkin-server@1.1.1.1:8080", 100), entry("zipkin-server@2.2.2.2:8080", 200) ); } /** * If a collector node goes down, or is renamed, we shouldn't read the old key, as it would skew * the sample rate. */ @Test public void onlyReadsCurrentGroupMembers() throws Exception { Map<String, Integer> inputs = new ConcurrentHashMap<>(); Function<Map<String, Integer>, Optional<Float>> calculator = (input) -> { inputs.putAll(input); return Optional.empty(); }; updater(calculator, () -> Boolean.TRUE); local.setThisData("100".getBytes()); peer.setThisData("200".getBytes()); zookeeper.sleepABit(); assertThat(inputs).containsOnly( entry("zipkin-server@1.1.1.1:8080", 100), entry("zipkin-server@2.2.2.2:8080", 200) ); inputs.clear(); // Simulate a peer going down and the local node taking over the load peer.close(); local.setThisData("300".getBytes()); zookeeper.sleepABit(); assertThat(inputs).containsOnly( entry("zipkin-server@1.1.1.1:8080", 300) ); } @Test public void skipsMalformedData() throws Exception { Map<String, Integer> inputs = new ConcurrentHashMap<>(); Function<Map<String, Integer>, Optional<Float>> calculator = (input) -> { inputs.putAll(input); return Optional.empty(); }; updater(calculator, () -> Boolean.TRUE); local.setThisData("crying babies".getBytes()); peer.setThisData("200".getBytes()); zookeeper.sleepABit(); assertThat(inputs).containsOnly( entry("zipkin-server@2.2.2.2:8080", 200) ); } @Test public void writesSampleRate() throws Exception { updater((input) -> Optional.of(0.9f), () -> Boolean.TRUE); local.setThisData("100".getBytes()); // trigger zookeeper.sleepABit(); assertThat(zookeeper.client.getData().forPath(PREFIX + "/sampleRate")) .isEqualTo("0.9".getBytes()); } @Test public void obeysGuard() throws Exception { updater((input) -> Optional.of(0.9f), () -> Boolean.FALSE); local.setThisData("100".getBytes()); zookeeper.sleepABit(); assertThat(zookeeper.client.checkExists().forPath(PREFIX + "/sampleRate")) .isNull(); } Closer closer = Closer.create(); SampleRateUpdater updater(Function<Map<String, Integer>, Optional<Float>> calculator, Supplier<Boolean> guard) { return closer.register(new SampleRateUpdater( zookeeper.client, local, storeRatePath, PREFIX + "/sampleRate", calculator, guard)); } @Before public void joinGroup() throws Exception { zookeeper.client.createContainers(storeRatePath); local = closer.register( new GroupMember(zookeeper.client, storeRatePath, "zipkin-server@1.1.1.1:8080")); peer = closer.register( new GroupMember(zookeeper.client, storeRatePath, "zipkin-server@2.2.2.2:8080")); local.start(); peer.start(); zookeeper.sleepABit(); // for the group members to start } @After public void closeThings() throws IOException { closer.close(); } }