/* Copyright [2013-2014] eBay Software Foundation 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. */ /* Copyright 2012 eBay Software Foundation 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.ebay.cloud.cms.sysmgmt.monitor.metrics; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Random; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ebay.cloud.cms.utils.EqualsUtil; public class SlidingWindowMetricsTest { private static Logger logger = LoggerFactory.getLogger(SlidingWindowMetricsTest.class); @Before public void setUp() throws Exception { } @After public void tearDown() throws Exception { } @Test public void test01() { long startMillis = System.currentTimeMillis(); long endMillis = startMillis + 3600L * 1000L; long deltaMillis = 20; int maxValue = 10000; TimedMetric[] testMetrics = generateMetrics(startMillis, endMillis, deltaMillis, maxValue); int windowSeconds = 3600; int bucketSeconds = 100; int slotNumber = 1000; AccurateTimeWindowMetric accWindow = new AccurateTimeWindowMetric("acc_metric_1", "qps_acc", null, 10, windowSeconds); ApproximateTimeWindowMetric appWindow = new ApproximateTimeWindowMetric("app_metric_1", "qps_app", 100, windowSeconds, bucketSeconds, maxValue, slotNumber); appWindow.initialize(); int testInterval = 1000; int size = testMetrics.length; for (int i = 0; i < size; i++) { TimedMetric metric = testMetrics[i]; accWindow.addValue(metric.value, metric.timestamp, null); appWindow.addValue(metric.value, metric.timestamp, null); if (i % testInterval == 0) { long timestamp = System.currentTimeMillis(); accWindow.snapshot(timestamp); appWindow.snapshot(timestamp); compareWindowMetrics(accWindow, appWindow, maxValue, slotNumber); } if (i % (10 * testInterval) == 0) { logger.info("left count = " + (size - i)); } } Map<String, Object> accOutput = accWindow.output(); Assert.assertNotNull(accOutput); Assert.assertEquals(5, accOutput.size()); Map<String, Object> appOutput = appWindow.output(); Assert.assertNotNull(appOutput); Assert.assertEquals(5, appOutput.size()); } private void compareWindowMetrics(AccurateTimeWindowMetric accWindow, ApproximateTimeWindowMetric appWindow, int maxValue, int slotNumber) { int delta = maxValue / slotNumber; int percentile = 0; int errorCount = 0; while (percentile <= 100) { long accValue = accWindow.getPercentileValue(percentile); long appValue = appWindow.getPercentileValue(percentile); Assert.assertTrue(accValue <= accWindow.getMaxValue()); Assert.assertTrue(appValue <= appWindow.getMaxValue()); if (appValue < maxValue) { if (Math.abs(accValue - appValue) > delta * 100) { errorCount++; } } else { Assert.assertTrue(appValue >= maxValue); } percentile += 20; } Assert.assertEquals(errorCount, 5, 5); } private static class TimedMetric { public final long timestamp; public final long value; public TimedMetric(long timestamp, long value) { this.timestamp = timestamp; this.value = value; } } private static TimedMetric[] generateMetrics(long startMillis, long endMillis, long deltaMillis, int maxValue) { List<TimedMetric> metricsList = new ArrayList<TimedMetric>(); long timestamp = startMillis; Random rand = new Random(); while (timestamp < endMillis) { double d = Math.abs(rand.nextGaussian()); int value = (int) (maxValue / 2 * d); TimedMetric metric = new TimedMetric(timestamp, value); metricsList.add(metric); timestamp += deltaMillis * d; } return metricsList.toArray(new TimedMetric[0]); } @Test public void test02InvalidInterval() { int maxValue = 10000; int windowSeconds = 0; int bucketSeconds = 100; int slotNumber = 1000; // 1. test snapshoting not starting case: should get 0 for max value, // qps AccurateTimeWindowMetric accWindow = new AccurateTimeWindowMetric("acc_metric_1", "qps_acc", null, 10, windowSeconds); Assert.assertTrue(accWindow.getMaxValue() == 0); Assert.assertTrue(accWindow.getQps() == 0); ApproximateTimeWindowMetric appWindow = new ApproximateTimeWindowMetric("app_metric_1", "qps_app", 100, windowSeconds, bucketSeconds, maxValue, slotNumber); appWindow.initialize(); Assert.assertTrue(appWindow.getMaxValue() == 0); Assert.assertTrue(appWindow.getQps() == 0); // 2. test snapshot after window flipping case: the data before the // window seconds, should be removed windowSeconds = 1; long now = System.currentTimeMillis(); TimedMetric tm1 = new TimedMetric(now, 10), tm2 = new TimedMetric(now + 2000L, 5); accWindow = new AccurateTimeWindowMetric("acc_metric_1", "qps_acc", null, 10, 1); accWindow.addValue(tm1.value, tm1.timestamp, null); accWindow.snapshot(tm1.timestamp + 100); Assert.assertEquals(tm1.value, accWindow.getMaxValue()); accWindow.addValue(tm2.value, tm2.timestamp, null); accWindow.snapshot(tm2.timestamp + 100); Assert.assertEquals(tm2.value, accWindow.getMaxValue()); // 3. Can not call getPercentileValue() by given 0 and 100 to get the // 0th and max value try { accWindow.getPercentileValue(0); } catch (IllegalArgumentException e) { // expected illegal argument } // 4. Get max value of Approximate window should use getMaxValue() not // getPercentileValue(100) appWindow = new ApproximateTimeWindowMetric("app_metric_1", "qps_app", 100, windowSeconds, bucketSeconds, maxValue, slotNumber); appWindow.initialize(); try { appWindow.getPercentileValue(100); // Assert.fail();//not support percentile 100 } catch (IllegalArgumentException e) { // expected illegal argument } } @Test public void test03FlipWindow() { long startMillis = System.currentTimeMillis(); long endMillis = startMillis + 3600L * 1000L; long deltaMillis = 20; int maxValue = 10000; // metric sorted by time stamp TimedMetric[] testMetrics = generateMetrics(startMillis, endMillis, deltaMillis, maxValue); // make metric inverse sorting Arrays.sort(testMetrics, new Comparator<TimedMetric>() { @Override public int compare(TimedMetric object1, TimedMetric object2) { // reverse order return EqualsUtil.compare(object2.timestamp, object1.timestamp); } }); // int windowSeconds = 60; int bucketSeconds = 10; int slotNumber = 1000; ApproximateTimeWindowMetric appWindow = new ApproximateTimeWindowMetric("app_metric_1", "qps_app", 100, windowSeconds, bucketSeconds, maxValue, slotNumber); appWindow.initialize(); // int inWindowSize = 0; for (int i = 0; i < testMetrics.length; i++) { TimedMetric tm = testMetrics[i]; appWindow.addValue(tm.value, tm.timestamp, null); // if ((endMillis - tm.timestamp) < ((windowSeconds - bucketSeconds) // * 1000L)) { // inWindowSize++; // } if (i > 0 && i % 1000 == 0) { appWindow.snapshot(tm.timestamp); Assert.assertTrue(appWindow.getQps() >= 0); } appWindow.getPercentileValue(95); } System.out.println(appWindow.getValueCount()); // Assert.assertEquals(inWindowSize, appWindow.getValueCount()); } @Test(expected = IllegalArgumentException.class) public void test04InvalidValue() { int maxValue = 10000; int windowSeconds = 60; int bucketSeconds = 10; int slotNumber = 1000; ApproximateTimeWindowMetric appWindow = new ApproximateTimeWindowMetric("app_metric_1", "qps_app", 100, windowSeconds, bucketSeconds, maxValue, slotNumber); appWindow.initialize(); appWindow.addValue(-1, System.currentTimeMillis(), null); } /** * From data sync: maxvalue is not set as zero */ @Test public void test05MaxValueReset() { long startMillis = System.currentTimeMillis(); long endMillis = startMillis + 3600L * 100L; long deltaMillis = 20; long maxValue = 3600L * 10L * 10L; TimedMetric[] testMetrics = fakeMetrics(startMillis, endMillis, deltaMillis, maxValue, 2); int windowSeconds = 360; int bucketSeconds = 10; int slotNumber = 1000; ApproximateTimeWindowMetric appWindow = new ApproximateTimeWindowMetric("app_metric_1", "qps_app", 100, windowSeconds, bucketSeconds, maxValue, slotNumber); appWindow.initialize(); int testInterval = 1000; int size = testMetrics.length; int snapshotCount = 0; long lastSnapshoTime = 0; for (int i = 0; i < size; i++) { TimedMetric metric = testMetrics[i]; if (i % testInterval == 0) { long timestamp = startMillis + i * deltaMillis; if (timestamp - startMillis > windowSeconds * 1000L) { appWindow.addValue(metric.value, metric.timestamp, null); // a flipnext() MUST happen in this snapshot long oldMax = appWindow.getMaxValue(); appWindow.snapshot(timestamp); long newMax = appWindow.getMaxValue(); System.out.println("i: " + i + ". oldMax: " + oldMax + ", new Max:" + newMax); Assert.assertTrue((oldMax == 0 && newMax == 0) || (newMax > oldMax)); Assert.assertTrue(appWindow.getValueCount() < i); if (snapshotCount != 0 && (appWindow.getValueCount() == 0 || newMax == 0)) { Assert.fail(); } } else { appWindow.addValue(metric.value, metric.timestamp, null); long oldMax = appWindow.getMaxValue(); appWindow.snapshot(timestamp); long newMax = appWindow.getMaxValue(); System.out.println("i: " + i + ". oldMax: " + oldMax + ", new Max:" + newMax); } lastSnapshoTime = timestamp; System.out.println("last snapshot time:" + lastSnapshoTime); snapshotCount++; } else { appWindow.addValue(metric.value, metric.timestamp, null); } if (i % (10 * testInterval) == 0) { logger.info("left count = " + (size - i)); } } // make sure we could invalidate all metrics above long oldMax = appWindow.getMaxValue(); Assert.assertTrue(oldMax > 0); Assert.assertTrue(appWindow.getValueCount() > 0); long lastRest = endMillis + windowSeconds * 1000L * 2; appWindow.snapshot(lastRest); Assert.assertTrue(appWindow.getMaxValue() == 0); Assert.assertTrue(appWindow.getValueCount() == 0); } private static TimedMetric[] fakeMetrics(long startMillis, long endMillis, long deltaMillis, long maxValue, int step) { List<TimedMetric> metricsList = new ArrayList<TimedMetric>(); long timestamp = startMillis; Random rand = new Random(); long base = 0; while (timestamp < endMillis) { double d = Math.abs(rand.nextGaussian()); long value = (long) (step / 2 * d); value = value + base; TimedMetric metric = new TimedMetric(timestamp, value); metricsList.add(metric); base = base + step; timestamp += deltaMillis * d; } return metricsList.toArray(new TimedMetric[0]); } }