/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.bookkeeper.proto; import java.beans.ConstructorProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Bookie Server Stats */ public class BKStats { private static final Logger LOG = LoggerFactory.getLogger(BKStats.class); private static BKStats instance = new BKStats(); public static BKStats getInstance() { return instance; } /** * A read view of stats, also used in CompositeViewData to expose to JMX */ public static class OpStatData { private final long maxLatency, minLatency; private final double avgLatency; private final long numSuccessOps, numFailedOps; private final String latencyHist; @ConstructorProperties({"maxLatency", "minLatency", "avgLatency", "numSuccessOps", "numFailedOps", "latencyHist"}) public OpStatData(long maxLatency, long minLatency, double avgLatency, long numSuccessOps, long numFailedOps, String latencyHist) { this.maxLatency = maxLatency; this.minLatency = minLatency == Long.MAX_VALUE ? 0 : minLatency; this.avgLatency = avgLatency; this.numSuccessOps = numSuccessOps; this.numFailedOps = numFailedOps; this.latencyHist = latencyHist; } public long getMaxLatency() { return maxLatency; } public long getMinLatency() { return minLatency; } public double getAvgLatency() { return avgLatency; } public long getNumSuccessOps() { return numSuccessOps; } public long getNumFailedOps() { return numFailedOps; } public String getLatencyHist() { return latencyHist; } } /** * Operation Statistics */ public static class OpStats { static final int NUM_BUCKETS = 3*9 + 2; long maxLatency = 0; long minLatency = Long.MAX_VALUE; double totalLatency = 0.0f; long numSuccessOps = 0; long numFailedOps = 0; long[] latencyBuckets = new long[NUM_BUCKETS]; OpStats() {} /** * Increment number of failed operations */ synchronized public void incrementFailedOps() { ++numFailedOps; } /** * Update Latency */ synchronized public void updateLatency(long latency) { if (latency < 0) { // less than 0ms . Ideally this should not happen. // We have seen this latency negative in some cases due to the // behaviors of JVM. Ignoring the statistics updation for such // cases. LOG.warn("Latency time coming negative"); return; } totalLatency += latency; ++numSuccessOps; if (latency < minLatency) { minLatency = latency; } if (latency > maxLatency) { maxLatency = latency; } int bucket; if (latency <= 100) { // less than 100ms bucket = (int)(latency / 10); } else if (latency <= 1000) { // 100ms ~ 1000ms bucket = 1 * 9 + (int)(latency / 100); } else if (latency <= 10000) { // 1s ~ 10s bucket = 2 * 9 + (int)(latency / 1000); } else { // more than 10s bucket = 3 * 9 + 1; } ++latencyBuckets[bucket]; } public OpStatData toOpStatData() { double avgLatency = numSuccessOps > 0 ? totalLatency / numSuccessOps : 0.0f; StringBuilder sb = new StringBuilder(); for (int i=0; i<NUM_BUCKETS; i++) { sb.append(latencyBuckets[i]); if (i != NUM_BUCKETS - 1) { sb.append(','); } } return new OpStatData(maxLatency, minLatency, avgLatency, numSuccessOps, numFailedOps, sb.toString()); } /** * Diff with base opstats * * @param base * base opstats * @return diff opstats */ public OpStats diff(OpStats base) { OpStats diff = new OpStats(); diff.maxLatency = this.maxLatency > base.maxLatency ? this.maxLatency : base.maxLatency; diff.minLatency = this.minLatency > base.minLatency ? base.minLatency : this.minLatency; diff.totalLatency = this.totalLatency - base.totalLatency; diff.numSuccessOps = this.numSuccessOps - base.numSuccessOps; diff.numFailedOps = this.numFailedOps - base.numFailedOps; for (int i = 0; i < NUM_BUCKETS; i++) { diff.latencyBuckets[i] = this.latencyBuckets[i] - base.latencyBuckets[i]; } return diff; } /** * Copy stats from other OpStats * * @param other other op stats * @return void */ public synchronized void copyOf(OpStats other) { this.maxLatency = other.maxLatency; this.minLatency = other.minLatency; this.totalLatency = other.totalLatency; this.numSuccessOps = other.numSuccessOps; this.numFailedOps = other.numFailedOps; System.arraycopy(other.latencyBuckets, 0, this.latencyBuckets, 0, this.latencyBuckets.length); } } public static final int STATS_ADD = 0; public static final int STATS_READ = 1; public static final int STATS_UNKNOWN = 2; // NOTE: if add other stats, increment NUM_STATS public static final int NUM_STATS = 3; OpStats[] stats = new OpStats[NUM_STATS]; private BKStats() { for (int i=0; i<NUM_STATS; i++) { stats[i] = new OpStats(); } } /** * Stats of operations * * @return op stats */ public OpStats getOpStats(int type) { return stats[type]; } /** * Set stats of a specified operation * * @param type operation type * @param stat operation stats */ public void setOpStats(int type, OpStats stat) { stats[type] = stat; } /** * Diff with base stats * * @param base base stats * @return diff stats */ public BKStats diff(BKStats base) { BKStats diff = new BKStats(); for (int i=0; i<NUM_STATS; i++) { diff.setOpStats(i, stats[i].diff(base.getOpStats(i))); } return diff; } /** * Copy stats from other stats * * @param other other stats * @return void */ public void copyOf(BKStats other) { for (int i=0; i<NUM_STATS; i++) { stats[i].copyOf(other.getOpStats(i)); } } }