/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.util;
import java.util.concurrent.TimeUnit;
/**
* Rate exceed detector. It used to detect exceeding of rate of some operation. If rate is exceeded then method {@link
* #updateAndCheckRate()} returns {@code true}. A {@code RateExceedDetector} gets max allowed permits per seconds in constructor, then it
* checks that required time have elapsed between two calls of method {@link #updateAndCheckRate()}.
* <p/>
* Implementation is not threadsafe and required external synchronization if it's used in multi-thread environment.
* An example that prints on stdout if rate exceed 5 per seconds limit:
* <pre>
* {@code
* final RateExceedDetector r = new RateExceedDetector(5);
*
* void doSomething() {
* if (r.updateAndCheckRate()) {
* // do something useful
* } else {
* System.out.printf("Max rate exceeded, rate: %f 1/s%n", r.getRate());
* }
* }
*
* }
* </pre>
*/
public class RateExceedDetector {
private final long intervalMicros;
private final long reportTimeMicros;
private final long rateTime;
private long count;
private long lastMicros;
private double rate;
private long threshold;
public RateExceedDetector(double permits) {
intervalMicros = (long)(TimeUnit.SECONDS.toMicros(1L) / permits);
reportTimeMicros = TimeUnit.SECONDS.toMicros(1);
lastMicros = TimeUnit.NANOSECONDS.toMicros(System.nanoTime());
rateTime = 1L;
}
/**
* Update rate and check is max allowed rate exceeded.
*
* @return {@code true} if max allowed rate is exceeded and {@code false} otherwise. If this method return {@code true} typically need
* check average rate with method {@link #getRate()}. In some cases it is possible to have smaller then expected interval between two
* calls of this method but average rate may be under allowed limit.
*/
public boolean updateAndCheckRate() {
final long nowTimeMicros = TimeUnit.NANOSECONDS.toMicros(System.nanoTime());
final boolean exceed = (nowTimeMicros - threshold) < 0;
threshold = nowTimeMicros + intervalMicros;
countRate(nowTimeMicros);
return exceed;
}
private void countRate(long nowTimeMicros) {
if (lastMicros + reportTimeMicros < nowTimeMicros) {
rate = count / rateTime;
lastMicros = nowTimeMicros;
count = 0;
}
count++;
}
/** Get average rate of calls method {@link #updateAndCheckRate()}. */
public double getRate() {
return rate;
}
}