/* * Copyright (C) 2012-2015 DataStax 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 com.datastax.driver.core; import com.datastax.driver.core.exceptions.*; import com.google.common.collect.Lists; import com.google.common.util.concurrent.Uninterruptibles; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.testng.annotations.Test; import java.util.List; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; public abstract class PercentileTrackerTest<B extends PercentileTracker.Builder<B, T>, T extends PercentileTracker> { Host defaultHost = mock(Host.class); @SuppressWarnings("ThrowableResultOfMethodCallIgnored") Exception defaultException = mock(Exception.class); Statement defaultStatement = mock(Statement.class); long defaultMaxLatency = 1000; public abstract B builder(); @Test(groups = "unit") public void should_ignore_certain_exceptions() throws Exception { // given - a percentile tracker. Cluster cluster0 = mock(Cluster.class); T tracker = builder() .withInterval(50, TimeUnit.MILLISECONDS) .withMinRecordedValues(100).build(); tracker.onRegister(cluster0); // when - recording measurements with the following exceptions. List<Exception> exceptionsToIgnore = Lists.<Exception>newArrayList( new UnavailableException(ConsistencyLevel.ANY, 0, 0), new OverloadedException(null, "Overloaded"), new BootstrappingException(null, "Bootstrapping"), new UnpreparedException(null, "Unprepared"), new InvalidQueryException("Validation", new Exception())); long startTime = System.currentTimeMillis(); for (Exception exception : exceptionsToIgnore) { tracker.update(defaultHost, defaultStatement, exception, TimeUnit.NANOSECONDS.convert(999, TimeUnit.MILLISECONDS)); } for (int i = 0; i < 100; i++) { tracker.update(defaultHost, defaultStatement, defaultException, TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS)); } long waitTime = 50 - (System.currentTimeMillis() - startTime); Uninterruptibles.sleepUninterruptibly(waitTime + 100, TimeUnit.MILLISECONDS); // then - the resulting tracker's percentiles should all be 1, indicating those exceptions were ignored. for (int i = 1; i <= 99; i++) { long latencyAtPct = tracker.getLatencyAtPercentile(defaultHost, defaultStatement, defaultException, i); assertThat(latencyAtPct).isEqualTo(1); } } @Test(groups = "unit") public void should_not_record_anything_if_not_enough_measurements() throws Exception { // given - a percentile tracker with 100 min recorded values. Cluster cluster0 = mock(Cluster.class); T tracker = builder() .withInterval(50, TimeUnit.MILLISECONDS) .withMinRecordedValues(100).build(); tracker.onRegister(cluster0); // when - recording less measurements then required. long startTime = System.currentTimeMillis(); for (int i = 0; i < 99; i++) { tracker.update(defaultHost, defaultStatement, defaultException, TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS)); } long waitTime = 50 - (System.currentTimeMillis() - startTime); Uninterruptibles.sleepUninterruptibly(waitTime + 100, TimeUnit.MILLISECONDS); // then - the resulting tracker's percentiles should all be -1, indicating there were not enough values to consider. for (int i = 1; i <= 99; i++) { long latencyAtPct = tracker.getLatencyAtPercentile(defaultHost, defaultStatement, defaultException, i); assertThat(latencyAtPct).isEqualTo(-1); } } @Test(groups = "short") public void should_return_negative_value_when_interval_hasnt_elapsed() throws Exception { // given - a percentile tracker with a long interval. Cluster cluster0 = mock(Cluster.class); T tracker = builder() .withInterval(50, TimeUnit.MINUTES) .withMinRecordedValues(100).build(); tracker.onRegister(cluster0); // when - recording enough measurements. for (int i = 0; i < 99; i++) { tracker.update(defaultHost, defaultStatement, defaultException, TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS)); } // then - the resulting tracker's percentiles should all be -1, since not enough time was given to elapse interval. for (int i = 1; i <= 99; i++) { long latencyAtPct = tracker.getLatencyAtPercentile(defaultHost, defaultStatement, defaultException, i); assertThat(latencyAtPct).isEqualTo(-1); } } @Test(groups = "unit") public void should_not_record_value_and_log_when_measurement_higher_than_max_trackable_value() throws Exception { // given - a percentile tracker with a long interval. Cluster cluster0 = mock(Cluster.class); T tracker = builder() .withInterval(50, TimeUnit.MILLISECONDS) .withMinRecordedValues(100).build(); tracker.onRegister(cluster0); Logger percentileLogger = Logger.getLogger(PercentileTracker.class); Level originalLevel = percentileLogger.getLevel(); percentileLogger.setLevel(Level.WARN); MemoryAppender appender = new MemoryAppender(); percentileLogger.addAppender(appender); try { long startTime = System.currentTimeMillis(); for (int i = 0; i < 100; i++) { tracker.update(defaultHost, defaultStatement, defaultException, TimeUnit.NANOSECONDS.convert(1, TimeUnit.MILLISECONDS)); } // HdrHistogram adjusts its max based on bucket size, with these values it allows a max value of 2047 (2^11-1). long largeLatency = 2048; // when - recording a value larger than max trackable value. tracker.update(defaultHost, defaultStatement, defaultException, TimeUnit.NANOSECONDS.convert(largeLatency, TimeUnit.MILLISECONDS)); assertThat(appender.get()) .contains("Got request with latency of " + largeLatency + " ms, which exceeds the configured maximum trackable value " + defaultMaxLatency); long waitTime = 50 - (System.currentTimeMillis() - startTime); Uninterruptibles.sleepUninterruptibly(waitTime + 100, TimeUnit.MILLISECONDS); // then - the resulting tracker's percentiles should all be 1, indicating the large value was ignored. for (int i = 1; i <= 99; i++) { long latencyAtPct = tracker.getLatencyAtPercentile(defaultHost, defaultStatement, defaultException, i); assertThat(latencyAtPct).isEqualTo(1); } } finally { percentileLogger.setLevel(originalLevel); percentileLogger.removeAppender(appender); } } }