/* 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 ddlwindowing; import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.voltdb.VoltTable; import org.voltdb.client.ClientResponse; import org.voltdb.client.ProcCallException; import ddlwindowing.WindowingApp.PartitionInfo; /** * Periodically print a report of insert rates, latencies and failures, * as well as average values of the 'val' column in the 'timedata' table * over several moving windows. Also print per-partition statistics collected * by PartitionDataTracker. The delay between report printing is specified * by the user in the configuration. */ public class Reporter implements Runnable { final WindowingApp app; Reporter(WindowingApp app) { this.app = app; } @Override public void run() { boolean success = true; Map<Integer, Long> averagesForWindows = new TreeMap<Integer, Long>(); // For several time windows, fetch the average value of 'val' for that // time window. // Note, this could be easily be combined into a stored proc and made // a single transactional call if one wanted to. // See ddl.sql for the actual SQL being run by the 'Average' procedure. for (int seconds : new int[] { 1, 5, 10, 30 }) { try { // SQL BEING RUN: // SELECT SUM(sum_values) / SUM(count_values) // FROM agg_by_second // WHERE second_ts >= TO_TIMESTAMP(SECOND, SINCE_EPOCH(SECOND, NOW) - ?); ClientResponse cr = app.client.callProcedure("Average", -seconds); VoltTable result = cr.getResults()[0]; long average = result.asScalarLong(); if (! result.wasNull()) { averagesForWindows.put(seconds, average); } else { // If there are no rows in the selected time window (for example // if we stop the client and then start it up again), then the // average will be NULL. averagesForWindows.put(seconds, null); } } catch (IOException | ProcCallException e) { // Note any failure for reporting later. success = false; } } // Lock protects other (well-behaved) printing from being interspersed with // this report printing. synchronized(app) { long now = System.currentTimeMillis(); long time = Math.round((now - app.startTS) / 1000.0); // Print out how long the processing has been running System.out.printf("%02d:%02d:%02d Report:\n", time / 3600, (time / 60) % 60, time % 60); // If possible, print out the averages over several time windows. if (success) { System.out.println(" Average values over time windows:"); for (Entry<Integer, Long> e : averagesForWindows.entrySet()) { System.out.printf(" Average for past %2ds: %d\n", e.getKey(), e.getValue()); } } else { System.out.println(" Unable to retrieve average values at this time."); } System.out.println(" Partition statistics:"); for (Entry<Long, PartitionInfo> e : app.getPartitionData().entrySet()) { PartitionInfo pinfo = e.getValue(); System.out.printf(" Partition %2d: %9d tuples, youngest: %6.3fs, oldest: %6.3fs\n", e.getKey(), pinfo.tupleCount, pinfo.youngestTupleAge / 1000.0, pinfo.oldestTupleAge / 1000.0); } // Let the inserter process print a one line report. app.inserter.printReport(); // // FAILURE REPORTING FOR PERIODIC OPERATIONS // long partitionTrackerFailures = app.partitionTracker.failureCount.getAndSet(0); if (partitionTrackerFailures > 0) { System.out.printf(" Partition Tracker failed %d times since last report.\n", partitionTrackerFailures); } long maxTrackerFailures = app.maxTracker.failureCount.getAndSet(0); if (maxTrackerFailures > 0) { System.out.printf(" Max Tracker failed %d times since last report.\n", maxTrackerFailures); } System.out.println(); System.out.flush(); } } }