package com.yammer.breakerbox.service.tenacity; import com.google.common.base.Optional; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.netflix.turbine.discovery.Instance; import com.yammer.breakerbox.turbine.client.TurbineTenacityClient; import com.yammer.tenacity.core.TenacityCommand; import com.yammer.tenacity.core.config.TenacityConfiguration; import com.yammer.tenacity.core.properties.TenacityPropertyKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Objects; import java.util.concurrent.TimeUnit; import static com.google.common.base.Preconditions.checkNotNull; public class TenacityConfigurationFetcher extends TenacityCommand<Optional<TenacityConfiguration>> { public static class Factory { private final TurbineTenacityClient client; public Factory(TurbineTenacityClient client) { this.client = client; } public TenacityConfigurationFetcher create(Instance instance, TenacityPropertyKey key) { return new TenacityConfigurationFetcher(client, instance, key); } } private static class Key { private final Instance instance; private final TenacityPropertyKey tenacityPropertyKey; public Key(Instance instance, TenacityPropertyKey tenacityPropertyKey) { this.instance = checkNotNull(instance); this.tenacityPropertyKey = checkNotNull(tenacityPropertyKey); } @Override public int hashCode() { return Objects.hash(instance, tenacityPropertyKey); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final Key other = (Key) obj; return Objects.equals(this.instance, other.instance) && Objects.equals(this.tenacityPropertyKey, other.tenacityPropertyKey); } } private final TurbineTenacityClient client; private final Key key; private static final Cache<Key, TenacityConfiguration> cache = CacheBuilder .newBuilder() .expireAfterWrite(10, TimeUnit.SECONDS) //Small TTL to collapse simultaneous requests .build(); private static final Logger LOGGER = LoggerFactory.getLogger(TenacityConfigurationFetcher.class); public TenacityConfigurationFetcher(TurbineTenacityClient client, Instance instance, TenacityPropertyKey key) { super(BreakerboxDependencyKey.BRKRBX_SERVICES_CONFIGURATION); this.client = checkNotNull(client); this.key = new Key(checkNotNull(instance), checkNotNull(key)); } @Override protected Optional<TenacityConfiguration> run() throws Exception { try { return Optional.of(cache.get(key, () -> client.getTenacityConfiguration(key.instance, key.tenacityPropertyKey).orNull())); } catch (CacheLoader.InvalidCacheLoadException err) { //null was returned, don't negatively cache results } catch (Exception err) { LOGGER.warn("Unexpected exception", err); } return Optional.absent(); } @Override protected Optional<TenacityConfiguration> getFallback() { return Optional.absent(); } }