/*
* Copyright 2013 Rackspace
*
* 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 com.rackspacecloud.blueflood.service;
import com.codahale.metrics.Meter;
import com.codahale.metrics.Timer;
import com.rackspacecloud.blueflood.cache.MetadataCache;
import com.rackspacecloud.blueflood.exceptions.GranularityException;
import com.rackspacecloud.blueflood.io.AbstractMetricsRW;
import com.rackspacecloud.blueflood.io.CassandraModel;
import com.rackspacecloud.blueflood.io.CassandraModel.MetricColumnFamily;
import com.rackspacecloud.blueflood.rollup.Granularity;
import com.rackspacecloud.blueflood.types.*;
import com.rackspacecloud.blueflood.utils.Metrics;
import com.rackspacecloud.blueflood.utils.RollupUtils;
import com.rackspacecloud.blueflood.utils.TimeValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.rackspacecloud.blueflood.eventemitter.RollupEventEmitter;
import com.rackspacecloud.blueflood.eventemitter.RollupEvent;
import java.util.*;
import java.util.concurrent.TimeUnit;
/** rolls up data into one data point, inserts that data point. */
public class RollupRunnable implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(RollupRunnable.class);
protected final SingleRollupReadContext singleRollupReadContext;
protected static final MetadataCache metadataCache = MetadataCache.getInstance();
protected static final MetadataCache rollupTypeCache = MetadataCache.createLoadingCacheInstance(
new TimeValue(48, TimeUnit.HOURS), // todo: need a good default expiration here.
Configuration.getInstance().getIntegerProperty(CoreConfig.MAX_ROLLUP_READ_THREADS));
protected final RollupExecutionContext executionContext;
protected final RollupBatchWriter rollupBatchWriter;
protected final long startWait;
private static final Timer calcTimer = Metrics.timer(RollupRunnable.class, "Read And Calculate Rollup");
private static final Meter noPointsToCalculateRollup = Metrics.meter(RollupRunnable.class, "No points to calculate rollup");
private static HashMap<Granularity, Meter> granToMeters = new HashMap<Granularity, Meter>();
static {
for (Granularity rollupGranularity : Granularity.rollupGranularities()) {
granToMeters.put(rollupGranularity, Metrics.meter(RollupRunnable.class, String.format("%s Rollup", rollupGranularity.shortName())));
}
}
public RollupRunnable(RollupExecutionContext executionContext, SingleRollupReadContext singleRollupReadContext, RollupBatchWriter rollupBatchWriter) {
this.executionContext = executionContext;
this.singleRollupReadContext = singleRollupReadContext;
this.rollupBatchWriter = rollupBatchWriter;
startWait = System.currentTimeMillis();
}
public void run() {
// done waiting.
singleRollupReadContext.getWaitHist().update(System.currentTimeMillis() - startWait);
Granularity srcGran;
try {
srcGran = singleRollupReadContext.getRollupGranularity().finer();
} catch (GranularityException ex) {
executionContext.decrementReadCounter();
return; // no work to be done.
}
if (LOG.isTraceEnabled()) {
LOG.trace("Executing rollup from {} for {} {}", new Object[]{
srcGran.shortName(),
singleRollupReadContext.getRange().toString(),
singleRollupReadContext.getLocator()});
}
// start timing this action.
Timer.Context timerContext = singleRollupReadContext.getExecuteTimer().time();
try {
Timer.Context calcrollupContext = calcTimer.time();
granToMeters.get(srcGran.coarser()).mark();
// Read data and compute rollup
Points input;
Rollup rollup = null;
Locator rollupLocator = singleRollupReadContext.getLocator();
RollupType rollupType = RollupType.fromString((String) rollupTypeCache.get(
rollupLocator, MetricMetadata.ROLLUP_TYPE.name().toLowerCase()));
// RollupType | Class | Column Family
// -------------| ------------------------------ |--------------
// COUNTER | BluefloodCounterRollup | metrics_preaggr_{gran}
// TIMER | BluefloodTimerRollup | metrics_preaggr_{gran}
// SET | BluefloodSetRollup | metrics_preaggr_{gran}
// GAUGE | BluefloodGaugeRollup | metrics_preaggr_{gran}
// BF_BASIC | BasicRollup (if gran != full) | metrics_{gran}
// | SimpleNumber (if gran == full) | metrics_full
Class<? extends Rollup> rollupClass = RollupType.classOf(rollupType, srcGran.coarser());
MetricColumnFamily srcCF = CassandraModel.getColumnFamily(rollupClass, srcGran);
Granularity dstGran = srcGran.coarser();
MetricColumnFamily dstCF = CassandraModel.getColumnFamily(rollupClass, dstGran);
// first, get the points.
AbstractMetricsRW metricsRW;
try {
metricsRW = RollupUtils.getMetricsRWForRollupType(rollupType);
input = metricsRW.getDataToRollup(
singleRollupReadContext.getLocator(),
rollupType,
singleRollupReadContext.getRange(),
srcCF.getName());
if (input.isEmpty()) {
LOG.debug(String.format("No points rollup for locator %s", singleRollupReadContext.getLocator()));
noPointsToCalculateRollup.mark();
return;
}
// next, compute the rollup.
rollup = RollupRunnable.getRollupComputer(rollupType, srcGran).compute(input);
} finally {
calcrollupContext.stop();
}
// now enqueue the new rollup for writing.
rollupBatchWriter.enqueueRollupForWrite(new SingleRollupWriteContext(rollup, singleRollupReadContext, dstCF));
RollupService.lastRollupTime.set(System.currentTimeMillis());
//Emit a rollup event to event emitter
RollupEventEmitter.getInstance().emit(RollupEventEmitter.ROLLUP_EVENT_NAME,
new RollupEvent(singleRollupReadContext.getLocator(), rollup,
metadataCache.getUnitString(singleRollupReadContext.getLocator()),
singleRollupReadContext.getRollupGranularity().name(),
singleRollupReadContext.getRange().getStart()));
} catch (Exception e) {
LOG.error("Rollup failed; Locator: {}, Source Granularity: {}, For period: {}", new Object[]{
singleRollupReadContext.getLocator(),
srcGran.name(),
singleRollupReadContext.getRange().toString(),
e});
} finally {
executionContext.decrementReadCounter();
timerContext.stop();
}
}
// determine which DataType to use for serialization.
public static Rollup.Type getRollupComputer(RollupType srcType, Granularity srcGran) {
switch (srcType) {
case COUNTER:
return Rollup.CounterFromCounter;
case TIMER:
return Rollup.TimerFromTimer;
case GAUGE:
return Rollup.GaugeFromGauge;
case BF_BASIC:
return srcGran == Granularity.FULL ? Rollup.BasicFromRaw : Rollup.BasicFromBasic;
case SET:
return Rollup.SetFromSet;
default:
break;
}
throw new IllegalArgumentException(String.format("Cannot compute rollups for %s from %s", srcType.name(), srcGran.shortName()));
}
}