package edu.brown.hstore;
import java.io.File;
import java.util.Random;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import edu.brown.HStoreSiteTestUtil.LatchableProcedureCallback;
import org.voltdb.CatalogContext;
import org.voltdb.sysprocs.EvictTuples;
import org.voltdb.sysprocs.EvictHistory;
import org.voltdb.sysprocs.EvictedAccessHistory;
import org.voltdb.sysprocs.Statistics;
import org.voltdb.VoltSystemProcedure;
import org.junit.Before;
import org.junit.Test;
import org.voltdb.VoltTable;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Site;
import org.voltdb.catalog.Table;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.jni.ExecutionEngine;
import org.voltdb.utils.VoltTableUtil;
import org.voltdb.SysProcSelector;
import edu.brown.BaseTestCase;
import edu.brown.benchmark.AbstractProjectBuilder;
import edu.brown.benchmark.ycsb.YCSBConstants;
import edu.brown.benchmark.ycsb.YCSBProjectBuilder;
import edu.brown.catalog.CatalogUtil;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.conf.HStoreConf;
import edu.brown.utils.CollectionUtil;
import edu.brown.utils.FileUtil;
import edu.brown.utils.MathUtil;
import edu.brown.benchmark.ycsb.distributions.ZipfianGenerator;
/**
* Anti-Cache Manager Test Cases
* @author pavlo
*/
public class TestAntiCachePerformance extends BaseTestCase {
private static final int NOTIFY_TIMEOUT = 2000; // ms
private static final int NUM_PARTITIONS = 1;
private static final int NUM_TUPLES = 1000;
private static final int NUM_TRIALS = 1;
private static final int NUM_KEYS = 32766; // MAX
private static final String TARGET_TABLE = YCSBConstants.TABLE_NAME;
private static final long BLOCK_SIZE_1_MB = 1048576;
private static final long BLOCK_SIZE_2_MB = BLOCK_SIZE_1_MB * 2;
private static final long BLOCK_SIZE_3_MB = BLOCK_SIZE_1_MB * 3;
private static final long BLOCK_SIZE_4_MB = BLOCK_SIZE_1_MB * 4;
private static final long BLOCK_SIZE_8_MB = BLOCK_SIZE_1_MB * 8;
private static final long BLOCK_SIZE_16_MB = BLOCK_SIZE_1_MB * 16;
private static final double ZIPF_CONSTANT = 1.5;
private HStoreSite hstore_site;
private HStoreConf hstore_conf;
private File anticache_dir;
private Client client;
private PartitionExecutor executor;
@SuppressWarnings("unused")
private ExecutionEngine ee;
private Table catalog_tbl;
@SuppressWarnings("unused")
private int locators[];
private final AbstractProjectBuilder builder = new YCSBProjectBuilder() {
{
this.markTableEvictable(TARGET_TABLE);
this.addAllDefaults();
this.addProcedure(SelectBlaster.class);
}
};
@Before
public void setUp() throws Exception {
super.setUp(builder, false);
initializeCatalog(1, 1, NUM_PARTITIONS);
this.anticache_dir = FileUtil.getTempDirectory();
// Just make sure that the Table has the evictable flag set to true
this.catalog_tbl = getTable(TARGET_TABLE);
assertTrue(catalog_tbl.getEvictable());
this.locators = new int[] { catalog_tbl.getRelativeIndex() };
}
@Override
protected void tearDown() throws Exception {
if (this.client != null) this.client.close();
if (this.hstore_site != null) this.hstore_site.shutdown();
FileUtil.deleteDirectory(this.anticache_dir);
}
// --------------------------------------------------------------------------------------------
// UTILITY METHODS
// --------------------------------------------------------------------------------------------
private void startSite(boolean anticaching) throws Exception {
Site catalog_site = CollectionUtil.first(CatalogUtil.getCluster(catalog).getSites());
this.hstore_conf = HStoreConf.singleton();
this.hstore_conf.site.status_enable = false;
this.hstore_conf.site.anticache_enable = anticaching;
this.hstore_conf.site.anticache_profiling = true;
this.hstore_conf.site.anticache_check_interval = Integer.MAX_VALUE;
this.hstore_conf.site.anticache_dir = this.anticache_dir.getAbsolutePath();
this.hstore_site = createHStoreSite(catalog_site, hstore_conf);
this.executor = hstore_site.getPartitionExecutor(0);
assertNotNull(this.executor);
this.ee = executor.getExecutionEngine();
assertNotNull(this.executor);
this.client = createClient();
}
private void loadData(int tuples) throws Exception {
// Load in a bunch of dummy data for this table
VoltTable vt = CatalogUtil.getVoltTable(catalog_tbl);
assertNotNull(vt);
for (int i = 0; i < tuples; i++) {
Object row[] = VoltTableUtil.getRandomRow(catalog_tbl);
row[0] = i;
vt.addRow(row);
} // FOR
this.executor.loadTable(1000l, catalog_tbl, vt, false);
}
private Map<Integer, VoltTable> evictData(long block_size) throws Exception {
// System.err.printf("Evicting data...");
long start, stop;
String procName = VoltSystemProcedure.procCallName(EvictTuples.class);
CatalogContext catalogContext = this.getCatalogContext();
String tableNames[] = { TARGET_TABLE };
LatchableProcedureCallback callback = new LatchableProcedureCallback(catalogContext.numberOfPartitions);
long evictBytes[] = {block_size};
int numBlocks[] = { 1 };
start = System.currentTimeMillis(); // start timer
for (int partition : catalogContext.getAllPartitionIds()) {
// System.err.printf("Evicting data at partition %d...\n", partition);
Object params[] = { partition, tableNames, null, evictBytes, numBlocks };
boolean result = this.client.callProcedure(callback, procName, params);
assertTrue(result);
} // FOR
// Wait until they all finish
boolean result = callback.latch.await(NOTIFY_TIMEOUT, TimeUnit.MILLISECONDS);
assertTrue(callback.toString(), result);
stop = System.currentTimeMillis();
System.err.println(" Eviction time: " + (stop-start) + " ms");
// Construct a mapping BasePartition->VoltTable
Map<Integer, VoltTable> m = new TreeMap<Integer, VoltTable>();
for (ClientResponse cr : callback.responses) {
assertEquals(cr.toString(), Status.OK, cr.getStatus());
assertEquals(cr.toString(), 1, cr.getResults().length);
m.put(cr.getBasePartition(), cr.getResults()[0]);
} // FOR
assertEquals(catalogContext.numberOfPartitions, m.size());
// System.err.printf("Finished evicting data.");
// Our stats should now come back with one eviction executed
procName = VoltSystemProcedure.procCallName(EvictHistory.class);
ClientResponse cresponse = client.callProcedure(procName);
assertEquals(cresponse.toString(), Status.OK, cresponse.getStatus());
assertEquals(cresponse.toString(), 1, cresponse.getResults().length);
VoltTable result_table = cresponse.getResults()[0];
System.err.println(VoltTableUtil.format(result_table));
return (m);
}
private void UnevictData() throws Exception {
long start, stop;
String procName = "ReadRecord";
Object params[] = { 1 };
start = System.currentTimeMillis(); // start timer
ClientResponse cresponse = client.callProcedure(procName, params);
stop = System.currentTimeMillis();
System.err.println(" Uneviction Time: " + (stop-start) + " ms");
// procName = VoltSystemProcedure.procCallName(EvictedAccessHistory.class);
// cresponse = client.callProcedure(procName);
// assertEquals(cresponse.toString(), Status.OK, cresponse.getStatus());
// assertEquals(cresponse.toString(), 1, cresponse.getResults().length);
// VoltTable result = cresponse.getResults()[0];
// assertEquals(1, result.getRowCount());
// System.err.println(VoltTableUtil.format(result));
procName = VoltSystemProcedure.procCallName(Statistics.class);
Object params2[] = { SysProcSelector.ANTICACHE.name(), 0 };
cresponse = this.client.callProcedure(procName, params2);
assertEquals(cresponse.toString(), Status.OK, cresponse.getStatus());
assertEquals(cresponse.toString(), 1, cresponse.getResults().length);
VoltTable result = cresponse.getResults()[0];
assertEquals(1, result.getRowCount());
System.err.println(VoltTableUtil.format(result));
}
private void runSpeedTest() throws Exception {
Random rand = new Random(0);
double data[] = new double[NUM_TRIALS];
for (int trial = 0; trial < NUM_TRIALS; trial++) {
long keys[][] = new long[3][NUM_KEYS];
int num_reads = 0;
ZipfianGenerator zipf = new ZipfianGenerator(NUM_TUPLES, ZIPF_CONSTANT);
for (int i = 0; i < keys.length; i++) {
for (int j = 0; j < keys[i].length; j++) {
// keys[i][j] = rand.nextInt(NUM_TUPLES);
keys[i][j] = zipf.nextInt(NUM_TUPLES);
num_reads++;
} // FOR
} // FOR
Procedure proc = this.getProcedure(SelectBlaster.class);
Object params[] = new Object[keys.length];
for (int i = 0; i < keys.length; i++) {
params[i] = keys[i];
} // FOR
ClientResponse cresponse = this.client.callProcedure(proc.getName(), params);
assertEquals(Status.OK, cresponse.getStatus());
VoltTable results[] = cresponse.getResults();
assertEquals(1, results.length);
data[trial] = results[0].asScalarLong() / 1000000d;
System.err.printf(" TRIAL %d: %.2f ms [# of Reads=%d]\n", trial, data[trial], num_reads);
} // FOR
double avg = MathUtil.arithmeticMean(data);
System.err.printf("Elapsed Time: %.2f ms\n", avg);
}
// --------------------------------------------------------------------------------------------
// TEST CASES
// --------------------------------------------------------------------------------------------
/**
* testWithoutAntiCache
*/
@Test
public void testWithoutAntiCache() throws Exception {
this.startSite(false);
this.loadData(NUM_TUPLES);
this.runSpeedTest();
}
/**
* testWithAntiCache
*/
@Test
public void testWithAntiCache() throws Exception {
this.startSite(true);
this.loadData(NUM_TUPLES);
this.runSpeedTest();
}
/**
* testWithAntiCache
*/
@Test
public void testEvictData1() throws Exception {
this.startSite(true);
this.loadData(50000);
System.err.println("Testing with 1 MB block size: ");
this.evictData(BLOCK_SIZE_1_MB);
this.UnevictData();
}
/**
* testWithAntiCache
*/
@Test
public void testEvictData2() throws Exception {
this.startSite(true);
this.loadData(50000);
System.err.println("Testing with 2 MB block size: ");
this.evictData(BLOCK_SIZE_2_MB);
this.UnevictData();
}
// /**
// * testWithAntiCache
// */
// @Test
// public void testEvictData3() throws Exception {
// this.startSite(true);
// this.loadData(50000);
// System.err.println("Testing with 3 MB block size: ");
// this.evictData(BLOCK_SIZE_3_MB);
// this.UnevictData();
// }
// /**
// * testWithAntiCache
// */
// @Test
// public void testEvictData4() throws Exception {
// this.startSite(true);
// this.loadData(50000);
// System.err.println("Testing with 4 MB block size: ");
// this.evictData(BLOCK_SIZE_4_MB);
// this.UnevictData();
// }
//
// /**
// * testWithAntiCache
// */
// @Test
// public void testEvictData5() throws Exception {
// this.startSite(true);
// this.loadData(50000);
// System.err.println("Testing with 8 MB block size: ");
// this.evictData(BLOCK_SIZE_8_MB);
// this.UnevictData();
// }
//
// /**
// * testWithAntiCache
// */
// @Test
// public void testEvictData6() throws Exception {
// this.startSite(true);
// this.loadData(50000);
// System.err.println("Testing with 16 MB block size: ");
// this.evictData(BLOCK_SIZE_16_MB);
// this.UnevictData();
// }
}