package edu.brown.benchmark.seats;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import junit.framework.Test;
import org.voltdb.BackendTarget;
import org.voltdb.CatalogContext;
import org.voltdb.VoltSystemProcedure;
import org.voltdb.VoltTable;
import org.voltdb.VoltType;
import org.voltdb.catalog.Column;
import org.voltdb.catalog.Table;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.ProcCallException;
import org.voltdb.client.ProcedureCallback;
import org.voltdb.regressionsuites.LocalCluster;
import org.voltdb.regressionsuites.LocalSingleProcessServer;
import org.voltdb.regressionsuites.MultiConfigSuiteBuilder;
import org.voltdb.regressionsuites.RegressionSuite;
import org.voltdb.regressionsuites.RegressionSuiteUtil;
import org.voltdb.regressionsuites.VoltServerConfig;
import org.voltdb.sysprocs.AdHoc;
import org.voltdb.types.TimestampType;
import org.voltdb.utils.Pair;
import edu.brown.HStoreSiteTestUtil.WrapperProcedureCallback;
import edu.brown.benchmark.seats.SEATSClient.Transaction;
import edu.brown.benchmark.seats.util.SEATSHistogramUtil;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.StringUtil;
/**
* Simple test suite for the SEATS benchmark
* @author pavlo
*/
public class TestSEATSSuite extends RegressionSuite {
private static final String PREFIX = "seats";
private static final double SCALEFACTOR = 0.01;
/**
* Constructor needed for JUnit. Should just pass on parameters to superclass.
* @param name The name of the method to test. This is just passed to the superclass.
*/
public TestSEATSSuite(String name) {
super(name);
}
public SEATSLoader initializeSEATSDatabase(final CatalogContext catalogContext, final Client client) throws Exception {
SEATSProfile.clearCachedProfile();
File dataDir = SEATSHistogramUtil.findDataDir();
assert(dataDir != null);
String args[] = {
"NOCONNECTIONS=true",
"CLIENT.SCALEFACTOR=" + SCALEFACTOR,
"BENCHMARK.DATADIR=" + dataDir.getAbsolutePath()
};
SEATSLoader loader = new SEATSLoader(args) {
{
this.setCatalogContext(catalogContext);
this.setClientHandle(client);
}
@Override
public CatalogContext getCatalogContext() {
return (catalogContext);
}
@Override
public double getScaleFactor() {
return (SCALEFACTOR);
}
};
loader.load();
return (loader);
}
public SEATSClient initializeSEATSClient(final CatalogContext catalogContext, final Client client) throws Exception {
File dataDir = SEATSHistogramUtil.findDataDir();
assert(dataDir != null);
String args[] = {
"NOCONNECTIONS=true",
"CLIENT.SCALEFACTOR=" + SCALEFACTOR,
"BENCHMARK.DATADIR=" + dataDir.getAbsolutePath()
};
SEATSClient benchmarkClient = new SEATSClient(args) {
{
this.setCatalogContext(catalogContext);
this.setClientHandle(client);
}
@Override
public CatalogContext getCatalogContext() {
return (catalogContext);
}
@Override
public double getScaleFactor() {
return (SCALEFACTOR);
}
};
benchmarkClient.getProfile().loadProfile(client);
// Fire off a FindOpenSeats so that we can prime ourselves
Pair<Object[], ProcedureCallback> pair = benchmarkClient.getFindOpenSeatsParams();
assert(pair != null);
Object params[] = pair.getFirst();
WrapperProcedureCallback callback = new WrapperProcedureCallback(1, pair.getSecond());
client.callProcedure(callback, Transaction.FIND_OPEN_SEATS.getExecName(), params);
// Wait until it's done
boolean ret = callback.latch.await(1000, TimeUnit.MILLISECONDS);
assertTrue(callback.latch.toString(), ret);
return (benchmarkClient);
}
/**
* testInitialize
*/
public void testInitialize() throws Exception {
Client client = this.getClient();
CatalogContext catalogContext = this.getCatalogContext();
SEATSLoader loader = this.initializeSEATSDatabase(catalogContext, client);
SEATSProfile profile = loader.getProfile();
assertNotNull(profile);
Set<String> allTables = new HashSet<String>();
CollectionUtil.addAll(allTables, SEATSConstants.TABLES_SCALING);
CollectionUtil.addAll(allTables, SEATSConstants.TABLES_DATAFILES);
Map<String, Long> expected = new HashMap<String, Long>();
expected.put(SEATSConstants.TABLENAME_FLIGHT, profile.num_flights);
expected.put(SEATSConstants.TABLENAME_CUSTOMER, profile.num_customers);
expected.put(SEATSConstants.TABLENAME_RESERVATION, profile.num_reservations);
String procName = VoltSystemProcedure.procCallName(AdHoc.class);
for (String tableName : allTables) {
String query = "SELECT COUNT(*) FROM " + tableName;
ClientResponse cresponse = client.callProcedure(procName, query);
assertEquals(Status.OK, cresponse.getStatus());
VoltTable results[] = cresponse.getResults();
assertEquals(1, results.length);
long count = results[0].asScalarLong();
if (expected.containsKey(tableName)) {
assertEquals(tableName, expected.get(tableName).longValue(), count);
}
else {
assertTrue(tableName + " -> " + count, count > 0);
}
} // FOR
// Make sure that our FLIGHT rows are correct
String query = "SELECT * FROM " + SEATSConstants.TABLENAME_FLIGHT;
ClientResponse cresponse = RegressionSuiteUtil.sql(client, query);
VoltTable results[] = cresponse.getResults();
assertEquals(profile.num_flights, results[0].getRowCount());
Table tbl = catalogContext.getTableByName(SEATSConstants.TABLENAME_FLIGHT);
assertEquals(tbl.getColumns().size(), results[0].getColumnCount());
List<Column> notNullCols = new ArrayList<Column>();
for (Column col : tbl.getColumns()) {
if (col.getNullable() == false) notNullCols.add(col);
}
while (results[0].advanceRow()) {
for (Column col : notNullCols) {
results[0].get(col.getIndex());
assertFalse(col.fullName(), results[0].wasNull());
}
} // WHILE
}
/**
* testSaveLoadProfile
*/
public void testSaveLoadProfile() throws Exception {
Client client = this.getClient();
CatalogContext catalogContext = this.getCatalogContext();
SEATSLoader loader = this.initializeSEATSDatabase(catalogContext, client);
SEATSProfile orig = loader.getProfile();
assertNotNull(orig);
String sql = "SELECT CFP_NUM_FLIGHTS, CFP_NUM_CUSTOMERS, CFP_NUM_RESERVATIONS " +
" FROM " + SEATSConstants.TABLENAME_CONFIG_PROFILE;
ClientResponse cresponse = RegressionSuiteUtil.sql(client, sql);
VoltTable results[] = cresponse.getResults();
assertEquals(1, results.length);
assertEquals(1, results[0].getRowCount());
assertTrue(results[0].advanceRow());
assertEquals("num_flights", orig.num_flights, results[0].getLong("CFP_NUM_FLIGHTS"));
assertEquals("num_customers", orig.num_customers, results[0].getLong("CFP_NUM_CUSTOMERS"));
assertEquals("num_reservations", orig.num_reservations, results[0].getLong("CFP_NUM_RESERVATIONS"));
SEATSProfile copy = new SEATSProfile(catalogContext, orig.rng);
assert(copy.airport_histograms.isEmpty());
copy.loadProfile(client);
try {
assertEquals("scale_factor", orig.scale_factor, copy.scale_factor);
// BUSTED??? assertEquals(orig.airport_max_customer_id, copy.airport_max_customer_id);
// BUSTED??? assertEquals(orig.flight_start_date, copy.flight_start_date);
// BUSTED??? assertEquals(orig.flight_upcoming_date, copy.flight_upcoming_date);
assertEquals("flight_past_days", orig.flight_past_days, copy.flight_past_days);
assertEquals("flight_future_days", orig.flight_future_days, copy.flight_future_days);
assertEquals("num_flights", orig.num_flights, copy.num_flights);
assertEquals("num_customers", orig.num_customers, copy.num_customers);
assertEquals("num_reservations", orig.num_reservations, copy.num_reservations);
assertEquals("histograms", orig.histograms, copy.histograms);
assertEquals("airport_histograms", orig.airport_histograms, copy.airport_histograms);
} catch (AssertionError ex) {
System.err.println(StringUtil.columns(orig.toString(), copy.toString()));
throw ex;
}
}
/**
* testFindOpenSeats
*/
public void testFindOpenSeats() throws Exception {
Client client = this.getClient();
CatalogContext catalogContext = this.getCatalogContext();
this.initializeSEATSDatabase(catalogContext, client);
SEATSClient benchmarkClient = this.initializeSEATSClient(catalogContext, client);
assertNotNull(benchmarkClient);
Transaction txn = Transaction.FIND_OPEN_SEATS;
Pair<Object[], ProcedureCallback> pair = benchmarkClient.getFindOpenSeatsParams();
assertNotNull(pair);
Object params[] = pair.getFirst();
ClientResponse cresponse = null;
try {
cresponse = client.callProcedure(txn.getExecName(), params);
assertEquals(Status.OK, cresponse.getStatus());
} catch (ProcCallException ex) {
cresponse = ex.getClientResponse();
assertEquals(cresponse.toString(), Status.ABORT_USER, cresponse.getStatus());
}
VoltTable results[] = cresponse.getResults();
assertEquals(2, results.length);
// Make sure that the flight ids all match
assertEquals(1, results[0].getRowCount());
assertTrue(results[0].advanceRow());
long flight_id = results[0].getLong("F_ID");
assertNotSame(VoltType.NULL_BIGINT, flight_id);
while (results[1].advanceRow()) {
long seat_flight_id = results[1].getLong("F_ID");
assertEquals(flight_id, seat_flight_id);
} // WHILE
}
/**
* testNewReservation
*/
public void testNewReservation() throws Exception {
Client client = this.getClient();
CatalogContext catalogContext = this.getCatalogContext();
this.initializeSEATSDatabase(catalogContext, client);
ClientResponse cresponse;
String sql;
Random rng = new Random();
long r_id = 1000l;
long c_id = 10; // rng.nextInt((int)profile.num_customers);
long f_id = 10; // rng.nextInt((int)profile.num_flights);
long seatnum = rng.nextInt(SEATSConstants.FLIGHTS_NUM_SEATS);
long attrs[] = new long[SEATSConstants.NEW_RESERVATION_ATTRS_SIZE];
Arrays.fill(attrs, 9999l);
Object params[] = { r_id, c_id, f_id, seatnum, 100d, attrs, new TimestampType() };
// Check the number of available seats for this flight
sql = "SELECT F_SEATS_LEFT " +
" FROM " + SEATSConstants.TABLENAME_FLIGHT +
" WHERE F_ID = " + f_id;
cresponse = RegressionSuiteUtil.sql(client, sql);
long orig_num_seats = cresponse.getResults()[0].asScalarLong();
assert(orig_num_seats > 0);
// Then insert a new reservation
Transaction txn = Transaction.NEW_RESERVATION;
cresponse = client.callProcedure(txn.getExecName(), params);
assertEquals(cresponse.toString(), Status.OK, cresponse.getStatus());
// Then check that the number of available seats is reduced by one
cresponse = RegressionSuiteUtil.sql(client, sql);
long new_num_seats = cresponse.getResults()[0].asScalarLong();
assert(new_num_seats > 0);
assertEquals(orig_num_seats-1, new_num_seats);
// Make sure that our customer has the reservation
sql = "SELECT R_C_ID " +
" FROM " + SEATSConstants.TABLENAME_RESERVATION +
" WHERE R_F_ID = " + f_id +
" AND R_SEAT = " + seatnum;
cresponse = RegressionSuiteUtil.sql(client, sql);
long r_c_id = cresponse.getResults()[0].asScalarLong();
assertEquals(c_id, r_c_id);
}
// /**
// * testFindFlights
// */
// public void testFindFlights() throws Exception {
// Client client = this.getClient();
// CatalogContext catalogContext = this.getCatalogContext();
// this.initializeSEATSDatabase(catalogContext, client);
// SEATSClient benchmarkClient = this.initializeSEATSClient(catalogContext, client);
// assertNotNull(benchmarkClient);
//
// Transaction txn = Transaction.FIND_FLIGHTS;
// Pair<Object[], ProcedureCallback> pair = benchmarkClient.getFindFlightsParams();
// assertNotNull(pair);
// Object params[] = pair.getFirst();
//
// ClientResponse cresponse = null;
// try {
// cresponse = client.callProcedure(txn.getExecName(), params);
// assertEquals(Status.OK, cresponse.getStatus());
// } catch (ProcCallException ex) {
// cresponse = ex.getClientResponse();
// assertEquals(cresponse.toString(), Status.ABORT_USER, cresponse.getStatus());
// }
// }
public static Test suite() {
VoltServerConfig config = null;
// the suite made here will all be using the tests from this class
MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder(TestSEATSSuite.class);
builder.setGlobalConfParameter("client.scalefactor", SCALEFACTOR);
// build up a project builder for the benchmark
SEATSProjectBuilder project = new SEATSProjectBuilder();
project.addAllDefaults();
boolean success;
/////////////////////////////////////////////////////////////
// CONFIG #1: 1 Local Site/Partition running on JNI backend
/////////////////////////////////////////////////////////////
config = new LocalSingleProcessServer(PREFIX + "-1part.jar", 1, BackendTarget.NATIVE_EE_JNI);
success = config.compile(project);
assert(success);
builder.addServerConfig(config);
/////////////////////////////////////////////////////////////
// CONFIG #2: 1 Local Site with 2 Partitions running on JNI backend
/////////////////////////////////////////////////////////////
// config = new LocalSingleProcessServer(PREFIX + "-2part.jar", 2, BackendTarget.NATIVE_EE_JNI);
// success = config.compile(project);
// assert(success);
// builder.addServerConfig(config);
////////////////////////////////////////////////////////////
// CONFIG #3: cluster of 2 nodes running 2 site each, one replica
////////////////////////////////////////////////////////////
config = new LocalCluster(PREFIX + "-cluster.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI);
success = config.compile(project);
assert(success);
builder.addServerConfig(config);
return builder;
}
}