/* This file is part of VoltDB. * Copyright (C) 2008-2017 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.statistics; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.hsqldb_voltpatches.HSQLInterface; import org.voltdb.VoltTable; import org.voltdb.VoltTable.ColumnInfo; import org.voltdb.VoltType; import org.voltdb.client.Client; import org.voltdb.client.ProcCallException; import org.voltdb.regressionsuites.StatisticsTestSuiteBase; import org.voltdb.utils.MiscUtils; import junit.framework.Test; public class TestStatisticsSuiteDatabaseElementStats extends StatisticsTestSuiteBase { public TestStatisticsSuiteDatabaseElementStats(String name) { super(name); } public void testInvalidCalls() throws Exception { System.out.println("\n\nTESTING INVALID CALLS\n\n\n"); Client client = getFullyConnectedClient(); // // invalid selector // try { // No selector at all. client.callProcedure("@Statistics"); fail(); } catch (ProcCallException ex) { // All badness gets turned into ProcCallExceptions, so we need // to check specifically for this error, otherwise things that // crash the cluster also turn into ProcCallExceptions and don't // trigger failure (ENG-2347) assertEquals("Incorrect number of arguments to @Statistics (expects 2, received 0)", ex.getMessage()); } try { // extra stuff client.callProcedure("@Statistics", "table", 0, "OHHAI"); fail(); } catch (ProcCallException ex) { assertEquals("Incorrect number of arguments to @Statistics (expects 2, received 3)", ex.getMessage()); } try { // Invalid selector client.callProcedure("@Statistics", "garbage", 0); fail(); } catch (ProcCallException ex) {} } public void testTableStatistics() throws Exception { System.out.println("\n\nTESTING TABLE STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[13]; expectedSchema[0] = new ColumnInfo("TIMESTAMP", VoltType.BIGINT); expectedSchema[1] = new ColumnInfo("HOST_ID", VoltType.INTEGER); expectedSchema[2] = new ColumnInfo("HOSTNAME", VoltType.STRING); expectedSchema[3] = new ColumnInfo("SITE_ID", VoltType.INTEGER); expectedSchema[4] = new ColumnInfo("PARTITION_ID", VoltType.BIGINT); expectedSchema[5] = new ColumnInfo("TABLE_NAME", VoltType.STRING); expectedSchema[6] = new ColumnInfo("TABLE_TYPE", VoltType.STRING); expectedSchema[7] = new ColumnInfo("TUPLE_COUNT", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("TUPLE_ALLOCATED_MEMORY", VoltType.BIGINT); expectedSchema[9] = new ColumnInfo("TUPLE_DATA_MEMORY", VoltType.BIGINT); expectedSchema[10] = new ColumnInfo("STRING_DATA_MEMORY", VoltType.BIGINT); expectedSchema[11] = new ColumnInfo("TUPLE_LIMIT", VoltType.INTEGER); expectedSchema[12] = new ColumnInfo("PERCENT_FULL", VoltType.INTEGER); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; boolean success = false; long start = System.currentTimeMillis(); while (!success) { if (System.currentTimeMillis() - start > 60000) fail("Took too long"); success = true; // table // results = client.callProcedure("@Statistics", "table", 0).getResults(); System.out.println("Test statistics table: " + results[0].toString()); // one aggregate table returned assertEquals(1, results.length); validateSchema(results[0], expectedTable); // with 10 rows per site. Can be two values depending on the test scenario of cluster vs. local. if (HOSTS * SITES * 3 != results[0].getRowCount()) { success = false; } // Validate that each site returns a result for each table if (success) { success = validateRowSeenAtAllSites(results[0], "TABLE_NAME", "WAREHOUSE", true); } if (success) { success = validateRowSeenAtAllSites(results[0], "TABLE_NAME", "NEW_ORDER", true); } if (success) { validateRowSeenAtAllSites(results[0], "TABLE_NAME", "ITEM", true); } if (success) break; } } public void testIndexStatistics() throws Exception { System.out.println("\n\nTESTING INDEX STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[12]; expectedSchema[0] = new ColumnInfo("TIMESTAMP", VoltType.BIGINT); expectedSchema[1] = new ColumnInfo("HOST_ID", VoltType.INTEGER); expectedSchema[2] = new ColumnInfo("HOSTNAME", VoltType.STRING); expectedSchema[3] = new ColumnInfo("SITE_ID", VoltType.INTEGER); expectedSchema[4] = new ColumnInfo("PARTITION_ID", VoltType.BIGINT); expectedSchema[5] = new ColumnInfo("INDEX_NAME", VoltType.STRING); expectedSchema[6] = new ColumnInfo("TABLE_NAME", VoltType.STRING); expectedSchema[7] = new ColumnInfo("INDEX_TYPE", VoltType.STRING); expectedSchema[8] = new ColumnInfo("IS_UNIQUE", VoltType.TINYINT); expectedSchema[9] = new ColumnInfo("IS_COUNTABLE", VoltType.TINYINT); expectedSchema[10] = new ColumnInfo("ENTRY_COUNT", VoltType.BIGINT); expectedSchema[11] = new ColumnInfo("MEMORY_ESTIMATE", VoltType.BIGINT); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; boolean success = false; long start = System.currentTimeMillis(); while (!success) { if (System.currentTimeMillis() - start > 60000) fail("Took too long"); success = true; results = client.callProcedure("@Statistics", "index", 0).getResults(); System.out.println("Index results: " + results[0].toString()); assertEquals(1, results.length); validateSchema(results[0], expectedTable); if (success) { success = validateRowSeenAtAllSites(results[0], "INDEX_NAME", HSQLInterface.AUTO_GEN_PRIMARY_KEY_PREFIX + "WAREHOUSE_W_PK_TREE", true); } if (success) { success = validateRowSeenAtAllSites(results[0], "INDEX_NAME", HSQLInterface.AUTO_GEN_PRIMARY_KEY_PREFIX + "ITEM_I_PK_TREE", true); } if (success) break; } } public void testProcedureStatistics() throws Exception { System.out.println("\n\nTESTING PROCEDURE STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[20]; expectedSchema[0] = new ColumnInfo("TIMESTAMP", VoltType.BIGINT); expectedSchema[1] = new ColumnInfo("HOST_ID", VoltType.INTEGER); expectedSchema[2] = new ColumnInfo("HOSTNAME", VoltType.STRING); expectedSchema[3] = new ColumnInfo("SITE_ID", VoltType.INTEGER); expectedSchema[4] = new ColumnInfo("PARTITION_ID", VoltType.INTEGER); expectedSchema[5] = new ColumnInfo("PROCEDURE", VoltType.STRING); expectedSchema[6] = new ColumnInfo("INVOCATIONS", VoltType.BIGINT); expectedSchema[7] = new ColumnInfo("TIMED_INVOCATIONS", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("MIN_EXECUTION_TIME", VoltType.BIGINT); expectedSchema[9] = new ColumnInfo("MAX_EXECUTION_TIME", VoltType.BIGINT); expectedSchema[10] = new ColumnInfo("AVG_EXECUTION_TIME", VoltType.BIGINT); expectedSchema[11] = new ColumnInfo("MIN_RESULT_SIZE", VoltType.INTEGER); expectedSchema[12] = new ColumnInfo("MAX_RESULT_SIZE", VoltType.INTEGER); expectedSchema[13] = new ColumnInfo("AVG_RESULT_SIZE", VoltType.INTEGER); expectedSchema[14] = new ColumnInfo("MIN_PARAMETER_SET_SIZE", VoltType.INTEGER); expectedSchema[15] = new ColumnInfo("MAX_PARAMETER_SET_SIZE", VoltType.INTEGER); expectedSchema[16] = new ColumnInfo("AVG_PARAMETER_SET_SIZE", VoltType.INTEGER); expectedSchema[17] = new ColumnInfo("ABORTS", VoltType.BIGINT); expectedSchema[18] = new ColumnInfo("FAILURES", VoltType.BIGINT); expectedSchema[19] = new ColumnInfo("TRANSACTIONAL", VoltType.TINYINT); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; // // procedure // // Induce procedure invocations on all partitions. May fail in non-legacy hashing case // this plus R/W replication should ensure that every site on every node runs this transaction // at least once client.callProcedure("@Statistics", "proceduredetail", 1); results = client.callProcedure("@GetPartitionKeys", "INTEGER").getResults(); VoltTable keys = results[0]; for (int k = 0;k < keys.getRowCount(); k++) { long key = keys.fetchRow(k).getLong(1); client.callProcedure("NEW_ORDER.insert", key); } for (int i = 0; i < HOSTS * SITES; i++) { client.callProcedure("NEW_ORDER.insert", i); } // 3 seconds translates to 3 billion nanos, which overflows internal // values (ENG-1039) //It's possible that the nanosecond count goes backwards... so run this a couple //of times to make sure the min value gets set for (int ii = 0; ii < 3; ii++) { results = client.callProcedure("GoSleep", 3000, 0, null).getResults(); } results = client.callProcedure("@Statistics", "procedure", 0).getResults(); System.out.println("Test procedures table: " + results[0].toString()); // one aggregate table returned assertEquals(1, results.length); validateSchema(results[0], expectedTable); // For this table, where unique HSID isn't written to SITE_ID, these // two checks should ensure we get all the rows we expect? Map<String, String> columnTargets = new HashMap<>(); columnTargets.put("PROCEDURE", "NEW_ORDER.insert"); validateRowSeenAtAllHosts(results[0], columnTargets, false); validateRowSeenAtAllPartitions(results[0], "PROCEDURE", "NEW_ORDER.insert", false); results[0].resetRowPosition(); VoltTable stats = results[0]; String procname = "blerg"; while (!procname.equals("org.voltdb_testprocs.regressionsuites.malicious.GoSleep")) { stats.advanceRow(); procname = (String)stats.get("PROCEDURE", VoltType.STRING); } // Retrieve all statistics long min_time = (Long)stats.get("MIN_EXECUTION_TIME", VoltType.BIGINT); long max_time = (Long)stats.get("MAX_EXECUTION_TIME", VoltType.BIGINT); long avg_time = (Long)stats.get("AVG_EXECUTION_TIME", VoltType.BIGINT); long min_result_size = (Long)stats.get("MIN_RESULT_SIZE", VoltType.BIGINT); long max_result_size = (Long)stats.get("MAX_RESULT_SIZE", VoltType.BIGINT); long avg_result_size = (Long)stats.get("AVG_RESULT_SIZE", VoltType.BIGINT); long min_parameter_set_size = (Long)stats.get("MIN_PARAMETER_SET_SIZE", VoltType.BIGINT); long max_parameter_set_size = (Long)stats.get("MAX_PARAMETER_SET_SIZE", VoltType.BIGINT); long avg_parameter_set_size = (Long)stats.get("AVG_PARAMETER_SET_SIZE", VoltType.BIGINT); // Check for overflow assertTrue("Failed MIN_EXECUTION_TIME > 0, value was: " + min_time, min_time > 0); assertTrue("Failed MAX_EXECUTION_TIME > 0, value was: " + max_time, max_time > 0); assertTrue("Failed AVG_EXECUTION_TIME > 0, value was: " + avg_time, avg_time > 0); assertTrue("Failed MIN_RESULT_SIZE > 0, value was: " + min_result_size, min_result_size >= 0); assertTrue("Failed MAX_RESULT_SIZE > 0, value was: " + max_result_size, max_result_size >= 0); assertTrue("Failed AVG_RESULT_SIZE > 0, value was: " + avg_result_size, avg_result_size >= 0); assertTrue("Failed MIN_PARAMETER_SET_SIZE > 0, value was: " + min_parameter_set_size, min_parameter_set_size >= 0); assertTrue("Failed MAX_PARAMETER_SET_SIZE > 0, value was: " + max_parameter_set_size, max_parameter_set_size >= 0); assertTrue("Failed AVG_PARAMETER_SET_SIZE > 0, value was: " + avg_parameter_set_size, avg_parameter_set_size >= 0); // check for reasonable values assertTrue("Failed MIN_EXECUTION_TIME > 2,400,000,000ns, value was: " + min_time, min_time > 2400000000L); assertTrue("Failed MAX_EXECUTION_TIME > 2,400,000,000ns, value was: " + max_time, max_time > 2400000000L); assertTrue("Failed AVG_EXECUTION_TIME > 2,400,000,000ns, value was: " + avg_time, avg_time > 2400000000L); assertTrue("Failed MIN_RESULT_SIZE < 1,000,000, value was: " + min_result_size, min_result_size < 1000000L); assertTrue("Failed MAX_RESULT_SIZE < 1,000,000, value was: " + max_result_size, max_result_size < 1000000L); assertTrue("Failed AVG_RESULT_SIZE < 1,000,000, value was: " + avg_result_size, avg_result_size < 1000000L); assertTrue("Failed MIN_PARAMETER_SET_SIZE < 1,000,000, value was: " + min_parameter_set_size, min_parameter_set_size < 1000000L); assertTrue("Failed MAX_PARAMETER_SET_SIZE < 1,000,000, value was: " + max_parameter_set_size, max_parameter_set_size < 1000000L); assertTrue("Failed AVG_PARAMETER_SET_SIZE < 1,000,000, value was: " + avg_parameter_set_size, avg_parameter_set_size < 1000000L); // Validate the schema of PROCEDUREDETAIL results = client.callProcedure("@Statistics", "proceduredetail", 0).getResults(); assertEquals(1, results.length); expectedSchema = new ColumnInfo[20]; expectedSchema[0] = new ColumnInfo("TIMESTAMP", VoltType.BIGINT); expectedSchema[1] = new ColumnInfo("HOST_ID", VoltType.INTEGER); expectedSchema[2] = new ColumnInfo("HOSTNAME", VoltType.STRING); expectedSchema[3] = new ColumnInfo("SITE_ID", VoltType.INTEGER); expectedSchema[4] = new ColumnInfo("PARTITION_ID", VoltType.INTEGER); expectedSchema[5] = new ColumnInfo("PROCEDURE", VoltType.STRING); expectedSchema[6] = new ColumnInfo("STATEMENT", VoltType.STRING); expectedSchema[7] = new ColumnInfo("INVOCATIONS", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("TIMED_INVOCATIONS", VoltType.BIGINT); expectedSchema[9] = new ColumnInfo("MIN_EXECUTION_TIME", VoltType.BIGINT); expectedSchema[10] = new ColumnInfo("MAX_EXECUTION_TIME", VoltType.BIGINT); expectedSchema[11] = new ColumnInfo("AVG_EXECUTION_TIME", VoltType.BIGINT); expectedSchema[12] = new ColumnInfo("MIN_RESULT_SIZE", VoltType.INTEGER); expectedSchema[13] = new ColumnInfo("MAX_RESULT_SIZE", VoltType.INTEGER); expectedSchema[14] = new ColumnInfo("AVG_RESULT_SIZE", VoltType.INTEGER); expectedSchema[15] = new ColumnInfo("MIN_PARAMETER_SET_SIZE", VoltType.INTEGER); expectedSchema[16] = new ColumnInfo("MAX_PARAMETER_SET_SIZE", VoltType.INTEGER); expectedSchema[17] = new ColumnInfo("AVG_PARAMETER_SET_SIZE", VoltType.INTEGER); expectedSchema[18] = new ColumnInfo("ABORTS", VoltType.BIGINT); expectedSchema[19] = new ColumnInfo("FAILURES", VoltType.BIGINT); expectedTable = new VoltTable(expectedSchema); validateSchema(results[0], expectedTable); // Validate the PROCEDUREPROFILE aggregation. results = client.callProcedure("@Statistics", "procedureprofile", 1).getResults(); System.out.println("\n\n\n" + results[0].toString() + "\n\n\n"); // expect NEW_ORDER.insert, GoSleep // see TestStatsProcProfile.java for tests of the aggregation itself. List<String> possibleProcs = new ArrayList<>(); possibleProcs.add("org.voltdb_testprocs.regressionsuites.malicious.GoSleep"); possibleProcs.add("NEW_ORDER.insert"); if (MiscUtils.isPro()) { possibleProcs.add("org.voltdb.sysprocs.SnapshotSave"); } while (results[0].advanceRow()) { assertTrue("Unexpected stored procedure executed: " + results[0].getString("PROCEDURE"), possibleProcs.contains(results[0].getString("PROCEDURE"))); } } // // Build a list of the tests to be run. Use the regression suite // helpers to allow multiple backends. // JUnit magic that uses the regression suite helper classes. // static public Test suite() throws IOException { return StatisticsTestSuiteBase.suite(TestStatisticsSuiteDatabaseElementStats.class, false); } }