/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB L.L.C.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package org.voltdb.regressionsuites;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.Test;
import org.voltdb.BackendTarget;
import org.voltdb.VoltProcedure;
import org.voltdb.VoltTable;
import org.voltdb.VoltDB.Configuration;
import org.voltdb.benchmark.tpcc.TPCCProjectBuilder;
import org.voltdb.benchmark.tpcc.procedures.InsertNewOrder;
import org.voltdb.catalog.LoadCatalogToString;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.ProcCallException;
import org.voltdb.client.ProcedureCallback;
import org.voltdb.client.SyncCallback;
import org.voltdb.types.TimestampType;
import edu.brown.hstore.Hstoreservice.Status;
/**
* Tests a mix of multi-partition and single partition procedures on a
* mix of replicated and partititioned tables on a mix of single-site and
* multi-site VoltDB instances.
*
*/
public class TestCatalogUpdateSuite extends RegressionSuite {
// procedures used by these tests
@SuppressWarnings("unchecked")
static final Class<? extends VoltProcedure> BASEPROCS[] = (Class<? extends VoltProcedure>[])new Class<?>[]
{ org.voltdb.benchmark.tpcc.procedures.InsertNewOrder.class,
org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
org.voltdb.benchmark.tpcc.procedures.delivery.class };
@SuppressWarnings("unchecked")
static final Class<? extends VoltProcedure> EXPANDEDPROCS[] = (Class<? extends VoltProcedure>[])new Class<?>[]
{ org.voltdb.benchmark.tpcc.procedures.InsertNewOrder.class,
org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
org.voltdb.benchmark.tpcc.procedures.delivery.class,
org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class };
@SuppressWarnings("unchecked")
static final Class<? extends VoltProcedure> CONFLICTPROCS[] = (Class<? extends VoltProcedure>[])new Class<?>[]
{ org.voltdb.catalog.InsertNewOrder.class,
org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
org.voltdb.benchmark.tpcc.procedures.delivery.class };
@SuppressWarnings("unchecked")
static final Class<? extends VoltProcedure> SOMANYPROCS[] = (Class<? extends VoltProcedure>[])new Class<?>[]
{ org.voltdb.benchmark.tpcc.procedures.InsertNewOrder.class,
org.voltdb.benchmark.tpcc.procedures.SelectAll.class,
org.voltdb.benchmark.tpcc.procedures.neworder.class,
org.voltdb.benchmark.tpcc.procedures.ostatByCustomerId.class,
org.voltdb.benchmark.tpcc.procedures.ostatByCustomerName.class,
org.voltdb.benchmark.tpcc.procedures.paymentByCustomerId.class,
org.voltdb.benchmark.tpcc.procedures.paymentByCustomerName.class,
org.voltdb.benchmark.tpcc.procedures.slev.class,
org.voltdb.benchmark.tpcc.procedures.delivery.class };
/**
* 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 TestCatalogUpdateSuite(String name) {
super(name);
}
AtomicInteger m_outstandingCalls = new AtomicInteger(0);
class CatTestCallback implements ProcedureCallback {
final Status m_expectedStatus;
CatTestCallback(Status expectedStatus) {
m_expectedStatus = expectedStatus;
m_outstandingCalls.incrementAndGet();
}
@Override
public void clientCallback(ClientResponse clientResponse) {
m_outstandingCalls.decrementAndGet();
if (m_expectedStatus != clientResponse.getStatus()) {
if (clientResponse.getStatusString() != null)
System.err.println(clientResponse.getStatusString());
if (clientResponse.getException() != null)
clientResponse.getException().printStackTrace();
assertTrue(false);
}
}
}
public void blockUntilNoOutstandingTransactions() {
}
public void testUpdate() throws Exception {
Client client = getClient();
String newCatalogURL;
VoltTable[] results;
CatTestCallback callback;
loadSomeData(client, 0, 25);
client.drain();
testStuffThatShouldObviouslyFail(client);
// asynchronously call some random inserts
loadSomeData(client, 25, 25);
// add a procedure "InsertOrderLineBatched"
newCatalogURL = VoltServerConfig.getPathToCatalogForTest("catalogupdate-cluster-expanded.jar");
callback = new CatTestCallback(Status.OK);
client.callProcedure(callback, "@UpdateApplicationCatalog", newCatalogURL);
client.drain();
// don't care if this succeeds or fails.
// calling the new proc before the cat change returns is not guaranteed to work
// we just hope it doesn't crash anything
int x = 3;
SyncCallback cb = new SyncCallback();
client.callProcedure(cb,
org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
new long[] {x}, new long[] {x}, x, new long[] {x},
new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
new double[] {x}, new String[] {"a"});
cb.waitForResponse();
// make sure the previous catalog change has completed
client.drain();
// now calling the new proc better work
x = 2;
client.callProcedure(org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
new long[] {x}, new long[] {x}, x, new long[] {x},
new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
new double[] {x}, new String[] {"a"});
loadSomeData(client, 50, 5);
// this is a do nothing change... shouldn't affect anything
newCatalogURL = VoltServerConfig.getPathToCatalogForTest("catalogupdate-cluster-expanded.jar");
results = client.callProcedure("@UpdateApplicationCatalog", newCatalogURL).getResults();
assertTrue(results.length == 1);
client.drain();
// now calling the new proc better work
x = 4;
client.callProcedure(org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
new long[] {x}, new long[] {x}, x, new long[] {x},
new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
new double[] {x}, new String[] {"a"});
loadSomeData(client, 55, 5);
// remove the procedure we just added async
newCatalogURL = VoltServerConfig.getPathToCatalogForTest("catalogupdate-cluster-base.jar");
callback = new CatTestCallback(Status.OK);
client.callProcedure(callback, "@UpdateApplicationCatalog", newCatalogURL);
// don't care if this works now
x = 4;
cb = new SyncCallback();
client.callProcedure(cb,
org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
new long[] {x}, new long[] {x}, x, new long[] {x},
new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
new double[] {x}, new String[] {"a"});
cb.waitForResponse();
// make sure the previous catalog change has completed
client.drain();
// now calling the new proc better fail
x = 5;
cb = new SyncCallback();
client.callProcedure(cb,
org.voltdb.benchmark.tpcc.procedures.InsertOrderLineBatched.class.getSimpleName(),
new long[] {x}, new long[] {x}, x, new long[] {x},
new long[] {x}, new long[] {x}, new TimestampType[] { new TimestampType() }, new long[] {x},
new double[] {x}, new String[] {"a"});
cb.waitForResponse();
assertNotSame(cb.getResponse().getStatus(), Status.OK);
loadSomeData(client, 60, 5);
// change the insert new order procedure
newCatalogURL = VoltServerConfig.getPathToCatalogForTest("catalogupdate-cluster-conflict.jar");
results = client.callProcedure("@UpdateApplicationCatalog", newCatalogURL).getResults();
assertTrue(results.length == 1);
// call the new proc and make sure the one we want gets run
results = client.callProcedure(InsertNewOrder.class.getSimpleName(), 100, 100, 100, 100, 100, 100, 1.0, "a").getResults();
assertEquals(1, results.length);
assertEquals(1776, results[0].asScalarLong());
// load a big catalog change just to make sure nothing fails horribly
newCatalogURL = VoltServerConfig.getPathToCatalogForTest("catalogupdate-cluster-many.jar");
results = client.callProcedure("@UpdateApplicationCatalog", newCatalogURL).getResults();
assertTrue(results.length == 1);
loadSomeData(client, 65, 5);
client.drain();
assertTrue(true);
}
public void loadSomeData(Client client, int start, int count) throws IOException, ProcCallException {
for (int i = start; i < (start + count); i++) {
CatTestCallback callback = new CatTestCallback(Status.OK);
client.callProcedure(callback, InsertNewOrder.class.getSimpleName(), i, i, i);
}
}
public void queryAndVerifySomeData() {
}
public void testStuffThatShouldObviouslyFail(Client client) throws UnsupportedEncodingException {
// this fails because it tries to change schema
String newCatalogURL;
newCatalogURL = VoltServerConfig.getPathToCatalogForTest("catalogupdate-cluster-addtables.jar");
try {
client.callProcedure("@UpdateApplicationCatalog", newCatalogURL);
fail();
}
catch (Exception e) {
assertTrue(e.getMessage().startsWith("The requested catalog change is not"));
}
// this fails because the catalog URL isn't a real thing
URL url = LoadCatalogToString.class.getResource("catalog.txt");
newCatalogURL = URLDecoder.decode(url.getPath(), "UTF-8");
try {
client.callProcedure("@UpdateApplicationCatalog", newCatalogURL);
fail();
}
catch (Exception e) {
assertTrue(e.getMessage().startsWith("Unable to read from catalog"));
}
}
/**
* Build a list of the tests that will be run when TestTPCCSuite gets run by JUnit.
* Use helper classes that are part of the RegressionSuite framework.
* This particular class runs all tests on the the local JNI backend with both
* one and two partition configurations, as well as on the hsql backend.
*
* @return The TestSuite containing all the tests to be run.
*/
static public Test suite() {
// the suite made here will all be using the tests from this class
MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder(TestCatalogUpdateSuite.class);
/////////////////////////////////////////////////////////////
// CONFIG #1: 1 Local Site/Partitions running on JNI backend
/////////////////////////////////////////////////////////////
// get a server config for the native backend with one sites/partitions
//VoltServerConfig config = new LocalSingleProcessServer("catalogupdate-local-base.jar", 2, BackendTarget.NATIVE_EE_JNI);
VoltServerConfig config = new LocalCluster("catalogupdate-cluster-base.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI);
// build up a project builder for the workload
TPCCProjectBuilder project = new TPCCProjectBuilder();
project.addDefaultSchema();
project.addDefaultPartitioning();
project.addProcedures(BASEPROCS);
// build the jarfile
config.compile(project);
// add this config to the set of tests to run
builder.addServerConfig(config);
/////////////////////////////////////////////////////////////
// DELTA CATALOGS FOR TESTING
/////////////////////////////////////////////////////////////
// Build a new catalog
//config = new LocalSingleProcessServer("catalogupdate-local-addtables.jar", 2, BackendTarget.NATIVE_EE_JNI);
config = new LocalCluster("catalogupdate-cluster-addtables.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI);
project = new TPCCProjectBuilder();
project.addDefaultSchema();
project.addSchema(TestCatalogUpdateSuite.class.getResource("testorderby-ddl.sql").getPath());
project.addDefaultPartitioning();
project.addProcedures(BASEPROCS);
config.compile(project);
// Build a new catalog
//config = new LocalSingleProcessServer("catalogupdate-local-expanded.jar", 2, BackendTarget.NATIVE_EE_JNI);
config = new LocalCluster("catalogupdate-cluster-expanded.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI);
project = new TPCCProjectBuilder();
project.addDefaultSchema();
project.addDefaultPartitioning();
project.addProcedures(EXPANDEDPROCS);
config.compile(project);
// Build a new catalog
//config = new LocalSingleProcessServer("catalogupdate-local-conflict.jar", 2, BackendTarget.NATIVE_EE_JNI);
config = new LocalCluster("catalogupdate-cluster-conflict.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI);
project = new TPCCProjectBuilder();
project.addDefaultSchema();
project.addDefaultPartitioning();
project.addProcedures(CONFLICTPROCS);
config.compile(project);
// Build a new catalog
//config = new LocalSingleProcessServer("catalogupdate-local-many.jar", 2, BackendTarget.NATIVE_EE_JNI);
config = new LocalCluster("catalogupdate-cluster-many.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI);
project = new TPCCProjectBuilder();
project.addDefaultSchema();
project.addDefaultPartitioning();
project.addProcedures(SOMANYPROCS);
config.compile(project);
return builder;
}
}