package storm.applications.bolt;
import java.util.ArrayList;
import java.util.List;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Tuple;
import backtype.storm.tuple.Values;
import static storm.applications.constants.MachineOutlierConstants.*;
import storm.applications.util.math.BFPRT;
/**
* The AlertTriggerBolt triggers an alert if a stream is identified as abnormal.
* @author yexijiang
*
*/
public class AlertTriggerBolt extends AbstractBolt {
private static final double dupper = Math.sqrt(2);
private long previousTimestamp;
private List<Tuple> streamList;
private double minDataInstanceScore = Double.MAX_VALUE;
private double maxDataInstanceScore = 0;
@Override
public void initialize() {
previousTimestamp = 0;
streamList = new ArrayList<>();
}
@Override
public void execute(Tuple input) {
long timestamp = input.getLongByField(Field.TIMESTAMP);
if (timestamp > previousTimestamp) {
// new batch of stream scores
if (!streamList.isEmpty()) {
List<Tuple> abnormalStreams = this.identifyAbnormalStreams();
int medianIdx = (int) streamList.size() / 2;
double minScore = abnormalStreams.get(0).getDouble(1);
double medianScore = abnormalStreams.get(medianIdx).getDouble(1);
for (int i = 0; i < abnormalStreams.size(); ++i) {
Tuple streamProfile = abnormalStreams.get(i);
double streamScore = streamProfile.getDouble(1);
double curDataInstScore = streamProfile.getDouble(4);
boolean isAbnormal = false;
// current stream score deviates from the majority
if ((streamScore > 2 * medianScore - minScore) && (streamScore > minScore + 2 * dupper)) {
// check whether cur data instance score return to normal
if (curDataInstScore > 0.1 + minDataInstanceScore) {
isAbnormal = true;
}
}
if (isAbnormal) {
collector.emit(new Values(streamProfile.getString(0), streamScore, streamProfile.getLong(2), isAbnormal, streamProfile.getValue(3)));
}
}
streamList.clear();
minDataInstanceScore = Double.MAX_VALUE;
maxDataInstanceScore = 0;
}
previousTimestamp = timestamp;
}
double dataInstScore = input.getDouble(4);
if (dataInstScore > maxDataInstanceScore) {
maxDataInstanceScore = dataInstScore;
}
if (dataInstScore < minDataInstanceScore) {
minDataInstanceScore = dataInstScore;
}
streamList.add(input);
collector.ack(input);
}
@Override
public Fields getDefaultFields() {
return new Fields(Field.ANOMALY_STREAM, Field.STREAM_ANOMALY_SCORE, Field.TIMESTAMP, Field.IS_ABNORMAL, Field.OBSERVATION);
}
/**
* Identify the abnormal streams.
* @return
*/
private List<Tuple> identifyAbnormalStreams() {
List<Tuple> abnormalStreamList = new ArrayList<>();
int medianIdx = (int)(streamList.size() / 2);
BFPRT.bfprt(streamList, medianIdx);
abnormalStreamList.addAll(streamList);
return abnormalStreamList;
}
}