package storm.applications.bolt; import java.util.HashMap; import java.util.Map; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import static storm.applications.constants.MachineOutlierConstants.*; /** * DataStreamAnomalyScoreBolt keeps and update the stream anomaly score for each stream. * @author yexijiang * @param <T> * */ public class DataStreamAnomalyScoreBolt<T> extends AbstractBolt { private Map<String, StreamProfile<T>> streamProfiles; private double lambda; private double factor; private double threashold; private boolean shrinkNextRound; private long previousTimestamp; @Override public void initialize() { this.lambda = config.getDouble(Conf.ANOMALY_SCORER_LAMBDA); this.factor = Math.pow(Math.E, -lambda); this.threashold = 1 / (1 - factor) * 0.5; this.shrinkNextRound = false; this.streamProfiles = new HashMap<>(); this.previousTimestamp = 0; } @Override public void execute(Tuple input) { long timestamp = input.getLongByField(Field.TIMESTAMP); if (timestamp > previousTimestamp) { for (Map.Entry<String, StreamProfile<T>> streamProfileEntry : streamProfiles.entrySet()) { StreamProfile<T> streamProfile = streamProfileEntry.getValue(); if (shrinkNextRound == true) { streamProfile.streamAnomalyScore = 0; } collector.emit(new Values(streamProfileEntry.getKey(), streamProfile.streamAnomalyScore, previousTimestamp, streamProfile.currentDataInstance, streamProfile.currentDataInstanceScore)); } if (shrinkNextRound == true) { shrinkNextRound = false; } previousTimestamp = timestamp; } String id = input.getString(0); StreamProfile<T> profile = streamProfiles.get(id); double dataInstanceAnomalyScore = input.getDouble(1); if (profile == null) { profile = new StreamProfile<>(id, (T)input.getValue(3), dataInstanceAnomalyScore, input.getDouble(1)); streamProfiles.put(id, profile); } else { // update stream score profile.streamAnomalyScore = profile.streamAnomalyScore * factor + dataInstanceAnomalyScore; profile.currentDataInstance = (T)input.getValue(3); profile.currentDataInstanceScore = dataInstanceAnomalyScore; if (profile.streamAnomalyScore > threashold) { shrinkNextRound = true; } streamProfiles.put(id, profile); } collector.ack(input); } @Override public Fields getDefaultFields() { return new Fields(Field.ID, Field.STREAM_ANOMALY_SCORE, Field.TIMESTAMP, Field.OBSERVATION, Field.CUR_DATAINST_SCORE); } /** * Keeps the profile of the stream. * @author yexijiang * * @param <T> */ class StreamProfile<T> { String id; double streamAnomalyScore; T currentDataInstance; double currentDataInstanceScore; public StreamProfile(String id, T dataInstanceScore, double initialAnomalyScore, double currentDataInstanceScore) { this.id = id; this.streamAnomalyScore = initialAnomalyScore; this.currentDataInstance = dataInstanceScore; this.currentDataInstanceScore = currentDataInstanceScore; } } }