org.voltdb.client.exampleutils.PerfCounter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of voltdbclient Show documentation
Show all versions of voltdbclient Show documentation
VoltDB client interface libraries
/* 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 .
*/
package org.voltdb.client.exampleutils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.voltdb.client.ClientResponse;
/**
* A thread-safe performance counter used to track procedure/statement execution statistics.
*
* @author Seb Coursol
* @since 2.0
*/
public class PerfCounter implements Cloneable
{
private static final Lock lock = new ReentrantLock();
private long StartTime = Long.MAX_VALUE;
private long EndTime = Long.MIN_VALUE;
private long min = 999999999l;
private long max = -1l;
private long tot = 0l;
private long cnt = 0l;
private long err = 0l;
/*
* The buckets cover 0 - 500ms+.
*
* The first 100 buckets cover 0 - 100ms, 1ms each (e.g. (0, 1ms]). The next
* 8 buckets cover 100 - 500ms, 50ms each. The last one covers 500ms+.
*/
private long[] lat = new long[109];
/**
* Creates a new performance counter and immediately starts tracking time (for rate/second calculations).
*/
public PerfCounter() { this(true); }
/**
* Creates a new performance counter, optionally starting time tracking.
*
* @param start the flag indicating whether time tracking should be started immediately. When false time tracking is started on the first counter update.
*/
public PerfCounter(boolean start)
{
if (start)
StartTime = System.currentTimeMillis();
}
/**
* Gets the time (in milliseconds since 1/1/1970 00:00 UTC) at which this counter's time tracking began (the time at which this counter was created - if created with the start parameter set to true - or the time of the first update).
*/
public long getStartTime()
{
return StartTime;
}
/**
* Gets the time (in milliseconds since 1/1/1970 00:00 UTC) at which this counter's time tracking ended (the time if the last update).
*/
public long getEndTime()
{
return EndTime;
}
/**
* Gets the minimum execution latency tracked by this counter.
*/
public long getMinLatency()
{
return min;
}
/**
* Gets the maximum execution latency tracked by this counter.
*/
public long getMaxLatency()
{
return max;
}
/**
* Gets the total execution duration tracked by this counter (the sum of execution durations of all calls tracked by this counter).
*/
public long getTotalExecutionDuration()
{
return tot;
}
/**
* Gets the number of execution calls tracked by this counter.
*/
public long getExecutionCount()
{
return cnt;
}
/**
* Gets the number of execution errors tracked by this counter.
*/
public long getErrorCount()
{
return err;
}
/**
* Gets the latency distribution buckets of execution calls tracked by this counter.
*/
public long[] getLatencyBuckets()
{
return lat;
}
/**
* Gets the elapsed duration between the start and end time of this counter.
*/
public long getElapsedDuration()
{
return this.EndTime-this.StartTime;
}
/**
* Gets the average number of execution calls per second for all execution calls tracked by this counter.
*/
public double getTransactionRatePerSecond()
{
return getExecutionCount()*1000d/getElapsedDuration();
}
/**
* Gets the average execution latency for calls tracked by this counter.
*/
public double getAverageLatency()
{
return (double)getTotalExecutionDuration()/(double)getExecutionCount();
}
/**
* Tracks a call execution by processing the ClientResponse sent back by the VoltDB server.
*
* @param response the response sent by the VoltDB server, containing details about the procedure/statement execution.
*/
public void update(ClientResponse response)
{
this.update(response.getClientRoundtrip(), response.getStatus() == ClientResponse.SUCCESS);
}
/**
* Tracks a generic call execution by reporting the execution duration. This method should be used for successful calls only.
*
* @param executionDuration the duration of the execution call to track in this counter.
* @see #update(long executionDuration, boolean success)
*/
public void update(long executionDuration)
{
this.update(executionDuration, true);
}
/**
* Tracks a generic call execution by reporting the execution duration. This method should be used for successful calls only.
*
* @param executionDuration the duration of the execution call to track in this counter.
* @param success the flag indicating whether the execution call was successful.
*/
public void update(long executionDuration, boolean success)
{
lock.lock();
try
{
EndTime = System.currentTimeMillis();
if (StartTime == Long.MAX_VALUE)
StartTime = EndTime-executionDuration;
cnt++;
tot+= executionDuration;
if (min > executionDuration)
min = executionDuration;
if (max < executionDuration)
max = executionDuration;
int bucket = (int) executionDuration;
if (executionDuration > 100)
bucket = Math.min((int)((executionDuration-100l)/50l),8) + 100;
lat[bucket]++;
if (!success)
err++;
}
finally
{
lock.unlock();
}
}
/**
* Gets a representation of this counter as a single-line short-format string detailing the statistics tracked by this counter.
*
* @return the string representation of this counter.
* @see #toString(boolean useSimpleFormat)
*/
@Override
public String toString()
{
return toString(true);
}
/**
* Gets a representation of this counter as a string detailing the statistics tracked by this counter.
*
* @param useSimpleFormat the flag indicating whether to use a short one-line format, or detailed statistics including latency bucketing.
* @return the string representation of this counter.
*/
public String toString(boolean useSimpleFormat)
{
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
long elapsedDuration = (this.StartTime == Long.MAX_VALUE) ? 1 : this.EndTime-this.StartTime;
if (useSimpleFormat)
return String.format(
"%8s | Txn.: %,11d%s @ %,11.1f TPS | Lat. = %7d < %7.2f < %7d\n"
, dateFormat.format(new Date(Math.round(elapsedDuration/1000d)*1000l))
, this.cnt
, this.err > 0 ? String.format(" [!%,11d]", this.err) : ""
, (this.cnt*1000d / (double)elapsedDuration)
, this.min == 999999999l ? 0l : this.min
, (double)this.tot/(double)this.cnt
, this.max == -1l ? 0l : this.max
);
else {
long[] coarseLat = new long[9];
// Roll up the latencies below 100ms
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 25; j++) {
coarseLat[i] += this.lat[i * 25 + j];
}
}
// Roll up the rest
for (int i = 4; i < 8; i++) {
coarseLat[i] = this.lat[100 + i - 4];
}
for (int j = 104; j < this.lat.length; j++) {
coarseLat[8] += this.lat[j];
}
return String.format(
"-------------------------------------------------------------------------------------\n" +
"Final: | Txn.: %,11d%s @ %,11.1f TPS | Lat. = %7d < %7.2f < %7d\n" +
"-------------------------------------------------------------------------------------\n" +
"Lat.: 25 < 50 < 75 < 100 < 150 < 200 < 250 < 300 < 300+\n" +
"-------------------------------------------------------------------------------------\n" +
"%% %6.2f | %6.2f | %6.2f | %6.2f | %6.2f | %6.2f | %6.2f | %6.2f | %6.2f\n"
, this.cnt
, this.err > 0 ? String.format(" [!%,11d]", this.err) : ""
, (this.cnt*1000d / elapsedDuration)
, this.min == 999999999l ? 0l : this.min
, (double)this.tot/(double)this.cnt
, this.max == -1l ? 0l : this.max
, 100*(double)coarseLat[0]/this.cnt
, 100*(double)coarseLat[1]/this.cnt
, 100*(double)coarseLat[2]/this.cnt
, 100*(double)coarseLat[3]/this.cnt
, 100*(double)coarseLat[4]/this.cnt
, 100*(double)coarseLat[5]/this.cnt
, 100*(double)coarseLat[6]/this.cnt
, 100*(double)coarseLat[7]/this.cnt
, 100*(double)coarseLat[8]/this.cnt
);
}
}
/**
* Format the statistics into a delimiter separated string.
*
* Currently, the format is
* "start (ms), end (ms), total proc calls, min lat., max lat., lat. buckets..."
*
* @param delimiter Delimiter to separate values, e.g. ',' or '\t'
* @return
*/
public String toRawString(char delimiter)
{
StringBuilder sb = new StringBuilder();
sb.append(getStartTime())
.append(delimiter)
.append(getEndTime())
.append(delimiter)
.append(getExecutionCount())
.append(delimiter)
.append(getMinLatency())
.append(delimiter)
.append(getMaxLatency());
// There are 109 buckets
for (long latency : getLatencyBuckets()) {
sb.append(delimiter).append(latency);
}
return sb.toString();
}
/**
* Clones this counter to provide a snapshot copy of statistics.
*
* @return the snapshot clone of this counter.
*/
@Override
public PerfCounter clone()
{
PerfCounter counter = new PerfCounter(false);
counter.StartTime = this.StartTime;
counter.EndTime = this.EndTime;
counter.min = this.min;
counter.max = this.max;
counter.tot = this.tot;
counter.cnt = this.cnt;
counter.err = this.err;
for(int i=0;i<9;i++)
counter.lat[i] = this.lat[i];
return counter;
}
/**
* Merges the statistics of this counter with another counter.
*
* @param other the counter to merge statistics from.
* @return the reference to the current counter after modification. Useful for command-chaining.
*/
public PerfCounter merge(PerfCounter other)
{
lock.lock();
try
{
this.StartTime = Math.min(this.StartTime, other.StartTime);
this.EndTime = Math.max(this.EndTime, other.EndTime);
this.min = Math.min(this.min,other.min);
this.max = Math.max(this.max,other.max);
this.tot = this.tot+other.tot;
this.cnt = this.cnt+other.cnt;
this.err = this.err+other.err;
for(int i=0;i<109;i++)
this.lat[i] = this.lat[i]+other.lat[i];
}
finally
{
lock.unlock();
}
return this;
}
/**
* Merges the statistics of a list of counters.
*
* @param counters the list of counters to merge statistics from.
* @return the new counter containing aggregated statistics from all the provided counters.
*/
public static PerfCounter merge(PerfCounter[] counters)
{
PerfCounter counter = counters[0].clone();
for(int i=1;i