/*
* Copyright (C) 2012 Facebook, 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.facebook.stats;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.concurrent.atomic.AtomicBoolean;
public class TestMultiWindowRate {
private static final Logger LOG = LoggerFactory.getLogger(TestMultiWindowRate.class);
private DateTime now;
@BeforeMethod(alwaysRun = true)
public void setUp() throws Exception {
now = new DateTime("2010-01-01T00:00:00");
DateTimeUtils.setCurrentMillisFixed(now.getMillis());
}
/**
* This test always succeeds, but it will show you the rate at which a counter
* can be called on your machine when you build this
*/
@Test(groups = "fast")
public void testPerformance() throws Exception {
NumberFormat format = new DecimalFormat();
long i = 0;
long start = System.nanoTime();
long end;
while (i < 20000000) {
i++;
}
end = System.nanoTime();
LOG.info("ceiling rate/s : " + format.format(1000000000 * i/ (end - start)));
final MultiWindowRate rate = new MultiWindowRate(500);
final AtomicBoolean done = new AtomicBoolean(false);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while (!done.get()) {
rate.add(1);
}
}
});
start = System.nanoTime();
end = start;
t.start();
try {
Thread.sleep(3250);
done.set(true);
end = System.nanoTime();
t.join();
} catch (InterruptedException e) {
LOG.error("interrupted");
}
long elapsedNanos = end - start;
LOG.info(
"rate/s : " + format.format(1000000000 * rate.getAllTimeSum() / elapsedNanos)
);
}
@Test(groups = "fast")
public void testRates() throws Exception {
MultiWindowRate rate = new MultiWindowRate();
long initialValue = 60000;
long rate30Seconds = initialValue/ 30;
long rate60Seconds = initialValue / 60;
long rate2Minutes = initialValue / 120;
long rate11Minutes = initialValue / 660;
long rate60Minutes = initialValue / 3600;
long rate120Minutes = initialValue / 7200;
assertRateValues(rate, 0, 0, 0, 0);
advanceNowSeconds(30);
rate.add(initialValue); // rate for 30s period
assertRateValues(rate, rate30Seconds, rate30Seconds, rate30Seconds, rate30Seconds);
advanceNowSeconds(30); // rate for 60s period
assertRateValues(rate, rate60Seconds, rate60Seconds, rate60Seconds, rate60Seconds);
advanceNowMinutes(1); // 1-minute loses value
assertRateValues(rate, 0, rate2Minutes, rate2Minutes, rate2Minutes);
advanceNowMinutes(9); // 10-minute loses a value (total = 11m)
assertRateValues(rate, 0, 0, rate11Minutes, rate11Minutes);
advanceNowMinutes(49); // total= 60m
assertRateValues(rate, 0, 0, rate60Minutes, rate60Minutes);
advanceNowMinutes(60); // total= 120m
assertRateValues(rate, 0, 0, 0, rate120Minutes);
rate.add(initialValue);
// we have sent 2 * initialValue
assertRateValues(
rate,
initialValue / 60,
initialValue / 600,
initialValue / 3600,
2 * initialValue / 7200
);
}
@Test(groups = "fast")
public void testMerge() throws Exception {
MultiWindowRate rate1 = new MultiWindowRate();
MultiWindowRate rate2 = new MultiWindowRate();
rate1.add(1);
rate2.add(2);
Assert.assertEquals(rate1.merge(rate2).getAllTimeSum(), 3);
Assert.assertEquals(rate1.merge(rate2).getHourSum(), 3);
Assert.assertEquals(rate1.merge(rate2).getTenMinuteSum(), 3);
Assert.assertEquals(rate1.merge(rate2).getMinuteSum(), 3);
}
@Test(groups = "fast")
public void testMergeWithZero() throws Exception {
MultiWindowRate rate1 = new MultiWindowRate();
MultiWindowRate rate2 = new MultiWindowRate();
rate2.add(2);
Assert.assertEquals(rate1.merge(rate2).getAllTimeSum(), 2);
Assert.assertEquals(rate1.merge(rate2).getHourSum(), 2);
Assert.assertEquals(rate1.merge(rate2).getTenMinuteSum(), 2);
Assert.assertEquals(rate1.merge(rate2).getMinuteSum(), 2);
}
private void assertRateValues(
MultiWindowRate rate, long minute, long tenMinute, long hour, long allTime
) {
Assert.assertEquals(rate.getMinuteRate(), minute);
Assert.assertEquals(rate.getTenMinuteRate(), tenMinute);
Assert.assertEquals(rate.getHourRate(), hour);
Assert.assertEquals(rate.getAllTimeRate(), allTime);
}
private void advanceNowMinutes(int minutes) {
advanceNowSeconds(minutes * 60);
}
private void advanceNowSeconds(int seconds) {
DateTime updatedNow = new DateTime(now.plusSeconds(seconds));
DateTimeUtils.setCurrentMillisFixed(updatedNow.getMillis());
now = updatedNow;
}
}