/******************************************************************************* * Copyright (c) 2012-2015 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; } }