/** * 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.collect.ImmutableMap; import java.io.Closeable; import java.io.IOException; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.function.Supplier; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.cache.PathChildrenCache; import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent; import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener; import org.apache.curator.framework.recipes.nodes.GroupMember; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static zipkin.internal.Util.UTF_8; /** * This watches the store rate path for updates. */ final class SampleRateUpdater implements PathChildrenCacheListener, Closeable { final Logger log = LoggerFactory.getLogger(SampleRateUpdater.class); private final GroupMember storeRateMember; private final String sampleRatePath; private final Function<Map<String, Integer>, Optional<Float>> calculator; private final Supplier<Boolean> guard; private final PathChildrenCache dataWatcher; SampleRateUpdater(CuratorFramework client, GroupMember storeRateMember, String storeRatePath, String sampleRatePath, Function<Map<String, Integer>, Optional<Float>> calculator, Supplier<Boolean> guard ) { this.storeRateMember = storeRateMember; this.sampleRatePath = sampleRatePath; this.calculator = calculator; this.guard = guard; // We don't need to cache the data as we can already access it from storeRateMember this.dataWatcher = new PathChildrenCache(client, storeRatePath, false); try { this.dataWatcher.start(); } catch (Exception e) { throw new IllegalStateException(e); } dataWatcher.getListenable().addListener(this); } @Override public void close() throws IOException { dataWatcher.close(); } @Override public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) { switch (event.getType()) { case CHILD_ADDED: case CHILD_UPDATED: case CHILD_REMOVED: break; default: return; } ImmutableMap.Builder<String, Integer> builder = new ImmutableMap.Builder(); for (Map.Entry<String, byte[]> i : storeRateMember.getCurrentMembers().entrySet()) { if (i.getValue() == null) continue; try { builder.put(i.getKey(), Integer.valueOf(new String(i.getValue(), UTF_8))); } catch (NumberFormatException e) { log.debug("malformed data at path {}: {}", i.getKey(), e.getMessage()); } } Optional<Float> newSampleRate = calculator.apply(builder.build()); if (!newSampleRate.isPresent() || !guard.get()) return; Float rate = newSampleRate.get(); log.info("updating sample rate: {} {}", sampleRatePath, rate); try { client.create().creatingParentsIfNeeded() .forPath(sampleRatePath, rate.toString().getBytes(UTF_8)); } catch (Exception e) { log.warn("could not set sample rate to {} for {}", rate, sampleRatePath); } } }