/*
* Copyright 2008-2009 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 static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static voldemort.store.stats.Tracked.DELETE;
import static voldemort.store.stats.Tracked.GET;
import static voldemort.store.stats.Tracked.GET_ALL;
import static voldemort.store.stats.Tracked.GET_VERSIONS;
import static voldemort.store.stats.Tracked.PUT;
import static voldemort.utils.Time.NS_PER_MS;
import java.util.EnumSet;
import org.junit.Test;
public class StoreStatsJmxTest {
@Test
public void getThroughPutShouldBeSumOfThroughputOperations() {
StoreStats stats = mock(StoreStats.class);
when(stats.getThroughput(eq(Tracked.GET))).thenReturn(1.0f);
when(stats.getThroughput(eq(Tracked.GET_ALL))).thenReturn(20.0f);
when(stats.getThroughput(eq(Tracked.DELETE))).thenReturn(300.0f);
when(stats.getThroughput(eq(Tracked.GET_VERSIONS))).thenReturn(4000.0f);
when(stats.getThroughput(eq(Tracked.PUT))).thenReturn(50000.0f);
StoreStatsJmx storeStatsJmx = new StoreStatsJmx(stats);
assertEquals(54321.0f, storeStatsJmx.getOperationThroughput(), 0.1f);
for(Tracked op: EnumSet.of(Tracked.GET,
Tracked.GET_ALL,
Tracked.DELETE,
Tracked.GET_VERSIONS,
Tracked.PUT)) {
verify(stats, times(1)).getThroughput(op);
}
}
// Verify that the various classes are wired correctly between the JMX class
// and the stats tracking classes
@Test
public void getMaxLatenciesWork() {
// Max latency should be tracked across calls and one Op's should not
// affect the others.
for(Tracked op: EnumSet.of(GET, GET_VERSIONS, GET_ALL, DELETE, PUT)) {
StoreStats stats = new StoreStats("tests.getMaxLatenciesWork");
StoreStatsJmx jmx = new StoreStatsJmx(stats);
stats.recordTime(op, 5 * NS_PER_MS);
stats.recordTime(op, 15 * NS_PER_MS);
stats.recordTime(op, 7 * NS_PER_MS);
assertEquals(op == PUT ? 15 : 0, jmx.getMaxPutLatency());
assertEquals(op == GET ? 15 : 0, jmx.getMaxGetLatency());
assertEquals(op == GET_VERSIONS ? 15 : 0, jmx.getMaxGetVersionsLatency());
assertEquals(op == GET_ALL ? 15 : 0, jmx.getMaxGetAllLatency());
assertEquals(op == DELETE ? 15 : 0, jmx.getMaxDeleteLatency());
}
}
// Verify that the logic for determining maximum value returned and average
// value return has been wired up correctly.
@Test
public void maxAndAvgSizeOfValuesAreCalculatedCorrectly() {
for(Tracked op: EnumSet.of(GET, GET_ALL, PUT)) {
StoreStats stats = new StoreStats("tests.maxAndAvgSizeOfValuesAreCalculatedCorrectly");
StoreStatsJmx jmx = new StoreStatsJmx(stats);
long[] valueSizes = new long[] { 100, 450, 200, 300 };
long sum = 0l;
long max = valueSizes[0];
final long timeNS = 1 * NS_PER_MS;
for(long v: valueSizes) {
if(op == GET)
stats.recordGetTime(timeNS, false, v, 0);
else if(op == GET_ALL)
stats.recordGetAllTime(timeNS, 1, 1, v, 0);
else
// PUT
stats.recordPutTimeAndSize(timeNS, v, 0);
sum += v;
max = Math.max(max, v);
}
double average = sum / (double) valueSizes.length;
assertEquals(op == PUT ? max : 0, jmx.getMaxPutSizeInBytes());
assertEquals(op == PUT ? average : Double.NaN, jmx.getAveragePutSizeInBytes(), 0.0);
assertEquals(op == GET ? max : 0, jmx.getMaxGetSizeInBytes());
assertEquals(op == GET ? average : Double.NaN, jmx.getAverageGetSizeInBytes(), 0.0);
assertEquals(op == GET_ALL ? max : 0, jmx.getMaxGetAllSizeInBytes());
assertEquals(op == GET_ALL ? average : Double.NaN, jmx.getAverageGetAllSizeInBytes(), 0.0);
}
}
// Verify that the logic for determining maximum value returned and average
// value return has been wired up correctly.
@Test
public void maxAndAvgSizeOfKeysAreCalculatedCorrectly() {
for(Tracked op: EnumSet.of(GET, GET_ALL, PUT, DELETE)) {
StoreStats stats = new StoreStats("tests.maxAndAvgSizeOfKeysAreCalculatedCorrectly");
StoreStatsJmx jmx = new StoreStatsJmx(stats);
long[] keySizes = new long[] { 100, 450, 200, 300 };
long sum = 0l;
long max = keySizes[0];
final long timeNS = 1 * NS_PER_MS;
for(long k: keySizes) {
if(op == GET)
stats.recordGetTime(timeNS, false, 0, k);
else if(op == GET_ALL)
stats.recordGetAllTime(timeNS, 1, 1, 0, k);
else if(op == DELETE)
stats.recordDeleteTime(timeNS, k);
else
// PUT
stats.recordPutTimeAndSize(timeNS, 0, k);
sum += k;
max = Math.max(max, k);
}
double average = sum / (double) keySizes.length;
assertEquals(op == PUT ? max : 0, jmx.getMaxPutKeySizeInBytes());
assertEquals(op == PUT ? average : Double.NaN, jmx.getAveragePutKeySizeInBytes(), 0.0);
assertEquals(op == GET ? max : 0, jmx.getMaxGetKeySizeInBytes());
assertEquals(op == GET ? average : Double.NaN, jmx.getAverageGetKeySizeInBytes(), 0.0);
assertEquals(op == GET_ALL ? max : 0, jmx.getMaxGetAllKeySizeInBytes());
assertEquals(op == GET_ALL ? average : Double.NaN, jmx.getAverageGetAllKeySizeInBytes(), 0.0);
assertEquals(op == DELETE ? max : 0, jmx.getMaxDeleteKeySizeInBytes());
assertEquals(op == DELETE ? average : Double.NaN, jmx.getAverageDeleteKeySizeInBytes(), 0.0);
}
}
@Test
public void testGetPercentageGetEmptyResponses() {
StoreStats stats = new StoreStats("tests.testGetPercentageGetEmptyResponses");
StoreStatsJmx jmx = new StoreStatsJmx(stats);
stats.recordGetTime(100, false, 1000, 0);
assertEquals(0, jmx.getPercentGetReturningEmptyResponse(), 0.0);
stats.recordGetTime(200, true, 1001, 0);
assertEquals(0.5, jmx.getPercentGetReturningEmptyResponse(), 0.0);
stats.recordGetTime(300, false, 1002, 0);
assertEquals(0.33, jmx.getPercentGetReturningEmptyResponse(), 0.05);
}
@Test
public void testGetPercentageGetAllEmptyResponses() {
StoreStats stats = new StoreStats("tests.testGetPercentageGetAllEmptyResponses");
StoreStatsJmx jmx = new StoreStatsJmx(stats);
stats.recordGetAllTime(100, 2, 2, 1000, 0); // requested values for two
// keys, got both of them
assertEquals(0.0, jmx.getPercentGetAllReturningEmptyResponse(), 0.0);
stats.recordGetAllTime(200, 2, 0, 1001, 0); // requested 2, got 0
assertEquals(0.5, jmx.getPercentGetAllReturningEmptyResponse(), 0.0);
}
@Test
public void testAverageGetAllCount() {
StoreStats stats = new StoreStats("tests.testAverageGetAllCount");
StoreStatsJmx jmx = new StoreStatsJmx(stats);
stats.recordGetAllTime(100, 2, 2, 1000, 0);
assertEquals(2.0, jmx.getAverageGetAllCount(), 0.0);
stats.recordGetAllTime(100, 4, 4, 1000, 0);
assertEquals(3.0, jmx.getAverageGetAllCount(), 0.0);
}
}