/*
* #!
* %
* Copyright (C) 2014 - 2016 Humboldt-Universität zu Berlin
* %
* 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 de.hub.cs.dbis.aeolus.monitoring.throughput;
import java.util.HashMap;
import java.util.Map.Entry;
import backtype.storm.topology.OutputFieldsDeclarer;
import backtype.storm.tuple.Fields;
import backtype.storm.tuple.Values;
/**
* {@link AbstractThroughputCounter} counts the number of incoming or outgoing tuples of a single Spout or Bolt task. It
* computes a separate count for each logical stream and the overall count over all streams. The collected statistical
* values can be emitted to a regular stream within a topology.<br />
* <br />
* <strong>Statistics report stream output schema:</strong> {@code <ts:}{@link Long}{@code ,streamId:}{@link String}
* {@code ,count:}{@link Long} {@code ,delta:}{@link Long}{@code >}<br />
* <br />
* where {@code ts} is the timestamp when reporting is triggered, {@code streamId} is the stream name the statistics
* belong to, {@code count} is the overall count, and {@code delta} is the count since the last report. <em>Be aware,
* that each declared input or output stream is prefixed with</em> {@code in::} <em>or</em> {@code out::}
* <em>respectively. Additionally, the stream IDs</em> {@code in} <em>and</em> {@code out}
* <em>are used for the counters over all incoming or outgoing streams.</em>
*
* @author mjsax
*/
abstract class AbstractThroughputCounter {
/** The name of the timestamp attribute. */
public final static String TS_ATTRIBUTE = "ts";
/** The name of the stream ID attribute. */
public final static String STREAM_ID_ATTRIBUTE = "streamId";
/** The name of the task ID attribute. */
public final static String TASK_ID_ATTRIBUTE = "taskId";
/** The name of the overall count attribute. */
public final static String COUNT_ATTRIBUTE = "count";
/** The name of the delta count attribute. */
public final static String DELTA_ATTRIBUTE = "delta";
/** The name of the throughput attribute. */
public final static String THROUGHPUT_ATTRIBUTE = "throughput";
/** The index of the timestamp attribute. */
public final static int TS_INDEX = 0;
/** The index of the stream ID attribute. */
public final static int STREAM_ID_INDEX = 1;
/** The index of the overall count attribute. */
public final static int COUNT_INDEX = 2;
/** The index of the delta count attribute. */
public final static int DELTA_INDEX = 3;
/**
* Indicates if this counter is used to count input or output streams. The value is {@code true} or {@code false}
* for input or output streams, respectively.
*/
private final boolean inputOrOutput;
/** The ID of the task reporting the metric. */
protected final Integer taskId;
/** Holds the overall count per stream. */
private final HashMap<String, Counter> counter = new HashMap<String, Counter>();
/** Hold the count per stream since the last report. */
private final HashMap<String, Counter> deltaCounter = new HashMap<String, Counter>();
/** The overall count over all streams. */
private long overallCount = 0;
/**
* Instantiates a new throughput-counter.
*
* @param inputOrOutput
* Indicates if input ({@code true}) or output ({@code false}) streams are monitored.
* @param taskId
* The task ID.
*/
public AbstractThroughputCounter(boolean inputOrOutput, Integer taskId) {
this.inputOrOutput = inputOrOutput;
this.taskId = taskId;
}
/**
* Increases the counter of an input stream.
*
* @param streamId
* The ID of the input stream.
*/
void countIn(String streamId) {
assert (this.inputOrOutput);
this.doCount("in::" + streamId);
}
/**
* Increases the count of an output stream.
*
* @param streamId
* The ID of the output stream.
*/
void countOut(String streamId) {
assert (!this.inputOrOutput);
this.doCount("out::" + streamId);
}
/**
* Performs the actual counting for input and output streams.
*
* @param streamId
* The ID of the stream.
*/
private synchronized void doCount(String streamId) {
Counter c = this.deltaCounter.get(streamId);
if(c == null) {
c = new Counter();
this.deltaCounter.put(streamId, c);
}
++c.counter;
}
/**
* Reports all count values of each stream and the overall count over all streams.
*
* @param ts
* The timestamp when this report is triggered.
* @param factor
* The normalization factor for reported values "per second".
*/
synchronized void reportCount(long ts, double factor) {
long overallDelta = 0;
for(Entry<String, Counter> count : this.deltaCounter.entrySet()) {
String streamId = count.getKey();
Counter delta = count.getValue();
Counter c = this.counter.get(streamId);
if(c == null) {
c = new Counter();
this.counter.put(streamId, c);
}
c.counter += delta.counter;
overallDelta += delta.counter;
this.doEmit(new Values(new Long(ts), streamId, taskId, new Long(c.counter), new Long(delta.counter),
new Long((long)(delta.counter / factor))));
count.setValue(new Counter());
}
this.overallCount += overallDelta;
String id;
if(this.inputOrOutput) {
id = "in";
} else {
id = "out";
}
this.doEmit(new Values(new Long(ts), id, taskId, new Long(this.overallCount), new Long(overallDelta), new Long(
(long)(overallDelta / factor))));
}
/**
* Reports a single statistical value.
*
* @param statsTuple
* The report tuple to be emitted.
*/
abstract void doEmit(Values statsTuple);
/**
* Declares an report stream with schema {@code <ts:}{@link Long}{@code ,streamId:}{@link String} {@code ,count:}
* {@link Long} {@code ,delta:}{@link Long}{@code >}
*
* @param reportStream
* The ID of the report stream to be declared.
* @param declarer
* The declarer object the report stream is declared to.
*/
static void declareStatsStream(String reportStream, OutputFieldsDeclarer declarer) {
declarer.declareStream(reportStream, new Fields(TS_ATTRIBUTE, STREAM_ID_ATTRIBUTE, TASK_ID_ATTRIBUTE,
COUNT_ATTRIBUTE, DELTA_ATTRIBUTE, THROUGHPUT_ATTRIBUTE));
}
}