/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with VoltDB. If not, see <http://www.gnu.org/licenses/>. */ package org.voltdb.utils; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Hashtable; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.HdrHistogram_voltpatches.AbstractHistogram; import org.HdrHistogram_voltpatches.Histogram; import org.voltcore.utils.CompressionStrategySnappy; import org.voltdb.VoltTable; import org.voltdb.client.Client; import org.voltdb.client.ClientConfig; import org.voltdb.client.ClientFactory; import org.voltdb.client.ProcCallException; public class LatencyLogger { private static Histogram m_histogramData; public static String usage() { return "Usage1: server port reportIntervalSeconds [hostname]\n" + "Usage2: filename"; } public static void main(String[] args) throws Exception { final String hostname; if (args.length == 1) { readHistogramFromFile(args[0]); return; } if (args.length != 3) { if (args.length == 4) { hostname = args[3]; } else { System.out.println(usage()); return; } } else { hostname = args[0]; } final String server = args[0]; int dur = 0; try { dur = Integer.valueOf(args[2]); if (dur < 1) { throw new NumberFormatException(); } } catch (NumberFormatException e) { System.out.println( "reportIntervalSeconds should be greater than or equal to 1"); System.out.println(usage()); System.exit(0); } final int duration = dur; // start with an empty password String username = ""; String password = ""; // if credentials set in env, use them if (System.getenv().containsKey("VOLTDBUSER")) { username = System.getenv("VOLTDBUSER"); } if (System.getenv().containsKey("VOLTDBPASSWORD")) { password = System.getenv("VOLTDBPASSWORD"); } // create the client with our credentials ClientConfig clientConfig = new ClientConfig(username, password); final Client c = ClientFactory.createClient(clientConfig); int port = 0; try { port = Integer.valueOf(args[1]); } catch (NumberFormatException e) { System.out.println("Failed to parse port number."); System.out.println("Usage server port reportIntervalSeconds"); System.exit(0); } System.out.println("Connecting to " + server + " port " + port); c.createConnection(args[0], port); System.out.printf( "%12s, %10s, %10s, %10s, %10s, %10s, %10s, %10s\n", "TIMESTAMP", "COUNT", "TPS", "95", "99", "99.9", "99.99", "99.999"); final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS"); ScheduledExecutorService ses = Executors .newSingleThreadScheduledExecutor(); ses.scheduleAtFixedRate(new Runnable() { @Override public void run() { VoltTable table = null; try { table = c.callProcedure("@Statistics", "LATENCY_HISTOGRAM", 0).getResults()[0]; } catch (IOException | ProcCallException e) { System.out.println("Failed to get statistics:"); e.printStackTrace(); System.exit(0); } List<String> hostnames = new ArrayList<String>(); String tableHostname = ""; while (!hostname.equalsIgnoreCase(tableHostname)) { if (!table.advanceRow()) { System.out.println("Server host name " + server + " not found. Valid host names are " + hostnames.toString()); System.exit(0); } tableHostname = table.getString(2); hostnames.add(tableHostname); } Date now = new Date(table.getLong(0)); Histogram newHistogram = AbstractHistogram .fromCompressedBytes(table.getVarbinary(4), CompressionStrategySnappy.INSTANCE); Histogram diffHistogram; if (m_histogramData == null) { diffHistogram = newHistogram; } else { diffHistogram = Histogram.diff(newHistogram, m_histogramData); } long totalCount = diffHistogram.getTotalCount(); if (totalCount > 0) { System.out.printf( "%12s, %10d, %10.0f, %8.2fms, %8.2fms, %8.2fms, %8.2fms, %8.2fms\n", sdf.format(now), totalCount, ((double) totalCount / duration), (diffHistogram.getValueAtPercentile(95.0D) / 1000.0D), (diffHistogram.getValueAtPercentile(99) / 1000.0D), (diffHistogram.getValueAtPercentile(99.9) / 1000.0D), (diffHistogram.getValueAtPercentile(99.99) / 1000.0D), (diffHistogram.getValueAtPercentile(99.999) / 1000.0D)); } else { System.out.printf( "%12s, %10d, %10d, %8.2fms, %8.2fms, %8.2fms, %8.2fms, %8.2fms\n", sdf.format(now), totalCount, 0, 0D, 0D, 0D, 0D, 0D); } m_histogramData = AbstractHistogram.fromCompressedBytes( table.getVarbinary(4), CompressionStrategySnappy.INSTANCE); ; } }, 0, duration, TimeUnit.SECONDS); } public static void readHistogramFromFile(String filename) { System.out.println("Reading histograms from " + filename); Hashtable<String, Histogram> histograms = new Hashtable<String, Histogram>(); Hashtable<String, Long> timestamps = new Hashtable<String, Long>(); System.out.printf( "%23s, %32s, %10s, %10s, %10s, %10s, %10s, %10s, %10s\n", "TIMESTAMP", "HOST NAME", "COUNT", "TPS", "95", "99", "99.9", "99.99", "99.999"); try { BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(new FileInputStream(filename))); String line = null; while ((line = bufferedReader.readLine()) != null) { StringTokenizer st = new StringTokenizer(line, ","); String time = st.nextToken().replaceAll("^\"|\"$", ""); String host_id = st.nextToken().replaceAll("^\"|\"$", ""); String host_name = st.nextToken().replaceAll("^\"|\"$", ""); String site_id = st.nextToken().replaceAll("^\"|\"$", ""); String histogram_val = st.nextToken().replaceAll("^\"|\"$", ""); SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS"); String friendly_ts = sdf.format(new Date(Long.parseLong(time))); byte[] raw_histogram = Encoder.hexDecode(histogram_val); Histogram newHistogram = AbstractHistogram.fromCompressedBytes( raw_histogram, CompressionStrategySnappy.INSTANCE); // Look for a prior histogram for this host Histogram priorHistogram = histograms.get(host_name); Long priorTime = timestamps.get(host_name); Histogram diffHistogram; long diffTime; if (priorHistogram == null) { diffHistogram = newHistogram; diffTime = 1; } else { diffHistogram = Histogram.diff(newHistogram, priorHistogram); diffTime = (Long.parseLong(time) - priorTime)/1000; } // Set the most recent histogram as the prior histogram for this hostname. histograms.put(host_name, newHistogram); timestamps.put(host_name, Long.parseLong(time)); long totalCount = diffHistogram.getTotalCount(); if (totalCount > 0) { System.out.printf( "%23s, %32s, %10d, %10.0f, %8.2fms, %8.2fms, %8.2fms, %8.2fms, %8.2fms\n", friendly_ts, host_name, totalCount, (double) totalCount / diffTime, (diffHistogram.getValueAtPercentile(95.0D) / 1000.0D), (diffHistogram.getValueAtPercentile(99) / 1000.0D), (diffHistogram.getValueAtPercentile(99.9) / 1000.0D), (diffHistogram.getValueAtPercentile(99.99) / 1000.0D), (diffHistogram.getValueAtPercentile(99.999) / 1000.0D)); } else { System.out.printf( "%23s, %32s, %10d, %10d, %8.2fms, %8.2fms, %8.2fms, %8.2fms, %8.2fms\n", friendly_ts, host_name, totalCount, 0, 0D, 0D, 0D, 0D, 0D); } } bufferedReader.close(); } catch (FileNotFoundException e) { System.err.println( "Histogram file '" + filename + "' could not be found."); System.exit(-1); } catch (IOException e) { System.err.println(e.getMessage()); System.exit(-1); } //System.out.println("Number of Histograms: " + histograms.size()); } }