/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB Inc.
*
* 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.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
import junit.framework.Test;
import org.voltdb.BackendTarget;
import org.voltdb.CatalogContext;
import org.voltdb.DefaultSnapshotDataTarget;
import org.voltdb.VoltTable;
import org.voltdb.VoltTable.ColumnInfo;
import org.voltdb.VoltType;
import org.voltdb.catalog.CatalogMap;
import org.voltdb.catalog.Cluster;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Site;
import org.voltdb.catalog.Table;
import org.voltdb.client.Client;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.ProcCallException;
import org.voltdb.sysprocs.saverestore.SaveRestoreTestProjectBuilder;
import org.voltdb.utils.SnapshotVerifier;
import com.sun.net.httpserver.Authenticator.Success;
import edu.brown.benchmark.ycsb.YCSBConstants;
import edu.brown.benchmark.ycsb.YCSBLoader;
import edu.brown.benchmark.ycsb.YCSBProjectBuilder;
import edu.brown.benchmark.ycsb.procedures.ReadRecord;
import edu.brown.catalog.CatalogUtil;
import edu.brown.hstore.Hstoreservice.Status;
import edu.brown.hstore.cmdlog.CommandLogReader;
import edu.brown.hstore.cmdlog.CommandLogWriter;
import edu.brown.hstore.cmdlog.LogEntry;
/**
* Test the SnapshotSave and SnapshotRestore system procedures
*/
public class TestSnapshotSaveAndRestoreReplicatedTable extends RegressionSuite {
private static final String TMPDIR = "./snapshot";
private static final String TESTNONCE = "testnonce";
private static final int ALLOWEXPORT = 0;
private static int NUM_SITES = -1;
private static int NUM_PARTITIONS = -1;
public TestSnapshotSaveAndRestoreReplicatedTable(String name) {
super(name);
}
@Override
public void setUp() {
deleteTestFiles();
super.setUp();
DefaultSnapshotDataTarget.m_simulateFullDiskWritingChunk = false;
DefaultSnapshotDataTarget.m_simulateFullDiskWritingHeader = false;
org.voltdb.sysprocs.SnapshotRegistry.clear();
}
@Override
public void tearDown() {
try {
deleteTestFiles();
super.tearDown();
} catch (final Exception e) {
e.printStackTrace();
}
}
private void deleteTestFiles() {
FilenameFilter cleaner = new FilenameFilter() {
public boolean accept(File dir, String file) {
return file.startsWith(TESTNONCE) || file.endsWith(".vpt") || file.endsWith(".digest") || file.endsWith(".tsv") || file.endsWith(".csv");
}
};
File tmp_dir = new File(TMPDIR);
File[] tmp_files = tmp_dir.listFiles(cleaner);
for (File tmp_file : tmp_files) {
tmp_file.delete();
}
}
private static synchronized void setUpSnapshotDir(){
try {
File snapshotDir = new File(TMPDIR);
if (!snapshotDir.exists()) {
boolean result = snapshotDir.mkdir();
if (result) {
System.out.println("Created snapshot directory.");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private VoltTable createReplicatedTable(int numberOfItems, int indexBase, StringBuilder sb) {
return createReplicatedTable(numberOfItems, indexBase, sb, false);
}
private VoltTable createReplicatedTable(int numberOfItems, int indexBase, StringBuilder sb, boolean generateCSV) {
VoltTable repl_table = new VoltTable(new ColumnInfo("RT_ID", VoltType.INTEGER), new ColumnInfo("RT_NAME", VoltType.STRING), new ColumnInfo("RT_INTVAL", VoltType.INTEGER), new ColumnInfo(
"RT_FLOATVAL", VoltType.FLOAT));
char delimeter = generateCSV ? ',' : '\t';
for (int i = indexBase; i < numberOfItems + indexBase; i++) {
String stringVal = null;
String escapedVal = null;
if (sb != null) {
if (generateCSV) {
int escapable = i % 5;
switch (escapable) {
case 0:
stringVal = "name_" + i;
escapedVal = "name_" + i;
break;
case 1:
stringVal = "na,me_" + i;
escapedVal = "\"na,me_" + i + "\"";
break;
case 2:
stringVal = "na\"me_" + i;
escapedVal = "\"na\"\"me_" + i + "\"";
break;
case 3:
stringVal = "na\rme_" + i;
escapedVal = "\"na\rme_" + i + "\"";
break;
case 4:
stringVal = "na\nme_" + i;
escapedVal = "\"na\nme_" + i + "\"";
break;
}
} else {
int escapable = i % 5;
switch (escapable) {
case 0:
stringVal = "name_" + i;
escapedVal = "name_" + i;
break;
case 1:
stringVal = "na\tme_" + i;
escapedVal = "na\\tme_" + i;
break;
case 2:
stringVal = "na\nme_" + i;
escapedVal = "na\\nme_" + i;
break;
case 3:
stringVal = "na\rme_" + i;
escapedVal = "na\\rme_" + i;
break;
case 4:
stringVal = "na\\me_" + i;
escapedVal = "na\\\\me_" + i;
break;
}
}
} else {
stringVal = "name_" + i;
}
Object[] row = new Object[] { i, stringVal, i, new Double(i) };
if (sb != null) {
sb.append(i).append(delimeter).append(escapedVal).append(delimeter);
sb.append(i).append(delimeter).append(new Double(i).toString()).append('\n');
}
repl_table.addRow(row);
}
return repl_table;
}
private VoltTable createPartitionedTable(int numberOfItems, int indexBase) {
VoltTable partition_table = new VoltTable(new ColumnInfo("PT_ID", VoltType.INTEGER), new ColumnInfo("PT_NAME", VoltType.STRING), new ColumnInfo("PT_INTVAL", VoltType.INTEGER), new ColumnInfo(
"PT_FLOATVAL", VoltType.FLOAT));
for (int i = indexBase; i < numberOfItems + indexBase; i++) {
Object[] row = new Object[] { i, "name_" + i, i, new Double(i) };
partition_table.addRow(row);
}
return partition_table;
}
private VoltTable[] loadTable(Client client, String tableName, VoltTable table) {
VoltTable[] results = null;
int allowExport = 0;
try {
client.callProcedure("@LoadMultipartitionTable", tableName, table);
} catch (Exception ex) {
ex.printStackTrace();
fail("loadTable exception: " + ex.getMessage());
}
return results;
}
private void loadLargeReplicatedTable(Client client, String tableName, int itemsPerChunk, int numChunks) {
loadLargeReplicatedTable(client, tableName, itemsPerChunk, numChunks, false, null);
}
private void loadLargeReplicatedTable(Client client, String tableName, int itemsPerChunk, int numChunks, boolean generateCSV, StringBuilder sb) {
for (int i = 0; i < numChunks; i++) {
VoltTable repl_table = createReplicatedTable(itemsPerChunk, i * itemsPerChunk, sb, generateCSV);
loadTable(client, tableName, repl_table);
}
if (sb != null) {
sb.trimToSize();
}
}
private void loadLargePartitionedTable(Client client, String tableName, int itemsPerChunk, int numChunks) {
for (int i = 0; i < numChunks; i++) {
VoltTable part_table = createPartitionedTable(itemsPerChunk, i * itemsPerChunk);
loadTable(client, tableName, part_table);
}
}
private VoltTable[] saveTables(Client client) {
VoltTable[] results = null;
try {
results = client.callProcedure("@SnapshotSave", TMPDIR, TESTNONCE, (byte) 1).getResults();
} catch (Exception ex) {
ex.printStackTrace();
fail("SnapshotSave exception: " + ex.getMessage());
}
return results;
}
private void checkTable(Client client, String tableName, String orderByCol, int expectedRows) {
if (expectedRows > 200000) {
System.out.println("Table too large to retrieve with select *");
System.out.println("Skipping integrity check");
}
VoltTable result = null;
try {
result = client.callProcedure("SaveRestoreSelect", tableName).getResults()[0];
} catch (Exception e) {
e.printStackTrace();
}
final int rowCount = result.getRowCount();
assertEquals(expectedRows, rowCount);
// System.out.println("Check table :: \n"+result);
int i = 0;
while (result.advanceRow()) {
assertEquals(i, result.getLong(0));
assertEquals("name_" + i, result.getString(1));
assertEquals(i, result.getLong(2));
assertEquals(new Double(i), result.getDouble(3));
++i;
}
}
private void validateSnapshot(boolean expectSuccess) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
PrintStream original = System.out;
try {
System.setOut(ps);
String args[] = new String[] { TESTNONCE, "--dir", TMPDIR };
SnapshotVerifier.main(args);
ps.flush();
String reportString = baos.toString("UTF-8");
System.err.println("Validate Snapshot :"+reportString);
if (expectSuccess) {
assertTrue(reportString.startsWith("Snapshot valid\n"));
System.err.println("Validate Snapshot :" + "Snapshot valid");
} else {
assertTrue(reportString.startsWith("Snapshot corrupted\n"));
System.err.println("Validate Snapshot :" + "Snapshot corrupted");
}
} catch (UnsupportedEncodingException e) {
} finally {
System.setOut(original);
}
}
public void testSaveAndRestoreReplicatedTable() throws IOException, InterruptedException, ProcCallException {
System.out.println("Starting testSaveAndRestoreReplicatedTable");
deleteTestFiles();
setUpSnapshotDir();
int num_replicated_items_per_chunk = 3;
int num_replicated_chunks = 2;
Client client = getClient();
loadLargeReplicatedTable(client, "REPLICATED_TESTER", num_replicated_items_per_chunk, num_replicated_chunks);
VoltTable[] results = null;
results = saveTables(client);
validateSnapshot(true);
VoltTable[] results_tmp = null;
results_tmp = client.callProcedure("@Statistics", "table", 0).getResults();
System.out.println("@Statistics after saveTables :");
System.out.println(results_tmp[0]);
// Kill and restart all the execution sites.
m_config.shutDown();
m_config.startUp();
client = getClient();
try {
results = client.callProcedure("@SnapshotRestore", TMPDIR, TESTNONCE, ALLOWEXPORT).getResults();
System.out.println(results[0]);
while (results[0].advanceRow()) {
if (results[0].getString("RESULT").equals("FAILURE")) {
fail(results[0].getString("ERR_MSG"));
}
}
} catch (Exception ex) {
ex.printStackTrace();
fail("SnapshotRestore exception: " + ex.getMessage());
}
System.out.println("@Statistics after restore:");
results = client.callProcedure("@Statistics", "table", 0).getResults();
System.out.println(results[0]);
checkTable(client, "REPLICATED_TESTER", "RT_ID", num_replicated_items_per_chunk * num_replicated_chunks);
int foundItem = 0;
while (results[0].advanceRow()) {
if (results[0].getString("TABLE_NAME").equals("REPLICATED_TESTER")) {
++foundItem;
assertEquals((num_replicated_chunks * num_replicated_items_per_chunk), results[0].getLong("TUPLE_COUNT"));
}
}
// make sure all sites were loaded
//assertEquals(3, foundItem);
validateSnapshot(true);
}
/**
* Build a list of the tests to be run. Use the regression suite helpers to
* allow multiple back ends. JUnit magic that uses the regression suite
* helper classes.
*/
static public Test suite() {
MultiConfigSuiteBuilder builder = new MultiConfigSuiteBuilder(TestSnapshotSaveAndRestoreReplicatedTable.class);
SaveRestoreTestProjectBuilder project = new SaveRestoreTestProjectBuilder("snapshot-VoltDB-project");
project.addAllDefaults();
VoltServerConfig m_config = null;
setUpSnapshotDir();
NUM_SITES = 2;
NUM_PARTITIONS = 2;
m_config = new LocalCluster("snapshot-"+NUM_SITES+"-site-"+NUM_PARTITIONS+"-partition.jar", NUM_SITES, NUM_PARTITIONS, 1, BackendTarget.NATIVE_EE_JNI);
//NUM_SITES = 1;
//NUM_PARTITIONS = 2;
//m_config = new LocalSingleProcessServer("snapshot-"+NUM_SITES+"-site-"+NUM_PARTITIONS+"-partition.jar", NUM_PARTITIONS, BackendTarget.NATIVE_EE_JNI);
boolean success = m_config.compile(project);
assert (success);
builder.addServerConfig(m_config);
return builder;
}
}