Package voldemort.store.stats

Source Code of voldemort.store.stats.RequestCounter

/*
* Copyright 2012 LinkedIn, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package voldemort.store.stats;


import org.tehuti.Metric;
import org.tehuti.metrics.MetricConfig;
import org.tehuti.metrics.MetricsRepository;
import org.tehuti.metrics.Sensor;
import org.tehuti.metrics.stats.*;
import org.tehuti.utils.Time;
import org.tehuti.utils.SystemTime;
import org.apache.log4j.Logger;

import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* A thread-safe request counter that calculates throughput for a specified
* duration of time.
*
*
*/
public class RequestCounter {

    private final Time time;

    // Sensors
    private Sensor timeSensor, emptyResponseKeysSensor, valueBytesSensor, keyBytesSensor, getAllKeysCountSensor;

    // Metrics
    private Metric
            // Averages
            latencyAverage, valueBytesAverage, keyBytesAverage,
            // Percentiles
            latency10thPercentile, latency50thPercentile, latency95thPercentile, latency99thPercentile,
            // Maximums
            latencyMax, valueBytesMax, keyBytesMax, getAllKeysCountMax,
            // Sampled Totals
            getAllKeysCountSampledTotal, emptyResponseKeysSampledTotal,
            // Sampled Counts
            requestSampledCount,
            // All-time Count
            requestAllTimeCount,
            // Rates
            getAllKeysThroughput, requestThroughput, requestThroughputInBytes;

    private MetricsRepository metricsRepository;

    private static final Logger logger = Logger.getLogger(RequestCounter.class.getName());

    public RequestCounter(String name, long durationMs) {
        this(name, durationMs, new SystemTime(), false, (RequestCounter[]) null);
    }

    /**
     * @param durationMs specifies for how long you want to maintain this
     *        counter (in milliseconds).
     */
    public RequestCounter(String name, long durationMs, RequestCounter... parents) {
        this(name, durationMs, new SystemTime(), false, parents);
    }

    public RequestCounter(String name, long durationMs, boolean useHistogram) {
        this(name, durationMs, new SystemTime(), useHistogram, (RequestCounter[]) null);
    }

    /**
     * @param durationMs specifies for how long you want to maintain this
     *        counter (in milliseconds). useHistogram indicates that this
     *        counter should also use a histogram.
     */
    public RequestCounter(String name, long durationMs, boolean useHistogram, RequestCounter... parents) {
        this(name, durationMs, new SystemTime(), useHistogram, parents);
    }

    RequestCounter(String name, long durationMs, Time time) {
        this(name, durationMs, time, false, (RequestCounter[]) null);
    }

    /**
     * For testing request expiration via an injected time provider
     */
    RequestCounter(String name, long durationMs, Time time, RequestCounter... parents) {
        this(name, durationMs, time, false, parents);
    }

    RequestCounter(String name, long durationMs, Time time, boolean useHistogram, RequestCounter... parents) {

        this.time = time;

        this.metricsRepository = new MetricsRepository(time);

        // Initialize parent sensors arrays...
        int amountOfParentSensors = 0;
        if (parents != null) {
            amountOfParentSensors = parents.length;
        }
        Sensor[] timeParentSensors = null;
        Sensor[] emptyResponseKeysParentSensors = null;
        Sensor[] getAllKeysCountParentSensors = null;
        Sensor[] valueBytesParentSensors = new Sensor[amountOfParentSensors + 1];
        Sensor[] keyBytesParentSensors = new Sensor[amountOfParentSensors + 1];

        if (parents != null && parents.length > 0) {
            timeParentSensors = new Sensor[parents.length];
            emptyResponseKeysParentSensors = new Sensor[parents.length];
            getAllKeysCountParentSensors = new Sensor[parents.length];
            for (int i = 0; i < parents.length; i++) {
                timeParentSensors[i] = parents[i].timeSensor;
                emptyResponseKeysParentSensors[i] = parents[i].emptyResponseKeysSensor;
                getAllKeysCountParentSensors[i] = parents[i].getAllKeysCountSensor;
                valueBytesParentSensors[i] = parents[i].valueBytesSensor;
                keyBytesParentSensors[i] = parents[i].keyBytesSensor;
            }
        }

        // Initialize MetricConfig
        MetricConfig metricConfig = new MetricConfig().timeWindow(durationMs, TimeUnit.MILLISECONDS);

        // Time Sensor

        String timeSensorName = name + ".time";
        this.timeSensor =
                metricsRepository.sensor(timeSensorName, metricConfig, timeParentSensors);
        if (useHistogram) {
            Percentiles timePercentiles = new Percentiles(40000, 10000, Percentiles.BucketSizing.LINEAR,
                    new Percentile(timeSensorName + ".10thPercentile", 10),
                    new Percentile(timeSensorName + ".50thPercentile", 50),
                    new Percentile(timeSensorName + ".95thPercentile", 95),
                    new Percentile(timeSensorName + ".99thPercentile", 99));
            Map<String, Metric> percentiles = this.timeSensor.add(timePercentiles);
            this.latency10thPercentile = percentiles.get(timeSensorName + ".10thPercentile");
            this.latency50thPercentile = percentiles.get(timeSensorName + ".50thPercentile");
            this.latency95thPercentile = percentiles.get(timeSensorName + ".95thPercentile");
            this.latency99thPercentile = percentiles.get(timeSensorName + ".99thPercentile");
        }
        this.latencyMax = this.timeSensor.add(timeSensorName + ".max", new Max(0));
        this.latencyAverage = this.timeSensor.add(timeSensorName + ".avg", new Avg());
        // Sampled count, all-time count and throughput rate, piggy-backing off of the Time Sensor
        this.requestSampledCount = this.timeSensor.add(name + ".sampled-count", new SampledCount());
        this.requestAllTimeCount = this.timeSensor.add(name + ".count", new Count());
        this.requestThroughput = this.timeSensor.add(name + ".throughput", new OccurrenceRate());

        // Empty Reponse Keys Sensor

        String emptyResponseKeysSensorName = name + ".empty-response-keys";
        this.emptyResponseKeysSensor =
                metricsRepository.sensor(emptyResponseKeysSensorName, metricConfig, emptyResponseKeysParentSensors);
        this.emptyResponseKeysSampledTotal =
                this.emptyResponseKeysSensor.add(emptyResponseKeysSensorName + ".sampled-total", new SampledTotal());

        // Key and Value Bytes Sensor

        String keyAndValueBytesSensorName= name + ".key-and-value-bytes";
        Sensor keyAndValueBytesSensor = metricsRepository.sensor(keyAndValueBytesSensorName, metricConfig);
        this.requestThroughputInBytes =
                keyAndValueBytesSensor.add(keyAndValueBytesSensorName + ".bytes-throughput", new Rate());
        valueBytesParentSensors[amountOfParentSensors] = keyAndValueBytesSensor;
        keyBytesParentSensors[amountOfParentSensors] = keyAndValueBytesSensor;

        // Value Bytes Sensor

        String valueBytesSensorName = name + ".value-bytes";
        this.valueBytesSensor = metricsRepository.sensor(valueBytesSensorName, metricConfig, valueBytesParentSensors);
        this.valueBytesMax = this.valueBytesSensor.add(valueBytesSensorName + ".max", new Max(0));
        this.valueBytesAverage = this.valueBytesSensor.add(valueBytesSensorName + ".avg", new Avg());

        // Key Bytes Sensor

        String keyBytesSensorName = name + ".key-bytes";
        this.keyBytesSensor = metricsRepository.sensor(keyBytesSensorName, metricConfig, keyBytesParentSensors);
        this.keyBytesMax = this.keyBytesSensor.add(keyBytesSensorName + ".max", new Max(0));
        this.keyBytesAverage = this.keyBytesSensor.add(keyBytesSensorName + ".avg", new Avg());

        // Get All Keys Count Sensor

        String getAllKeysCountSensorName = name + ".get-all-keys-count";
        this.getAllKeysCountSensor =
                metricsRepository.sensor(getAllKeysCountSensorName, metricConfig, getAllKeysCountParentSensors);
        this.getAllKeysCountSampledTotal =
                this.getAllKeysCountSensor.add(getAllKeysCountSensorName + ".sampled-total", new SampledTotal());
        this.getAllKeysCountMax = this.getAllKeysCountSensor.add(getAllKeysCountSensorName + ".max", new Max(0));
        this.getAllKeysThroughput = this.getAllKeysCountSensor.add(getAllKeysCountSensorName + ".throughput", new Rate());
    }

    /**
     * @return The count of queries tracked by this RequestCounter during the current set of (non-expired) sample windows.
     */
    public long getCount() {
        return (long) requestSampledCount.value();
    }

    /**
     * @return The total amount of queries tracked by this RequestCounter since the beginning of time.
     */
    public long getTotalCount() {
        return (long) requestAllTimeCount.value();
    }

    /**
     * @return The rate of queries per second to this RequestCounter.
     */
    public float getThroughput() {
        return (float) requestThroughput.value();
    }

    /**
     * @return The rate of keys per second that were queried through GetAll requests.
     */
    public float getGetAllKeysThroughput() {
        return (float) getAllKeysThroughput.value();
    }

    /**
     * @return The rate of bytes per second for queries tracked by this RequestCounter.
     */
    public float getThroughputInBytes() {
        return (float) requestThroughputInBytes.value();
    }

    public String getDisplayThroughput() {
        return String.format("%.2f", getThroughput());
    }

    public double getAverageTimeInMs() {
        return latencyAverage.value();
    }

    public String getDisplayAverageTimeInMs() {
        return String.format("%.4f", getAverageTimeInMs());
    }

    public long getMaxLatencyInMs() {
        return (long) latencyMax.value();
    }

    /**
     * @param timeNS time of operation, in nanoseconds
     */
    public void addRequest(long timeNS) {
        addRequest(timeNS, 0, 0, 0, 0);
    }

    /**
     * Detailed request to track additional data about PUT, GET and GET_ALL
     *
     * @param timeNS The time in nanoseconds that the operation took to complete
     * @param numEmptyResponses For GET and GET_ALL, how many keys were no values found
     * @param valueBytes Total number of bytes across all versions of values' bytes
     * @param keyBytes Total number of bytes in the keys
     * @param getAllAggregatedCount Total number of keys returned for getAll calls
     */
    public void addRequest(long timeNS,
                           long numEmptyResponses,
                           long valueBytes,
                           long keyBytes,
                           long getAllAggregatedCount) {
        // timing instrumentation (trace only)
        long startTimeNs = 0;
        if(logger.isTraceEnabled()) {
            startTimeNs = System.nanoTime();
        }

        long currentTime = time.milliseconds();

        timeSensor.record((double) timeNS / voldemort.utils.Time.NS_PER_MS, currentTime);
        emptyResponseKeysSensor.record(numEmptyResponses, currentTime);
        valueBytesSensor.record(valueBytes, currentTime);
        keyBytesSensor.record(keyBytes, currentTime);
        getAllKeysCountSensor.record(getAllAggregatedCount, currentTime);

        // timing instrumentation (trace only)
        if(logger.isTraceEnabled()) {
            logger.trace("addRequest took " + (System.nanoTime() - startTimeNs) + " ns.");
        }
    }

    /**
     * @return the number of requests that have returned returned no value for the requested key. Tracked only for GET.
     */
    public long getNumEmptyResponses() {
        return (long) emptyResponseKeysSampledTotal.value();
    }

    /**
     * @return the size of the largest response or request in bytes returned. Tracked only for GET, GET_ALL and PUT.
     */
    public long getMaxValueSizeInBytes() {
        return (long) valueBytesMax.value();
    }

    /**
     * @return the size of the largest response or request in bytes returned.
     */
    public long getMaxKeySizeInBytes() {
        return (long) keyBytesMax.value();
    }

    /**
     * @return the average size of all the versioned values returned. Tracked only for GET, GET_ALL and PUT.
     */
    public double getAverageValueSizeInBytes() {
        return valueBytesAverage.value();
    }

    /**
     * @return the average size of all the keys. Tracked for all operations.
     */
    public double getAverageKeySizeInBytes() {
        return keyBytesAverage.value();
    }

    /**
     * @return the aggregated number of keys returned across all getAll calls,
     * taking into account multiple values returned per call.
     */
    public long getGetAllAggregatedCount() {
        return (long) getAllKeysCountSampledTotal.value();
    }

    /**
     * @return the maximum number of keys returned across all getAll calls.
     */
    public long getGetAllMaxCount() {
        return (long) getAllKeysCountMax.value();
    }
   
    public double getQ10LatencyMs() {
        return latency10thPercentile.value();
    }
   
    public double getQ50LatencyMs() {
        return latency50thPercentile.value();
    }
   
    public double getQ95LatencyMs() {
        return latency95thPercentile.value();
    }
   
    public double getQ99LatencyMs() {
        return latency99thPercentile.value();
    }
}
TOP

Related Classes of voldemort.store.stats.RequestCounter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.