package streamcruncher.test.func.generic;
import java.sql.Timestamp;
import java.util.List;
import org.testng.Assert;
import streamcruncher.api.StreamCruncherException;
import streamcruncher.api.aggregator.DiffBaselineProvider;
import streamcruncher.api.artifact.RowSpec;
import streamcruncher.api.artifact.RowSpec.Info;
import streamcruncher.test.func.BatchResult;
import streamcruncher.test.func.MultiStreamEventGenerator;
/*
* Author: Ashwin Jayaprakash Date: Mar 14, 2007 Time: 8:10:02 PM
*/
/**
* <p>
* This Test demonstrates the use of the Extra parameters allowed in the
* In-built Aggregation functions. The <code>$diff</code> parameter provided,
* instructs the function to compute the difference between the New value and
* the Old value, instead of outputting the New value directly had the extra
* clause not been provided.
* </p>
* <p>
* This example demonstrates a case where a Servers in a Datacenter keeps
* pinging the ESP Kernel with Heartbeat Events every 5 seconds. With the use of
* the <code>pinned</code> clause, the Partitions/Window remain in memory even
* if the Window becomes empty - when the Server fails to ping within 5 seconds;
* usually if the Server has gone down or the Network has partitioned. By using
* the <code>count(xxcolumn $diff)</code> function definition, the Window and
* hence the Aggregate remain in memory even if the Ping Event is not received
* in time. The "count" dips to 0 and the "diff" (New value - Old value) turns
* negative, which is when the Alarm is raised. The <code>avg(..)</code>
* function can also be used to identify missed alerts when the avgrage value
* becomes <code>null</code>.
* </p>
* There are 3 <code>count(.. )</code> functions defined for
* <code>levelb</code>. One does not use the <code>$diff</code> clause and
* so, it provides the absolute count of Events over the past 5 seconds. Two of
* them use the <code>$diff</code> clause, out of which one uses a custom
* {@link DiffBaselineProvider}, which always uses 0 as the baseline. The other
* one, for which no custom Provider has been declared, uses the default
* Provider.
* <p>
* <p>
* Thus, Aggregates can be used to detect Trends in the Stream.
* </p>
* <p>
* This test uses only one Stream. The other Streams are dormant.
* </p>
*/
// todo Test Restart with Provider - Diff and WindowSize.
public abstract class ClusterHealthTest extends MultiStreamEventGenerator {
/**
* Timestamp column will be filled later.
*/
protected final Event[] eventSequence = new Event[] {
new Event(EventType.stg1_event, new Object[] { "datacntr1", "ultrasparc14", null, 1L },
3),
new Event(EventType.pause, new Object[] { 5000L }, -1),
new Event(EventType.stg1_event, new Object[] { "datacntr1", "ultrasparc14", null, 2L },
3),
new Event(EventType.pause, new Object[] { 5000L }, -1),
new Event(EventType.stg1_event, new Object[] { "datacntr1", "ultrasparc14", null, 3L },
3),
// Long wait. Restart counting.
new Event(EventType.pause, new Object[] { 10000L }, -1),
new Event(EventType.stg1_event, new Object[] { "datacntr1", "ultrasparc14", null, 4L },
3),
new Event(EventType.pause, new Object[] { 5000L }, -1),
new Event(EventType.stg1_event, new Object[] { "datacntr1", "ultrasparc14", null, 5L },
3) };
@Override
protected String[] getResultColumnNames() {
return new String[] { "levela", "levelb", "change", "change2", "beats", "testcol1",
"testcol2", "testcol3" };
}
@Override
protected String[] getResultColumnTypes() {
return new String[] { RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
java.lang.Integer.class.getName(), java.lang.Integer.class.getName(),
java.lang.Integer.class.getName(), java.lang.Double.class.getName(),
java.lang.Double.class.getName(), java.lang.Double.class.getName() };
}
@Override
protected String[] getStage1ColumnTypes() {
return new String[] { RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
java.sql.Timestamp.class.getName(), java.lang.Long.class.getName() };
}
@Override
protected String[] getStage2ColumnTypes() {
return new String[] { RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
java.sql.Timestamp.class.getName(), java.lang.Long.class.getName() };
}
@Override
protected String[] getStage3ColumnTypes() {
return new String[] { RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
RowSpec.addInfo(java.lang.String.class.getName(), Info.SIZE, 15),
java.sql.Timestamp.class.getName(), java.lang.Long.class.getName() };
}
@Override
protected String getRQL() {
String csv = getRQLColumnsCSV();
return "select " + csv + " from stg1_event (partition by levela, levelb"
+ " store last 5 seconds with pinned count(event_id $diff) as change,"
+ " count(event_id $diff 'DiffBaseline/CountBL') as change2,"
+ " count(event_id) as beats, avg(event_id $diff) as testcol1,"
+ " avg(event_id $diff 'DiffBaseline/AvgBL') as testcol2,"
+ " avg(event_id) as testcol3) as heartbeat"
+ " where heartbeat.$row_status is new;";
}
@Override
protected void beforeQueryParse() {
super.beforeQueryParse();
try {
cruncher.registerProvider("DiffBaseline/CountBL", CountDiffBaselineProvider.class);
cruncher.registerProvider("DiffBaseline/AvgBL", StatsDiffBaselineProvider.class);
}
catch (StreamCruncherException e) {
throw new RuntimeException(e);
}
}
@Override
protected Event[] createEventArray() {
return eventSequence;
}
@Override
protected void verify(List<BatchResult> results) {
String hash = "";
System.out.println("--Results--");
for (BatchResult result : results) {
System.out.println("Batch created at: " + new Timestamp(result.getTimestamp())
+ ". Rows: " + result.getRows().size());
List<Object[]> rows = result.getRows();
System.out.println(" Batch results");
for (Object[] objects : rows) {
System.out.print(" ");
for (Object object : objects) {
System.out.print(object + " ");
}
/*
* "levela", "levelb", "change", "change2","beats", "testcol1",
* "testcol2", "testcol3".
*/
String levela = (String) objects[0];
String levelb = (String) objects[1];
Integer change = objects[2] == null ? null : ((Number) objects[2]).intValue();
Integer change2 = objects[3] == null ? null : ((Number) objects[3]).intValue();
Integer beats = objects[4] == null ? null : ((Number) objects[4]).intValue();
Double testcol1 = objects[5] == null ? null : ((Number) objects[5]).doubleValue();
Double testcol2 = objects[6] == null ? null : ((Number) objects[6]).doubleValue();
Double testcol3 = objects[7] == null ? null : ((Number) objects[7]).doubleValue();
hash = levela + " " + levelb + " " + change + " " + change2 + " " + beats + " "
+ testcol1 + " " + testcol2 + " " + testcol3;
System.out.println();
}
}
String expectedHash = "datacntr1 ultrasparc14 -1 0 0 -5.0 0.0 null";
Assert.assertEquals(hash, expectedHash, "Last entry does not match expectations");
}
/**
* Custom Provider for the <code>count</code> function. Therefore, works
* only on {@link Integer}s.
*/
public static class CountDiffBaselineProvider extends DiffBaselineProvider<Integer> {
public CountDiffBaselineProvider() {
super();
System.out.println("Using CountDiffBaselineProvider");
}
/**
* @return Fixed Baseline. Always Zero.
*/
@Override
public Integer getBaseline(Integer oldValue, Integer newValue) {
return 0;
}
}
/**
* Custom Provider for the Statistics functions (<code>avg,variance..</code>).
* Therefore, works only on {@link Double}s.
*/
public static class StatsDiffBaselineProvider extends DiffBaselineProvider<Double> {
public StatsDiffBaselineProvider() {
super();
System.out.println("Using StatsDiffBaselineProvider");
}
/**
* @return Fixed Baseline. Always Zero.
*/
@Override
public Double getBaseline(Double oldValue, Double newValue) {
return 0.0;
}
}
}