/*
* Copyright (c) 2005-2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.synapse.transport.passthru.jmx;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;
/**
* Class responsible for update LatencyParameters
*/
public class LatencyParameter {
private static final int SMALL_DATA_COLLECTION_PERIOD = 5;
private static final int LARGE_DATA_COLLECTION_PERIOD = 5 * 60;
private static final int SAMPLES_PER_MINUTE = 60 / SMALL_DATA_COLLECTION_PERIOD;
private static final int SAMPLES_PER_HOUR = (60 * 60) / LARGE_DATA_COLLECTION_PERIOD;
private AtomicLong lastValue;
/**
* Queue of all latency values reported. The short term data collector clears this queue up
* time to time thus ensuring it doesn't grow indefinitely.
*/
private Queue<Long> cache;
/**
* Queue of samples collected by the short term data collector. This is maintained
* as a fixed length queue
*/
private Queue<Long> shortTermCache;
/**
* Queue of samples collected by the long term data collector. This is maintained
* as a fixed length queue
*/
private Queue<Long> longTermCache;
private double allTimeAverage = 0.0;
private int count = 0;
private final Object lock = new Object();
private final Object shortTermCacheLock = new Object();
private final Object longTermCacheLock = new Object();
private final boolean enabled;
public LatencyParameter(boolean enabled) {
this.enabled = enabled;
if (!this.enabled) {
return;
}
lastValue = new AtomicLong(0);
cache = new ConcurrentLinkedQueue<Long>();
shortTermCache = new LinkedList<Long>();
longTermCache = new LinkedList<Long>();
}
/**
* Resetting ShortTerm and LongTermCaches.
*/
public void reset() {
if (!enabled) {
return;
}
lastValue.set(0);
cache.clear();
synchronized (shortTermCacheLock) {
shortTermCache.clear();
}
synchronized (longTermCacheLock) {
longTermCache.clear();
}
synchronized (lock) {
allTimeAverage = 0.0;
count = 0;
}
}
/**
* @return Latency
*/
public long getLatency() {
return enabled ? lastValue.get() : 0L;
}
/**
* @return AllTimeAverage
*/
public double getAllTimeAverage() {
synchronized (lock) {
return enabled ? allTimeAverage : 0.0;
}
}
/**
* update cache
*/
public void updateCache() {
if (!enabled) {
return;
}
updateCacheQueue();
}
public void update(long value) {
if (!enabled) {
return;
}
lastValue.set(value);
cache.offer(lastValue.get());
}
private void updateCacheQueue() {
if (!enabled) {
return;
}
int size = cache.size();
if (size > 0) {
long sum = 0;
for (int i = 0; i < size; i++) {
sum += cache.poll();
}
synchronized (lock) {
allTimeAverage = (allTimeAverage * count + sum) / (count + size);
count = count + size;
}
}
updateShortTermCache(size);
}
private void updateShortTermCache(int size) {
if (!enabled) {
return;
}
long value = getLatency();
synchronized (shortTermCacheLock) {
if (shortTermCache.size() != 0 || value != 0) {
// take a sample for the short term latency calculation
if (shortTermCache.size() == SAMPLES_PER_MINUTE * 15) {
shortTermCache.remove();
}
if (size == 0) {
// there's no latency data available -> no new requests received
shortTermCache.offer(0L);
} else {
shortTermCache.offer(value);
}
}
}
}
/**
* Method for update LongTermCache
*/
public void updateLongTermCache() {
if (!enabled) {
return;
}
synchronized (longTermCacheLock) {
if (longTermCache.size() != 0 || getLatency() != 0) {
if (longTermCache.size() == SAMPLES_PER_HOUR * 24) {
longTermCache.remove();
}
// adds the average latency value in last five minutes
longTermCache.offer((long) getAverageLatencyByMinute(LARGE_DATA_COLLECTION_PERIOD / 60));
}
}
}
public double getAverageLatency15m() {
return getAverageLatencyByMinute(15);
}
public double getAverageLatency5m() {
return getAverageLatencyByMinute(5);
}
public double getAverageLatency1m() {
return getAverageLatencyByMinute(1);
}
public double getAverageLatency24h() {
return getAverageLatencyByHour(24);
}
public double getAverageLatency8h() {
return getAverageLatencyByHour(8);
}
public double getAverageLatency1h() {
return getAverageLatencyByHour(1);
}
private double getAverageLatencyByMinute(int n) {
if (!enabled) {
return 0.0;
}
int samples = n * SAMPLES_PER_MINUTE;
double sum = 0.0;
Long[] array;
synchronized (shortTermCacheLock) {
array = shortTermCache.toArray(new Long[shortTermCache.size()]);
}
if (samples > array.length) {
// If we don't have enough samples collected yet
// add up everything we have
samples = array.length;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
} else {
// We have enough samples to make the right calculation
// Add up starting from the end of the queue (to give the most recent values)
for (int i = 0; i < samples; i++) {
sum += array[array.length - 1 - i];
}
}
if (samples == 0) {
return 0.0;
}
return sum / samples;
}
private double getAverageLatencyByHour(int n) {
if (!enabled) {
return 0.0;
}
int samples = n * SAMPLES_PER_HOUR;
double sum = 0.0;
Long[] array;
synchronized (longTermCacheLock) {
array = longTermCache.toArray(new Long[longTermCache.size()]);
}
if (samples > array.length) {
samples = array.length;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
} else {
for (int i = 0; i < samples; i++) {
sum += array[array.length - 1 - i];
}
}
if (samples == 0) {
return 0.0;
}
return sum / samples;
}
}