package edu.brown.api.results;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;
import org.voltdb.VoltTable;
import org.voltdb.VoltType;
import org.voltdb.catalog.Database;
import org.voltdb.client.ClientResponse;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.utils.JSONSerializable;
import edu.brown.utils.JSONUtil;
public class ResponseEntries implements JSONSerializable {
public static class Entry implements Comparable<Entry> {
private long timestamp;
private int txnNameId;
public int clientId;
public boolean singlePartition;
public int basePartition;
public Status status;
public int resultSize;
public long clusterRoundTrip;
public long clientRoundTrip;
public int restartCounter;
private Entry() {
// Needed for deserialization
}
public Entry(ClientResponse cr, int clientId, int txnNameId, long timestamp) {
this.txnNameId = txnNameId;
this.clientId = clientId;
this.timestamp = timestamp;
this.singlePartition = cr.isSinglePartition();
this.basePartition = cr.getBasePartition();
this.status = cr.getStatus();
this.resultSize = cr.getResultsSize();
this.clusterRoundTrip = cr.getClusterRoundtrip();
this.clientRoundTrip = cr.getClientRoundtrip();
this.restartCounter = cr.getRestartCounter();
}
@Override
public int compareTo(Entry other) {
if (this.timestamp != other.timestamp) {
return (int)(this.timestamp - other.timestamp);
}
if (this.basePartition != other.basePartition) {
return (this.basePartition - other.basePartition);
}
if (this.clusterRoundTrip != other.clusterRoundTrip) {
return (int)(this.clusterRoundTrip - other.clusterRoundTrip);
}
if (this.clientRoundTrip != other.clientRoundTrip) {
return (int)(this.clientRoundTrip - other.clientRoundTrip);
}
if (this.restartCounter != other.restartCounter) {
return (int)(this.restartCounter - other.restartCounter);
}
if (this.resultSize != other.resultSize) {
return (int)(this.resultSize - other.resultSize);
}
if (this.txnNameId != other.txnNameId) {
return (int)(this.txnNameId - other.txnNameId);
}
if (this.clientId != other.clientId) {
return (int)(this.clientId - other.clientId);
}
if (this.status != other.status) {
return (this.status.compareTo(other.status));
}
if (this.singlePartition != other.singlePartition) {
return (this.singlePartition ? 1 : -1);
}
return (0);
}
}
private final Collection<Entry> entries = new ConcurrentLinkedQueue<Entry>();
public ResponseEntries() {
// Needed for deserialization
}
/**
* Copy constructor. This is not a deep copy
* @param other
*/
public ResponseEntries(ResponseEntries other) {
this.entries.addAll(other.entries);
}
public void add(ClientResponse cr, int clientId, int txnNameId, long timestamp) {
this.entries.add(new Entry(cr, clientId, txnNameId, timestamp));
}
public void addAll(ResponseEntries other) {
this.entries.addAll(other.entries);
}
public void clear() {
this.entries.clear();
}
public boolean isEmpty() {
return (this.entries.isEmpty());
}
public int size() {
return (this.entries.size());
}
public static VoltTable toVoltTable(ResponseEntries re, String txnNames[]) {
VoltTable.ColumnInfo[] RESULT_COLS = {
new VoltTable.ColumnInfo("TIMESTAMP", VoltType.BIGINT),
new VoltTable.ColumnInfo("CLIENT ID", VoltType.INTEGER),
new VoltTable.ColumnInfo("PROCEDURE", VoltType.STRING),
new VoltTable.ColumnInfo("SINGLE-PARTITION", VoltType.STRING),
new VoltTable.ColumnInfo("BASE PARTITION", VoltType.INTEGER),
new VoltTable.ColumnInfo("STATUS", VoltType.STRING),
new VoltTable.ColumnInfo("RESULT SIZE", VoltType.INTEGER),
new VoltTable.ColumnInfo("CLUSTER ROUNDTRIP", VoltType.BIGINT),
new VoltTable.ColumnInfo("CLIENT ROUNDTRIP", VoltType.BIGINT),
new VoltTable.ColumnInfo("RESTARTS", VoltType.INTEGER),
};
VoltTable vt = new VoltTable(RESULT_COLS);
List<Entry> list = new ArrayList<Entry>(re.entries);
Collections.sort(list);
for (Entry e : list) {
Object row[] = {
e.timestamp,
e.clientId,
txnNames[e.txnNameId],
Boolean.toString(e.singlePartition),
e.basePartition,
e.status.name(),
e.resultSize,
e.clusterRoundTrip,
e.clientRoundTrip,
e.restartCounter,
};
vt.addRow(row);
} // FOR
return (vt);
}
// ----------------------------------------------------------------------------
// SERIALIZATION METHODS
// ----------------------------------------------------------------------------
@Override
public void load(File input_path, Database catalog_db) throws IOException {
JSONUtil.load(this, catalog_db, input_path);
}
@Override
public void save(File output_path) throws IOException {
JSONUtil.save(this, output_path);
}
@Override
public String toJSONString() {
return (JSONUtil.toJSONString(this));
}
@Override
public void toJSON(JSONStringer stringer) throws JSONException {
// I did it this way because way because I think it will be faster to parse
// TIMESTAMPS
stringer.key("TIMESTAMPS").array();
for (Entry e : this.entries)
stringer.value(e.timestamp);
stringer.endArray();
// CLIENT IDS
stringer.key("CLIENT").array();
for (Entry e : this.entries)
stringer.value(e.clientId);
stringer.endArray();
// TXN NAME IDS
stringer.key("TXN").array();
for (Entry e : this.entries)
stringer.value(e.txnNameId);
stringer.endArray();
// SINGLE-PARTITION
stringer.key("SINGLEP").array();
for (Entry e : this.entries)
stringer.value(e.singlePartition);
stringer.endArray();
// BASE-PARTITION
stringer.key("BASEP").array();
for (Entry e : this.entries)
stringer.value(e.basePartition);
stringer.endArray();
// STATUS
stringer.key("STATUS").array();
for (Entry e : this.entries)
stringer.value(e.status.ordinal());
stringer.endArray();
// RESULT-SIZE
stringer.key("SIZE").array();
for (Entry e : this.entries)
stringer.value(e.resultSize);
stringer.endArray();
// CLUSTER ROUND-TRIP
stringer.key("CLUSTERRT").array();
for (Entry e : this.entries)
stringer.value(e.clusterRoundTrip);
stringer.endArray();
// CLIENT ROUND-TRIP
stringer.key("CLIENTRT").array();
for (Entry e : this.entries)
stringer.value(e.clientRoundTrip);
stringer.endArray();
// RESTART COUNTER
stringer.key("RESTARTS").array();
for (Entry e : this.entries)
stringer.value(e.restartCounter);
stringer.endArray();
}
@Override
public void fromJSON(JSONObject json_object, Database catalog_db) throws JSONException {
JSONArray jsonArrays[] = {
json_object.getJSONArray("TIMESTAMPS"),
json_object.getJSONArray("CLIENT"),
json_object.getJSONArray("TXN"),
json_object.getJSONArray("SINGLEP"),
json_object.getJSONArray("BASEP"),
json_object.getJSONArray("STATUS"),
json_object.getJSONArray("SIZE"),
json_object.getJSONArray("CLUSTERRT"),
json_object.getJSONArray("CLIENTRT"),
json_object.getJSONArray("RESTARTS")
};
assert(jsonArrays.length == json_object.names().length()) :
String.format("%d != %d", jsonArrays.length, json_object.names().length());
int expected = -1;
for (JSONArray arr : jsonArrays) {
if (expected != -1)
assert(expected == arr.length()) :
String.format("%d != %d", expected, arr.length());
expected = arr.length();
} // FOR
Status statuses[] = Status.values();
for (int i = 0; i < expected; i++) {
Entry e = new Entry();
int col = 0;
e.timestamp = jsonArrays[col++].getLong(i);
e.clientId = jsonArrays[col++].getInt(i);
e.txnNameId = jsonArrays[col++].getInt(i);
e.singlePartition = jsonArrays[col++].getBoolean(i);
e.basePartition = jsonArrays[col++].getInt(i);
e.status = statuses[jsonArrays[col++].getInt(i)];
e.resultSize = jsonArrays[col++].getInt(i);
e.clusterRoundTrip = jsonArrays[col++].getLong(i);
e.clientRoundTrip = jsonArrays[col++].getLong(i);
e.restartCounter = jsonArrays[col++].getInt(i);
this.entries.add(e);
} // FOR
}
}