All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.samskivert.util.Throttle Maven / Gradle / Ivy

//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2011 Michael Bayne, et al.
// http://github.com/samskivert/samskivert/blob/master/COPYING

package com.samskivert.util;

/**
 * A throttle is used to prevent code from attempting a particular operation too often. Often it is
 * desirable to retry an operation under failure conditions, but simplistic approaches to retrying
 * operations can lead to large numbers of spurious attempts to do something that will obviously
 * fail. The throttle class provides a mechanism for limiting such attempts by measuring whether or
 * not an activity has been performed N times in the last M seconds. The user of the class decides
 * the appropriate throttle parameters and then simply calls through to throttle to determine
 * whether or not to go ahead with the operation.
 *
 * 

For example: * *

 * protected Throttle _throttle = new Throttle(5, 60000L);
 *
 * public void performOp ()
 *     throws UnavailableException
 * {
 *     if (_throttle.throttleOp()) {
 *         throw new UnavailableException();
 *     }
 *
 *     // perform operation
 * }
 * 
*/ public class Throttle { /** * Constructs a new throttle instance that will allow the specified number of operations to * proceed within the specified period (the period is measured in milliseconds). * *

As operations and period define a ratio, use the smallest value possible for * operations as an array is created to track the time at which each operation was * performed (e.g. use 6 ops per 10 seconds rather than 60 ops per 100 seconds if * possible). However, note that you may not always want to reduce the ratio as much as * possible if you wish to allow bursts of operations up to some large value. */ public Throttle (int operations, long period) { _ops = new long[operations]; _period = period; } /** * Updates the number of operations for this throttle to a new maximum, retaining the current * history of operations if the limit is being increased and truncating the oldest operations * if the limit is decreased. * * @param operations the new maximum number of operations. * @param period the new period. */ public void reinit (int operations, long period) { _period = period; if (operations != _ops.length) { long[] ops = new long[operations]; if (operations > _ops.length) { // copy to a larger buffer, leaving zeroes at the beginning int lastOp = _lastOp + operations - _ops.length; System.arraycopy(_ops, 0, ops, 0, _lastOp); System.arraycopy(_ops, _lastOp, ops, lastOp, _ops.length - _lastOp); } else { // if we're truncating, copy the first (oldest) stamps into ops[0..] int endCount = Math.min(operations, _ops.length - _lastOp); System.arraycopy(_ops, _lastOp, ops, 0, endCount); System.arraycopy(_ops, 0, ops, endCount, operations - endCount); _lastOp = 0; } _ops = ops; } } /** * Registers an attempt at an operation and returns true if the operation should be throttled * (meaning N operations have already been performed in the last M seconds), or false if the * operation is allowed to be performed. * * @return true if the throttle is activated, false if the operation can proceed. */ public boolean throttleOp () { return throttleOp(System.currentTimeMillis()); } /** * Registers an attempt at an operation and returns true if the operation should be performed * or false if it should be throttled (meaning N operations have already been performed in the * last M seconds). For systems that don't wish to use {@link System#currentTimeMillis} (opting * in favor for some custom timing mechanism that is more accurate that {@link * System#currentTimeMillis}) or those that already have the time, they can avoid an * unnecessary call to {@link System#currentTimeMillis} by using this version of the method. * * @param timeStamp the timestamp at which this operation is being attempted. * * @return true if the throttle is activated, false if the operation can proceed. */ public boolean throttleOp (long timeStamp) { if (wouldThrottle(timeStamp)) { return true; } noteOp(timeStamp); return false; } /** * Check to see if we would throttle an operation occuring at the specified timestamp. * Typically used in conjunction with {@link #noteOp}. */ public boolean wouldThrottle (long timeStamp) { // if the oldest operation was performed less than _period ago, we need to throttle long elapsed = timeStamp - _ops[_lastOp]; // if negative time elapsed, we must be running on windows; let's just cope by not // throttling return (elapsed >= 0 && elapsed < _period); } /** * Note that an operation occurred at the specified timestamp. This method should be used with * {@link #wouldThrottle} to note an operation that has already been cleared to * occur. Typically this is used if there is another limiting factor besides the throttle that * determines whether the operation can occur. You are responsible for calling this method in a * safe and timely manner after using wouldThrottle. */ public void noteOp (long timeStamp) { // overwrite the oldest operation with the current time and move the oldest operation // pointer to the second oldest operation (which is now the oldest as we overwrote the // oldest) _ops[_lastOp] = timeStamp; _lastOp = (_lastOp + 1) % _ops.length; } /** * Returns the timestamp of the most recently recorded operation. */ public long getLatestOperation () { return _ops[(_lastOp + _ops.length - 1) % _ops.length]; } @Override // from Object public String toString () { long oldest = System.currentTimeMillis() - _ops[_lastOp]; return _ops.length + " ops per " + _period + "ms (oldest " + oldest + ")"; } /** * Used for testing. */ public static void main (String[] args) { // set up a throttle for 5 ops per 10 seconds Throttle throttle = new Throttle(5, 10000); // try doing one operation per second and we should hit the throttle on the sixth operation // and then kick in again on the eleventh, only to stop again on the fifteenth for (int i = 0; i < 20; i++) { System.out.println((i+1) + ". Throttle: " + throttle.throttleOp()); // pause for a sec try { Thread.sleep(1000L); } catch (InterruptedException ie) {} } } protected long[] _ops; protected int _lastOp; protected long _period; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy