/* 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.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeUnit; import junit.framework.Test; import org.HdrHistogram_voltpatches.AbstractHistogram; import org.HdrHistogram_voltpatches.Histogram; import org.voltcore.utils.CompressionStrategySnappy; 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.dtxn.LatencyHistogramStats; import org.voltdb.dtxn.LatencyStats; import org.voltdb.iv2.MpInitiator; import org.voltdb.regressionsuites.StatisticsTestSuiteBase; import org.voltdb_testprocs.regressionsuites.malicious.GoSleep; public class TestStatisticsSuite extends StatisticsTestSuiteBase { private static final Class<?>[] PROCEDURES = { GoSleep.class }; public TestStatisticsSuite(String name) { super(name); } private String claimRecentAnalysis() { String result = "No root cause analysis is available for this failure."; if (m_recentAnalysis != null) { result = m_recentAnalysis.toString(); m_recentAnalysis = null; } return result; } // validation functions supporting multiple columns private boolean checkRowForMultipleTargets(VoltTable result, Map<String, String> columnTargets) { for (Entry<String, String> entry : columnTargets.entrySet()) { if (!result.getString(entry.getKey()).equals(entry.getValue())) { return false; } } return true; } private int countHostsProvidingRows(VoltTable result, Map<String, String> columnTargets, boolean enforceUnique) { result.resetRowPosition(); Set<Long> hostsSeen = new HashSet<Long>(); while (result.advanceRow()) { if (checkRowForMultipleTargets(result, columnTargets)) { Long thisHostId = result.getLong("HOST_ID"); if (enforceUnique) { StringBuilder message = new StringBuilder(); message.append("HOST_ID: " + thisHostId + " seen twice in table looking for "); for (Entry<String, String> entry : columnTargets.entrySet()) { message.append(entry.getValue() + " in column " + entry.getKey() + ";"); } assertFalse(message.toString(), hostsSeen.contains(thisHostId)); } hostsSeen.add(thisHostId); } } //* Enable this to force a failure with diagnostics */ hostsSeen.add(123456789L); // Before possibly failing an assert, prepare to report details of the non-conforming result. m_recentAnalysis = null; if (HOSTS != hostsSeen.size()) { m_recentAnalysis = new StringBuilder(); m_recentAnalysis.append("Failure follows from these results:\n"); Set<Long> seenAgain = new HashSet<Long>(); result.resetRowPosition(); while (result.advanceRow()) { Long thisHostId = result.getLong("HOST_ID"); String rowStatus = "Found a non-match"; if (checkRowForMultipleTargets(result, columnTargets)) { if (seenAgain.add(thisHostId)) { rowStatus = "Added a match"; } else { rowStatus = "Duplicated a match"; } } m_recentAnalysis.append(rowStatus + " at host " + thisHostId + " for "); for (String key : columnTargets.keySet()) { m_recentAnalysis.append(key + " " + result.getString(key) + ";"); } m_recentAnalysis.append("\n"); } } return hostsSeen.size(); } 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) {} } /** Make sure @Statistics LATENCY returns sane data in the expected formats. */ public void testLatencyValidity() throws Exception { System.out.println("\n\nTESTING LATENCY STATS VALIDITY\n\n\n"); Client client = getFullyConnectedClient(); // Validate column names, types and ordering. ColumnInfo[] expectedSchema = new ColumnInfo[13]; expectedSchema[0] = new ColumnInfo("TIMESTAMP", VoltType.BIGINT); // milliseconds expectedSchema[1] = new ColumnInfo("HOST_ID", VoltType.INTEGER); expectedSchema[2] = new ColumnInfo("HOSTNAME", VoltType.STRING); expectedSchema[3] = new ColumnInfo("INTERVAL", VoltType.INTEGER); // milliseconds expectedSchema[4] = new ColumnInfo("COUNT", VoltType.INTEGER); // samples expectedSchema[5] = new ColumnInfo("TPS", VoltType.INTEGER); // samples per second expectedSchema[6] = new ColumnInfo("P50", VoltType.BIGINT); // microseconds expectedSchema[7] = new ColumnInfo("P95", VoltType.BIGINT); // microseconds expectedSchema[8] = new ColumnInfo("P99", VoltType.BIGINT); // microseconds expectedSchema[9] = new ColumnInfo("P99.9", VoltType.BIGINT); // microseconds expectedSchema[10] = new ColumnInfo("P99.99", VoltType.BIGINT); // microseconds expectedSchema[11] = new ColumnInfo("P99.999", VoltType.BIGINT); // microseconds expectedSchema[12] = new ColumnInfo("MAX", VoltType.BIGINT); // microseconds VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; // Do some work that will generate latency stats for (int i = 0; i < SITES * HOSTS; i++) { results = client.callProcedure("NEW_ORDER.insert", i).getResults(); } // Statistics roll over every 5 seconds. // Retry until we've seen the window with our data. // We retry more often than that so that we don't miss the window; // this also speeds up tests when the data comes in towards the end of a window. long samplesFound; int numInvocations = 0; final int delayMsec = (int) TimeUnit.SECONDS.toMillis(1); final int invocationLimit = 2 * LatencyStats.INTERVAL_MS / delayMsec; assertTrue(delayMsec < LatencyStats.INTERVAL_MS); // prevent misleading test failures if INTERVAL_MS changes do { Thread.sleep(delayMsec); results = client.callProcedure("@Statistics", "LATENCY", 0).getResults(); // one aggregate table returned assertEquals(1, results.length); System.out.println("Test latency table: " + results[0].toString()); validateSchema(results[0], expectedTable); // should have at least one row from each host results[0].advanceRow(); Map<String, String> columnTargets = new HashMap<String, String>(); columnTargets.put("HOSTNAME", results[0].getString("HOSTNAME")); validateRowSeenAtAllHosts(results[0], columnTargets, false); assertEquals(HOSTS, results[0].getRowCount()); // Search all nodes for the one that has our data results[0].resetRowPosition(); samplesFound = 0; while (results[0].advanceRow()) { final long interval = results[0].getLong("INTERVAL"); // milliseconds final long count = results[0].getLong("COUNT"); // samples final long tps = results[0].getLong("TPS"); // samples per second final long p50 = results[0].getLong("P50"); // microseconds final long p95 = results[0].getLong("P95"); // microseconds final long p99 = results[0].getLong("P99"); // microseconds final long p39 = results[0].getLong("P99.9"); // microseconds final long p49 = results[0].getLong("P99.99"); // microseconds final long p59 = results[0].getLong("P99.999"); // microseconds final long max = results[0].getLong("MAX"); // microseconds // Run sanity checks on the data assertEquals(interval, LatencyStats.INTERVAL_MS); // Test needs to do this calculation the exact same way as the code due to fixed point rounding assertEquals(tps, (int) (TimeUnit.SECONDS.toMillis(count) / interval)); assertTrue(count == 0 || p50 > 0); assertTrue(p50 <= p95); assertTrue(p95 <= p99); assertTrue(p99 <= p39); assertTrue(p39 <= p49); assertTrue(p49 <= p59); assertTrue(p59 <= max); samplesFound += count; } numInvocations++; } while (samplesFound == 0 && numInvocations < invocationLimit); assertTrue(samplesFound > 0); } // Helper method for testLatencyTiming() private static VoltTable doLatencyStatsCall(Client client) throws Exception { VoltTable[] results = client.callProcedure("@Statistics", "LATENCY", 0).getResults(); assertEquals(1, results.length); // Guarantee the same host is used every time - they are not always returned in a consistent order. // Expect about 20% failure if this loop is commented out. do { results[0].advanceRow(); } while (results[0].getLong("HOST_ID") != 0); return results[0]; } /** Verify that the timestamp from "@Statistics LATENCY" * is associated with the data window, not the procedure call. * * We do this by checking: * 1. Consecutive calls return the same timestamp (since they represent the same bin). * 2. Calls separated by more than a full time window return different timestamps. * * There are a lot of reasons two consecutive calls would not return the same timestamp. * Notably, histogram bin rollovers and sluggishness that cause the test to not execute. * Keep trying until identical timestamps are observed - * an implementation that gives each call a unique timestamp will still fail the test. */ public void testLatencyTiming() throws Exception { System.out.println("\n\nTESTING LATENCY STATS TIMING\n\n\n"); Client client = getFullyConnectedClient(); // Don't make so many attempts where a broken implementation will 'get lucky' and pass, // but also try enough times to avoid the benign situations mentioned above. // This may require some tweaking. final int maxAttempts = 12; int numAttempts = 0; VoltTable previous; VoltTable current = doLatencyStatsCall(client); do { previous = current; current = doLatencyStatsCall(client); } while (numAttempts++ < maxAttempts && previous.getLong("TIMESTAMP") != current.getLong("TIMESTAMP")); System.out.println(numAttempts + " attempts made at obtaining timestamps from the same time window."); assertTrue("Unable to use timestamps to verify that data comes from the same window.", numAttempts < maxAttempts); // Verify that matching timestamps implies matching data assertEquals(previous.getLong("TIMESTAMP"), current.getLong("TIMESTAMP")); assertEquals(previous.getLong("HOST_ID"), current.getLong("HOST_ID")); assertEquals(previous.getString("HOSTNAME"), current.getString("HOSTNAME")); assertEquals(previous.getLong("INTERVAL"), current.getLong("INTERVAL")); assertEquals(previous.getLong("COUNT"), current.getLong("COUNT")); assertEquals(previous.getLong("TPS"), current.getLong("TPS")); assertEquals(previous.getLong("P50"), current.getLong("P50")); assertEquals(previous.getLong("P95"), current.getLong("P95")); assertEquals(previous.getLong("P99"), current.getLong("P99")); assertEquals(previous.getLong("P99.9"), current.getLong("P99.9")); assertEquals(previous.getLong("P99.99"), current.getLong("P99.99")); assertEquals(previous.getLong("P99.999"), current.getLong("P99.999")); assertEquals(previous.getLong("MAX"), current.getLong("MAX")); // Verify that >5 seconds later the timestamps are not the same. final int extraDelayMsec = 1000; final long beforeTimestamp = current.getLong("TIMESTAMP"); Thread.sleep(LatencyStats.INTERVAL_MS + extraDelayMsec); final long afterTimestamp = doLatencyStatsCall(client).getLong("TIMESTAMP"); System.out.println("Non-consecutive timestamps: " + beforeTimestamp + ", " + afterTimestamp); assertTrue(afterTimestamp > beforeTimestamp); } public void testLatencyCompressed() throws Exception { System.out.println("\n\nTESTING LATENCY_COMPRESSED STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[5]; 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("HISTOGRAM", VoltType.VARBINARY); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; // Do some stuff to generate some latency stats for (int i = 0; i < SITES * HOSTS; i++) { results = client.callProcedure("NEW_ORDER.insert", i).getResults(); } results = client.callProcedure("@Statistics", "LATENCY_COMPRESSED", 0).getResults(); // one aggregate table returned assertEquals(1, results.length); System.out.println("Test latency_compressed table: " + results[0].toString()); validateSchema(results[0], expectedTable); // should have at least one row from each host results[0].advanceRow(); Map<String, String> columnTargets = new HashMap<String, String>(); columnTargets.put("HOSTNAME", results[0].getString("HOSTNAME")); validateRowSeenAtAllHosts(results[0], columnTargets, false); // actually, there are 26 rows per host so: assertEquals(HOSTS, results[0].getRowCount()); // Check for non-zero invocations (ENG-4668) long invocations = 0; results[0].resetRowPosition(); while (results[0].advanceRow()) { byte histogramBytes[] = results[0].getVarbinary("HISTOGRAM"); Histogram h = AbstractHistogram.fromCompressedBytes(histogramBytes, CompressionStrategySnappy.INSTANCE); invocations += h.getTotalCount(); } assertTrue(invocations > 0); } public void testInitiatorStatistics() throws Exception { System.out.println("\n\nTESTING INITIATOR 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("CONNECTION_ID", VoltType.BIGINT); expectedSchema[5] = new ColumnInfo("CONNECTION_HOSTNAME", VoltType.STRING); expectedSchema[6] = new ColumnInfo("PROCEDURE_NAME", VoltType.STRING); expectedSchema[7] = new ColumnInfo("INVOCATIONS", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("AVG_EXECUTION_TIME", VoltType.INTEGER); expectedSchema[9] = new ColumnInfo("MIN_EXECUTION_TIME", VoltType.INTEGER); expectedSchema[10] = new ColumnInfo("MAX_EXECUTION_TIME", VoltType.INTEGER); expectedSchema[11] = new ColumnInfo("ABORTS", VoltType.BIGINT); expectedSchema[12] = new ColumnInfo("FAILURES", VoltType.BIGINT); VoltTable expectedTable = new VoltTable(expectedSchema); // // initiator selector // VoltTable results[] = null; // This should get us an invocation at each host for (int i = 0; i < 1000; i++) { results = client.callProcedure("NEW_ORDER.insert", i).getResults(); } results = client.callProcedure("@Statistics", "INITIATOR", 0).getResults(); // one aggregate table returned assertEquals(1, results.length); System.out.println("Test initiators table: " + results[0].toString()); // Check the schema validateSchema(results[0], expectedTable); // One WAREHOUSE.select row per host assertEquals(HOSTS, results[0].getRowCount()); // Verify the invocation counts int counts = 0; while (results[0].advanceRow()) { String procName = results[0].getString("PROCEDURE_NAME"); if (procName.equals("@SystemCatalog")) { // One for each connection from the client assertEquals(HOSTS, results[0].getLong("INVOCATIONS")); } else if (procName.equals("NEW_ORDER.insert")) { counts += results[0].getLong("INVOCATIONS"); } } assertEquals(1000, counts); // verify that each node saw a NEW_ORDER.insert initiation Map<String, String> columnTargets = new HashMap<String, String>(); columnTargets.put("PROCEDURE_NAME", "NEW_ORDER.insert"); validateRowSeenAtAllHosts(results[0], columnTargets, true); } public void testPartitionCount() throws Exception { System.out.println("\n\nTESTING PARTITION COUNT\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[4]; 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("PARTITION_COUNT", VoltType.INTEGER); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; results = client.callProcedure("@Statistics", "PARTITIONCOUNT", 0).getResults(); // Only one table returned assertEquals(1, results.length); validateSchema(results[0], expectedTable); // Should only get one row, total assertEquals(1, results[0].getRowCount()); results[0].advanceRow(); int partCount = (int)results[0].getLong("PARTITION_COUNT"); assertEquals(PARTITIONS, partCount); } public void testMemoryStatistics() throws Exception { System.out.println("\n\nTESTING MEMORY STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[14]; 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("RSS", VoltType.INTEGER); expectedSchema[4] = new ColumnInfo("JAVAUSED", VoltType.INTEGER); expectedSchema[5] = new ColumnInfo("JAVAUNUSED", VoltType.INTEGER); expectedSchema[6] = new ColumnInfo("TUPLEDATA", VoltType.BIGINT); expectedSchema[7] = new ColumnInfo("TUPLEALLOCATED", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("INDEXMEMORY", VoltType.BIGINT); expectedSchema[9] = new ColumnInfo("STRINGMEMORY", VoltType.BIGINT); expectedSchema[10] = new ColumnInfo("TUPLECOUNT", VoltType.BIGINT); expectedSchema[11] = new ColumnInfo("POOLEDMEMORY", VoltType.BIGINT); expectedSchema[12] = new ColumnInfo("PHYSICALMEMORY", VoltType.BIGINT); expectedSchema[13] = new ColumnInfo("JAVAMAXHEAP", VoltType.INTEGER); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; // // memory // // give time to seed the stats cache? Thread.sleep(1000); results = client.callProcedure("@Statistics", "memory", 0).getResults(); System.out.println("Node memory statistics table: " + results[0].toString()); // one aggregate table returned assertEquals(1, results.length); validateSchema(results[0], expectedTable); results[0].advanceRow(); // Hacky, on a single local cluster make sure that all 'nodes' are present. // MEMORY stats lacks a common string across nodes, but we can hijack the hostname in this case. Map<String, String> columnTargets = new HashMap<String, String>(); columnTargets.put("HOSTNAME", results[0].getString("HOSTNAME")); validateRowSeenAtAllHosts(results[0], columnTargets, true); } public void testIOStatistics() throws Exception { System.out.println("\n\nTESTING IO STATS\n\n\n"); Client client = getFullyConnectedClient(); // Based on doc, not code // HOST_ID, SITE_ID, and PARTITION_ID all differ. Fixed to match // reality so tests would pass, but, ugh. ColumnInfo[] expectedSchema = new ColumnInfo[9]; 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("CONNECTION_ID", VoltType.BIGINT); expectedSchema[4] = new ColumnInfo("CONNECTION_HOSTNAME", VoltType.STRING); expectedSchema[5] = new ColumnInfo("BYTES_READ", VoltType.BIGINT); expectedSchema[6] = new ColumnInfo("MESSAGES_READ", VoltType.BIGINT); expectedSchema[7] = new ColumnInfo("BYTES_WRITTEN", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("MESSAGES_WRITTEN", VoltType.BIGINT); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; // // iostats // results = client.callProcedure("@Statistics", "iostats", 0).getResults(); System.out.println("Test iostats table: " + results[0].toString()); // one aggregate table returned assertEquals(1, results.length); validateSchema(results[0], expectedTable); } public void testTopoStatistics() throws Exception { System.out.println("\n\nTESTING TOPO STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema1 = new ColumnInfo[3]; expectedSchema1[0] = new ColumnInfo("Partition", VoltType.INTEGER); expectedSchema1[1] = new ColumnInfo("Sites", VoltType.STRING); expectedSchema1[2] = new ColumnInfo("Leader", VoltType.STRING); VoltTable expectedTable1 = new VoltTable(expectedSchema1); ColumnInfo[] expectedSchema2 = new ColumnInfo[2]; expectedSchema2[0] = new ColumnInfo("HASHTYPE", VoltType.STRING); expectedSchema2[1] = new ColumnInfo("HASHCONFIG", VoltType.VARBINARY); VoltTable expectedTable2 = new VoltTable(expectedSchema2); VoltTable[] results = null; // // TOPO with interval set to 0, retrieving binary hash config // results = client.callProcedure("@Statistics", "TOPO", 0).getResults(); // two aggregate tables returned assertEquals(2, results.length); System.out.println("Test TOPO table: " + results[0].toString()); System.out.println("Test TOPO table: " + results[1].toString()); validateSchema(results[0], expectedTable1); validateSchema(results[1], expectedTable2); VoltTable topo = results[0]; // Should have partitions + 1 rows in the first table assertEquals(PARTITIONS + 1, results[0].getRowCount()); // Make sure we can find the MPI, at least boolean found = false; while (topo.advanceRow()) { if ((int)topo.getLong("Partition") == MpInitiator.MP_INIT_PID) { found = true; } } assertTrue(found); // and only one row in the second table assertEquals(1, results[1].getRowCount()); // // TOPO with interval set to 1, for retrieving compressed json hash config // results = client.callProcedure("@Statistics", "TOPO", 1).getResults(); // two aggregate tables returned assertEquals(2, results.length); System.out.println("Test TOPO table: " + results[0].toString()); System.out.println("Test TOPO table: " + results[1].toString()); validateSchema(results[0], expectedTable1); validateSchema(results[1], expectedTable2); topo = results[0]; // Should have partitions + 1 rows in the first table assertEquals(PARTITIONS + 1, results[0].getRowCount()); // Make sure we can find the MPI, at least found = false; while (topo.advanceRow()) { if ((int)topo.getLong("Partition") == MpInitiator.MP_INIT_PID) { found = true; } } assertTrue(found); // and only one row in the second table assertEquals(1, results[1].getRowCount()); } public void testLiveClientsStatistics() throws Exception { System.out.println("\n\nTESTING LIVECLIENTS STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[9]; 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("CONNECTION_ID", VoltType.BIGINT); expectedSchema[4] = new ColumnInfo("CLIENT_HOSTNAME", VoltType.STRING); expectedSchema[5] = new ColumnInfo("ADMIN", VoltType.TINYINT); expectedSchema[6] = new ColumnInfo("OUTSTANDING_REQUEST_BYTES", VoltType.BIGINT); expectedSchema[7] = new ColumnInfo("OUTSTANDING_RESPONSE_MESSAGES", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("OUTSTANDING_TRANSACTIONS", VoltType.BIGINT); VoltTable expectedTable = new VoltTable(expectedSchema); int patientRetries = 2; int hostsHeardFrom = 0; // // LIVECLIENTS // do { // loop until we get the desired answer or lose patience waiting out possible races. VoltTable[] results = client.callProcedure("@Statistics", "LIVECLIENTS", 0).getResults(); // one aggregate table returned assertEquals(1, results.length); System.out.println("Test LIVECLIENTS table: " + results[0].toString()); validateSchema(results[0], expectedTable); // Hacky, on a single local cluster make sure that all 'nodes' are present. // LiveClients stats lacks a common string across nodes, but we can hijack the hostname in this case. results[0].advanceRow(); Map<String, String> columnTargets = new HashMap<String, String>(); columnTargets.put("HOSTNAME", results[0].getString("HOSTNAME")); hostsHeardFrom = countHostsProvidingRows(results[0], columnTargets, true); } while ((hostsHeardFrom < HOSTS) && (--patientRetries) > 0); assertEquals(claimRecentAnalysis(), HOSTS, hostsHeardFrom); } public void testStarvationStatistics() throws Exception { System.out.println("\n\nTESTING STARVATION STATS\n\n\n"); Client client = getFullyConnectedClient(); ColumnInfo[] expectedSchema = new ColumnInfo[10]; 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("COUNT", VoltType.BIGINT); expectedSchema[5] = new ColumnInfo("PERCENT", VoltType.FLOAT); expectedSchema[6] = new ColumnInfo("AVG", VoltType.BIGINT); expectedSchema[7] = new ColumnInfo("MIN", VoltType.BIGINT); expectedSchema[8] = new ColumnInfo("MAX", VoltType.BIGINT); expectedSchema[9] = new ColumnInfo("STDDEV", VoltType.BIGINT); VoltTable expectedTable = new VoltTable(expectedSchema); VoltTable[] results = null; // // STARVATION // results = client.callProcedure("@Statistics", "STARVATION", 0).getResults(); // one aggregate table returned assertEquals(1, results.length); System.out.println("Test STARVATION table: " + results[0].toString()); validateSchema(results[0], expectedTable); // One row per site, we don't use HSID though, so hard to do straightforward // per-site unique check. Finesse it. // We also get starvation stats for the MPI, so we need to add a site per host. assertEquals(HOSTS * (SITES + 1), results[0].getRowCount()); results[0].advanceRow(); Map<String, String> columnTargets = new HashMap<String, String>(); columnTargets.put("HOSTNAME", results[0].getString("HOSTNAME")); validateRowSeenAtAllHosts(results[0], columnTargets, false); } public void testManagementStats() throws Exception { System.out.println("\n\nTESTING MANAGEMENT STATS\n\n\n"); Client client = getFullyConnectedClient(); VoltTable[] results = null; // // LIVECLIENTS // results = client.callProcedure("@Statistics", "MANAGEMENT", 0).getResults(); // eight aggregate tables returned. Assume that we have selected the right // subset of stats internally, just check that we get stuff. assertEquals(8, results.length); } // // 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(TestStatisticsSuite.class, false); } }