gt:             this.min = Math.min(this.min, other.min);
context: /* This file is part of VoltDB.
   * Copyright (C) 2008-2013 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.jdbc;
  
  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 (copied and renamed from exampleutils)
   * @since 2.0
   */
  public class JDBC4PerfCounter 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 final long[] lat = new long[109];
  
      /**
       * Creates a new performance counter and immediately starts tracking time (for rate/second
       * calculations).
       */
      public JDBC4PerfCounter() {
          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 JDBC4PerfCounter(boolean start) {
          if (start)
              this.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 this.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 this.EndTime;
      }
  
      /**
       * Gets the minimum execution latency tracked by this counter.
       */
      public long getMinLatency() {
          return this.min;
      }
  
      /**
       * Gets the maximum execution latency tracked by this counter.
       */
      public long getMaxLatency() {
          return this.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 this.tot;
      }
  
      /**
       * Gets the number of execution calls tracked by this counter.
       */
      public long getExecutionCount() {
          return this.cnt;
      }
  
      /**
       * Gets the number of execution errors tracked by this counter.
       */
      public long getErrorCount() {
          return this.err;
      }
  
      /**
       * Gets the latency distribution buckets of execution calls tracked by this counter.
       */
      public long[] getLatencyBuckets() {
          return this.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 {
              this.EndTime = System.currentTimeMillis();
              if (this.StartTime == Long.MAX_VALUE)
                  this.StartTime = this.EndTime - executionDuration;
              this.cnt++;
              this.tot += executionDuration;
              if (this.min > executionDuration)
                  this.min = executionDuration;
              if (this.max < executionDuration)
                  this.max = executionDuration;
              int bucket = (int) executionDuration;
              if (executionDuration > 100)
                  bucket = Math.min((int) ((executionDuration - 100l) / 50l), 8) + 100;
              this.lat[bucket]++;
              if (!success)
                  this.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 JDBC4PerfCounter clone() {
          JDBC4PerfCounter counter = new JDBC4PerfCounter(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 JDBC4PerfCounter merge(JDBC4PerfCounter other) {
          lock.lock();
          try {
              this.StartTime = Math.min(this.StartTime, other.StartTime);
              this.EndTime = Math.max(this.EndTime, other.EndTime);
  