package org.spotter.ext.detection.appHiccups;
import java.util.List;
import org.aim.api.exceptions.InstrumentationException;
import org.aim.api.exceptions.MeasurementException;
import org.aim.api.measurement.dataset.Dataset;
import org.aim.api.measurement.dataset.DatasetCollection;
import org.aim.api.measurement.dataset.ParameterSelection;
import org.aim.artifacts.probes.ResponsetimeProbe;
import org.aim.artifacts.records.ResponseTimeRecord;
import org.aim.artifacts.scopes.EntryPointScope;
import org.aim.description.InstrumentationDescription;
import org.aim.description.builder.InstrumentationDescriptionBuilder;
import org.lpe.common.config.GlobalConfiguration;
import org.lpe.common.extension.IExtension;
import org.lpe.common.util.NumericPairList;
import org.spotter.core.ProgressManager;
import org.spotter.core.chartbuilder.AnalysisChartBuilder;
import org.spotter.core.detection.AbstractDetectionController;
import org.spotter.core.detection.IDetectionController;
import org.spotter.core.detection.IExperimentReuser;
import org.spotter.exceptions.WorkloadException;
import org.spotter.ext.detection.appHiccups.strategies.BucketStrategy;
import org.spotter.ext.detection.appHiccups.strategies.DBSCANStrategy;
import org.spotter.ext.detection.appHiccups.strategies.MovingPercentileStrategy;
import org.spotter.ext.detection.appHiccups.utils.Hiccup;
import org.spotter.ext.detection.appHiccups.utils.HiccupDetectionConfig;
import org.spotter.ext.detection.utils.Utils;
import org.spotter.shared.configuration.ConfigKeys;
import org.spotter.shared.result.model.SpotterResult;
/**
* Detection Controller for the Application Hiccups problem.
*
* @author Alexander Wert
*
*/
public class AppHiccupsController extends AbstractDetectionController implements IExperimentReuser {
private String analysisStrategy;
private double maxHiccupTimeProportion = AppHiccupsExtension.MAX_HICCUPS_TIME_PROPORTION_DEFAULT;
private HiccupDetectionConfig hiccupDetectionConfig = new HiccupDetectionConfig();
private IHiccupAnalysisStrategy analysisStrategyImpl;
/**
* Constructor.
*
* @param provider
* extension provider
*/
public AppHiccupsController(IExtension<IDetectionController> provider) {
super(provider);
}
@Override
public void loadProperties() {
analysisStrategy = getProblemDetectionConfiguration().getProperty(AppHiccupsExtension.APP_HICCUPS_STRATEGY_KEY,
AppHiccupsExtension.MVA_STRATEGY);
String mvaWindowSize = getProblemDetectionConfiguration().getProperty(
HiccupDetectionConfig.MOVING_AVERAGE_WINDOW_SIZE_KEY,
String.valueOf(HiccupDetectionConfig.MOVING_AVERAGE_WINDOW_SIZE_DEFAULT));
hiccupDetectionConfig.setMvaWindowSize(Integer.parseInt(mvaWindowSize));
String maxHiccupTimeProportionStr = getProblemDetectionConfiguration().getProperty(
AppHiccupsExtension.MAX_HICCUPS_TIME_PROPORTION_KEY,
String.valueOf(AppHiccupsExtension.MAX_HICCUPS_TIME_PROPORTION_DEFAULT));
maxHiccupTimeProportion = Double.parseDouble(maxHiccupTimeProportionStr);
switch (analysisStrategy) {
case AppHiccupsExtension.MVA_STRATEGY:
analysisStrategyImpl = new MovingPercentileStrategy();
break;
case AppHiccupsExtension.DBSCAN_STRATEGY:
analysisStrategyImpl = new DBSCANStrategy();
break;
case AppHiccupsExtension.BUCKET_STRATEGY:
analysisStrategyImpl = new BucketStrategy();
break;
default:
analysisStrategyImpl = new MovingPercentileStrategy();
}
}
@Override
protected SpotterResult analyze(DatasetCollection data) {
long perfReqThreshold = GlobalConfiguration.getInstance().getPropertyAsInteger(
ConfigKeys.PERFORMANCE_REQUIREMENT_THRESHOLD, ConfigKeys.DEFAULT_PERFORMANCE_REQUIREMENT_THRESHOLD);
double perfReqConfidence = GlobalConfiguration.getInstance().getPropertyAsDouble(
ConfigKeys.PERFORMANCE_REQUIREMENT_CONFIDENCE, ConfigKeys.DEFAULT_PERFORMANCE_REQUIREMENT_CONFIDENCE);
SpotterResult result = new SpotterResult();
result.setDetected(false);
Dataset rtDataset = data.getDataSet(ResponseTimeRecord.class);
if (rtDataset == null || rtDataset.size() == 0) {
result.setDetected(false);
result.addMessage("Instrumentation achieved no results for the given scope!");
return result;
}
for (String operation : rtDataset.getValueSet(ResponseTimeRecord.PAR_OPERATION, String.class)) {
ParameterSelection selectOperation = new ParameterSelection().select(ResponseTimeRecord.PAR_OPERATION,
operation);
Dataset operationSpecificDataset = selectOperation.applyTo(rtDataset);
NumericPairList<Long, Double> responseTimeSeries = Utils.toTimestampRTPairs(operationSpecificDataset);
if (responseTimeSeries.size() <= 5) {
continue;
}
// sort chronologically
responseTimeSeries.sort();
List<Hiccup> hiccups = analysisStrategyImpl.findHiccups(responseTimeSeries, hiccupDetectionConfig,
perfReqThreshold, perfReqConfidence, getResultManager(), result);
long experimentDuration = responseTimeSeries.getKeyMax() - responseTimeSeries.getKeyMin();
long hiccupsDuration = 0;
for (Hiccup hiccup : hiccups) {
hiccupsDuration += hiccup.getEndTimestamp() - hiccup.getStartTimestamp();
}
if (hiccups.size() > 1 && hiccupsDuration < maxHiccupTimeProportion * experimentDuration) {
result.addMessage("Detected hiccup behaviour in operation: " + operation);
result.setDetected(true);
createChart(result, operation, responseTimeSeries, hiccups, perfReqThreshold);
}
}
return result;
}
private void createChart(SpotterResult result, String operation, NumericPairList<Long, Double> responseTimeSeries,
List<Hiccup> hiccups, long perfReqThreshold) {
AnalysisChartBuilder chartBuilder = AnalysisChartBuilder.getChartBuilder();
String operationName = operation.contains("(")?operation.substring(0, operation.indexOf("(")):operation;
chartBuilder.startChart(operationName, "Experiment Time [ms]", "Response Time [ms]");
chartBuilder.addTimeSeries(responseTimeSeries, "Response Times");
chartBuilder.addHorizontalLine(perfReqThreshold, "Perf. Requirement");
long minTimestamp = responseTimeSeries.getKeyMin();
long maxTimestamp = responseTimeSeries.getKeyMax();
double minRT = responseTimeSeries.getValueMin().doubleValue();
NumericPairList<Long, Double> hiccupSeries = new NumericPairList<>();
hiccupSeries.add(minTimestamp, minRT);
for (Hiccup hiccup : hiccups) {
hiccupSeries.add(hiccup.getStartTimestamp(), minRT);
hiccupSeries.add(hiccup.getStartTimestamp(), hiccup.getMaxHiccupResponseTime());
hiccupSeries.add(hiccup.getEndTimestamp(), hiccup.getMaxHiccupResponseTime());
hiccupSeries.add(hiccup.getEndTimestamp(), minRT);
}
hiccupSeries.add(maxTimestamp, minRT);
chartBuilder.addTimeSeriesWithLine(hiccupSeries, "Hiccups");
getResultManager().storeImageChartResource(chartBuilder, "Hiccups", result);
}
@Override
public void executeExperiments() throws InstrumentationException, MeasurementException, WorkloadException {
executeDefaultExperimentSeries(this, 1, createInstrumentationDescription());
}
@Override
public InstrumentationDescription getInstrumentationDescription() {
// no additional instrumentation required
return null;
}
private InstrumentationDescription createInstrumentationDescription() {
InstrumentationDescriptionBuilder idBuilder = new InstrumentationDescriptionBuilder();
idBuilder.newAPIScopeEntity(EntryPointScope.class.getName()).addProbe(ResponsetimeProbe.MODEL_PROBE)
.entityDone();
return idBuilder.build();
}
@Override
public long getExperimentSeriesDuration() {
return ProgressManager.getInstance().calculateDefaultExperimentSeriesDuration(1);
}
}