package net.johnewart.gearman.server.util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import net.johnewart.gearman.engine.metrics.QueueMetrics;
import net.johnewart.shuzai.Frequency;
import net.johnewart.shuzai.SampleMethod;
import net.johnewart.shuzai.TimeSeries;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class SnapshottingJobQueueMonitor implements JobQueueMonitor {
private HashMap<String, JobQueueMetrics> snapshots;
private final Logger LOG = LoggerFactory.getLogger(SnapshottingJobQueueMonitor.class);
private List<SystemSnapshot> systemSnapshots;
private final int maxSnapshots = 2880;
private final QueueMetrics queueMetrics;
private final Object lockObject = new Object();
public SnapshottingJobQueueMonitor(QueueMetrics queueMetrics)
{
this.snapshots = new HashMap<>();
this.systemSnapshots = new LinkedList<>();
this.queueMetrics = queueMetrics;
ScheduledExecutorService executor =
Executors.newScheduledThreadPool(2);
Runnable snapshotTask = new Runnable() {
public void run() {
snapshotJobQueues();
}
};
Runnable compactTask = new Runnable() {
@Override
public void run() {
condenseDataPoints();
}
};
executor.scheduleAtFixedRate(compactTask, 2, 2, TimeUnit.MINUTES);
executor.scheduleAtFixedRate(snapshotTask, 0, 5, TimeUnit.SECONDS);
}
private void condenseDataPoints() {
synchronized(lockObject) {
LOG.debug("Condensing data points");
for (String jobQueueName : queueMetrics.getQueueNames()) {
JobQueueMetrics metrics = snapshots.get(jobQueueName);
if (metrics != null) {
try {
snapshots.put(jobQueueName, metrics.compact());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
private void snapshotJobQueues()
{
synchronized (lockObject) {
LOG.debug("Snapshotting job queues.");
for (String jobQueueName : queueMetrics.getQueueNames()) {
if (!snapshots.containsKey(jobQueueName)) {
snapshots.put(jobQueueName, new JobQueueMetrics());
}
JobQueueMetrics metrics = snapshots.get(jobQueueName);
DateTime now = DateTime.now();
Long highJobs = queueMetrics.getHighPriorityJobsCount(jobQueueName);
Long midJobs = queueMetrics.getMidPriorityJobsCount(jobQueueName);
Long lowJobs = queueMetrics.getLowPriorityJobsCount(jobQueueName);
metrics.highJobs.add(now, highJobs);
metrics.midJobs.add(now, midJobs);
metrics.lowJobs.add(now, lowJobs);
metrics.queued.add(now, queueMetrics.getEnqueuedJobCount(jobQueueName));
metrics.exceptions.add(now, queueMetrics.getExceptionCount(jobQueueName));
metrics.completed.add(now, queueMetrics.getCompletedJobCount(jobQueueName));
metrics.failed.add(now, queueMetrics.getFailedJobCount(jobQueueName));
}
}
// Generate an overall snapshot
SystemSnapshot currentSnapshot;
Long totalProcessed = queueMetrics.getCompletedJobCount();
Long totalQueued = queueMetrics.getEnqueuedJobCount();
Long totalPending = queueMetrics.getPendingJobsCount();
Long heapSize = Runtime.getRuntime().totalMemory();
Long heapUsed = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
if(systemSnapshots.size() > 0)
{
SystemSnapshot previousSnapshot = systemSnapshots.get(systemSnapshots.size()-1);
long processedDiff = totalProcessed - previousSnapshot.getTotalJobsProcessed();
long queuedDiff = totalQueued - previousSnapshot.getTotalJobsQueued();
currentSnapshot = new SystemSnapshot(totalQueued, totalProcessed, queuedDiff, processedDiff, totalPending, heapSize, heapUsed);
} else {
currentSnapshot = new SystemSnapshot(totalQueued, totalProcessed, 0L, 0L, totalPending, heapSize, heapUsed);
}
if(systemSnapshots.size() == maxSnapshots) {
systemSnapshots.remove(maxSnapshots - 1);
}
systemSnapshots.add(currentSnapshot);
}
private long sumOfJobsOccurringInOrAfter(ImmutableMap<Integer, Long> counts, int threshold) {
long sum = 0L;
for (int hoursFromNow : counts.keySet()) {
if (hoursFromNow >= threshold) {
sum += counts.get(hoursFromNow);
}
}
return sum;
}
private TimeSeries fiveMinuteAverage(TimeSeries original) {
return original.downSample(Frequency.of(5, TimeUnit.MINUTES), SampleMethod.MEAN);
}
public ImmutableMap<String, JobQueueMetrics> getSnapshots() {
return ImmutableMap.copyOf(snapshots);
}
public ImmutableList<SystemSnapshot> getSystemSnapshots() {
return ImmutableList.copyOf(systemSnapshots);
}
}