/* 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.Random; import java.util.concurrent.TimeUnit; import junit.framework.Test; import org.voltdb.join.BalancePartitionsStatistics; import org.voltdb.regressionsuites.StatisticsTestSuiteBase; public class TestStatisticsSuiteRebalanceStats extends StatisticsTestSuiteBase { public TestStatisticsSuiteRebalanceStats(String name) { super(name); } class RebalanceStatsChecker { final double fuzzFactor; final int rangesToMove; long tStartMS; long rangesMoved = 0; long bytesMoved = 0; long rowsMoved = 0; long invocations = 0; long totalInvTimeMS = 0; RebalanceStatsChecker(int rangesToMove, double fuzzFactor) { this.fuzzFactor = fuzzFactor; this.rangesToMove = rangesToMove; this.tStartMS = System.currentTimeMillis(); } void update(int ranges, int bytes, int rows) { rangesMoved += ranges; bytesMoved += bytes; rowsMoved += rows; invocations++; } void checkFuzz(double expected, double actual) { double delta = Math.abs((expected - actual) / expected); if (delta > fuzzFactor) { assertFalse(Math.abs((expected - actual) / expected) > fuzzFactor); } } void check(BalancePartitionsStatistics.StatsPoint stats) { double totalTimeS = (System.currentTimeMillis() - tStartMS) / 1000.0; double statsRangesMoved1 = (stats.getPercentageMoved() / 100.0) * rangesToMove; checkFuzz(rangesMoved, statsRangesMoved1); double statsRangesMoved2 = stats.getRangesPerSecond() * totalTimeS; checkFuzz(rangesMoved, statsRangesMoved2); double statsBytesMoved = stats.getMegabytesPerSecond() * 1000000.0 * totalTimeS; checkFuzz(bytesMoved, statsBytesMoved); double statsRowsMoved = stats.getRowsPerSecond() * totalTimeS; checkFuzz(rowsMoved, statsRowsMoved); double statsInvocations = stats.getInvocationsPerSecond() * totalTimeS; checkFuzz(invocations, statsInvocations); double statsInvTimeMS = stats.getAverageInvocationTime() * invocations; assertTrue(Math.abs((totalInvTimeMS - statsInvTimeMS) / totalInvTimeMS) <= fuzzFactor); checkFuzz(totalInvTimeMS, statsInvTimeMS); double estTimeRemainingS = totalTimeS * (rangesToMove / (double)rangesMoved - 1.0); double statsEstTimeRemainingS = stats.getEstimatedRemaining() / 1000.0; checkFuzz(estTimeRemainingS, statsEstTimeRemainingS); } } public void testRebalanceStats() throws Exception { System.out.println("testRebalanceStats"); // Test constants final int DURATION_SECONDS = 10; final int INVOCATION_SLEEP_MILLIS = 500; final int IDLE_SLEEP_MILLIS = 200; final int RANGES_TO_MOVE = Integer.MAX_VALUE; final int BYTES_TO_MOVE = 10000000; final int ROWS_TO_MOVE = 1000000; final double FUZZ_FACTOR = .1; RebalanceStatsChecker checker = new RebalanceStatsChecker(RANGES_TO_MOVE, FUZZ_FACTOR); BalancePartitionsStatistics bps = new BalancePartitionsStatistics(RANGES_TO_MOVE); Random r = new Random(2222); // Random numbers are between zero and the constant, so everything will average out // to half the time and quantities. Nothing will be exhausted by the test. final int loopCount = (DURATION_SECONDS * 1000) / (INVOCATION_SLEEP_MILLIS + IDLE_SLEEP_MILLIS); for (int i = 0; i < loopCount; i++) { bps.logBalanceStarts(); int invocationTimeMS = r.nextInt(INVOCATION_SLEEP_MILLIS); Thread.sleep(invocationTimeMS); checker.totalInvTimeMS += invocationTimeMS; int ranges = r.nextInt(RANGES_TO_MOVE / loopCount); int bytes = r.nextInt(BYTES_TO_MOVE / loopCount); int rows = r.nextInt(ROWS_TO_MOVE / loopCount); bps.logBalanceEnds(ranges, bytes, TimeUnit.MILLISECONDS.toNanos(invocationTimeMS), TimeUnit.MILLISECONDS.toNanos(invocationTimeMS), rows); checker.update(ranges, bytes, rows); checker.check(bps.getLastStatsPoint()); int idleTimeMS = r.nextInt(IDLE_SLEEP_MILLIS); Thread.sleep(idleTimeMS); } // Check the results with fuzzing to avoid rounding errors. checker.check(bps.getOverallStats()); } // // 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(TestStatisticsSuiteRebalanceStats.class, false); } }