package com.hubspot.blazar.data.service;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Inject;
import com.hubspot.blazar.base.InterProjectBuild;
import com.hubspot.blazar.base.ModuleBuild;
import com.hubspot.blazar.base.RepositoryBuild;
public class CachingMetricsService {
private static final Logger LOG = LoggerFactory.getLogger(CachingMetricsService.class);
private static final long MODULE_BUILD_COUNT_MAX_AGE_MILLIS = 500;
private static final long BRANCH_BUILD_COUNT_MAX_AGE_MILLIS = 500;
private static final long INTER_PROJECT_BUILD_COUNT_MAX_AGE_MILLIS = 500;
private static final long QUEUED_ITEM_COUNT_MAX_AGE_MILLIS = 500;
private static final long HUNG_BUILD_SET_MAX_AGE_MILLIS = 500;
private final MetricsService metricsService;
private volatile Map<ModuleBuild.State, Integer> moduleBuildCountMap = ImmutableMap.of();
private volatile Map<RepositoryBuild.State, Integer> repoBuildCountMap = ImmutableMap.of();
private volatile Map<InterProjectBuild.State, Integer> interProjectCountMap = ImmutableMap.of();
private volatile Map<Class<?>, Integer> queuedItemCountMap = ImmutableMap.of();
private volatile Set<RepositoryBuild> hungRepositoryBuilds = ImmutableSet.of();
private volatile long moduleBuildCountMapLastWrite = 0;
private volatile long repoBuildCountMapLastWrite = 0;
private volatile long interProjectBuildCountMapLastWrite = 0;
private volatile long queuedItemCountMapLastWrite = 0;
private volatile long hungRepositoryBuildsLastWrite = 0;
@Inject
public CachingMetricsService(MetricsService metricsService) {
this.metricsService = metricsService;
}
public synchronized int getCachedActiveModuleBuildCountByState(ModuleBuild.State state) {
if (isTooOld(moduleBuildCountMapLastWrite, MODULE_BUILD_COUNT_MAX_AGE_MILLIS)) {
LOG.info("Refreshing moduleBuildCountMap cache");
moduleBuildCountMap = metricsService.countActiveModuleBuildsByState();
moduleBuildCountMapLastWrite = System.currentTimeMillis();
}
if (!moduleBuildCountMap.containsKey(state)) {
LOG.info("No such state {} in count results returning 0", state);
return 0;
}
return moduleBuildCountMap.get(state);
}
public synchronized int getCachedActiveBranchBuildCountByState(RepositoryBuild.State state) {
if (isTooOld(repoBuildCountMapLastWrite, BRANCH_BUILD_COUNT_MAX_AGE_MILLIS)) {
LOG.info("Refreshing branchBuildCountMap cache");
repoBuildCountMap = metricsService.countActiveBranchBuildsByState();
repoBuildCountMapLastWrite = System.currentTimeMillis();
}
if (!repoBuildCountMap.containsKey(state)) {
LOG.info("No such state {} in count results returning 0", state);
return 0;
}
return repoBuildCountMap.get(state);
}
public synchronized int getCachedActiveInterProjectBuildCountByState(InterProjectBuild.State state) {
if (isTooOld(interProjectBuildCountMapLastWrite, INTER_PROJECT_BUILD_COUNT_MAX_AGE_MILLIS)) {
LOG.info("Refreshing interProjectBuildCountMap cache");
interProjectCountMap = metricsService.countActiveInterProjectBuildsByState();
interProjectBuildCountMapLastWrite = System.currentTimeMillis();
}
if (!interProjectCountMap.containsKey(state)) {
LOG.info("No such state {} in count results returning 0", state);
return 0;
}
return interProjectCountMap.get(state);
}
public int getCachedQueuedItemCountByType(Class<?> type) {
checkAndUpdateQueuedItemCountMap();
if (!queuedItemCountMap.containsKey(type)) {
LOG.info("No events of type {} in count results returning 0", type);
return 0;
}
return queuedItemCountMap.get(type);
}
public int getCachedHungBuildCount() {
checkAndUpdateHungBuildsSet();
return hungRepositoryBuilds.size();
}
public String getCachedHungBuildData() {
checkAndUpdateHungBuildsSet();
Set<String> hungBuildData = new HashSet<>();
long currentTime = System.currentTimeMillis();
for (RepositoryBuild build : hungRepositoryBuilds) {
hungBuildData.add(String.format("Branch %d BuildNumber %d BuildId %d Age %d",
build.getBranchId(),
build.getBuildNumber(),
build.getId().get(),
currentTime - build.getStartTimestamp().get()));
}
return Joiner.on("\n").join(hungBuildData);
}
private synchronized void checkAndUpdateHungBuildsSet() {
if (isTooOld(hungRepositoryBuildsLastWrite, HUNG_BUILD_SET_MAX_AGE_MILLIS)) {
LOG.info("Refreshing hungRepositoryBuilds cache");
hungRepositoryBuilds = metricsService.getHungRepoBuilds();
hungRepositoryBuildsLastWrite = System.currentTimeMillis();
}
}
public int getPendingEventTypeCount() {
checkAndUpdateQueuedItemCountMap();
return queuedItemCountMap.keySet().size();
}
private synchronized void checkAndUpdateQueuedItemCountMap() {
if (isTooOld(queuedItemCountMapLastWrite, QUEUED_ITEM_COUNT_MAX_AGE_MILLIS)) {
LOG.info("Refreshing queuedItemCountMap cache");
queuedItemCountMap = metricsService.countQueuedEventsByType();
queuedItemCountMapLastWrite = System.currentTimeMillis();
}
}
private boolean isTooOld(long lastWrite, long maxAge) {
return System.currentTimeMillis() - lastWrite > maxAge;
}
}