org.jgroups.protocols.RED Maven / Gradle / Ivy
package org.jgroups.protocols;
import org.jgroups.Message;
import org.jgroups.annotations.MBean;
import org.jgroups.annotations.ManagedAttribute;
import org.jgroups.annotations.Property;
import org.jgroups.stack.Protocol;
import org.jgroups.util.Util;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Implementation of Random Early Drop: messages are discarded when the bundler's queue in the transport nears exhaustion.
* See Floyd and Van Jacobsen's paper for details.
* @author Bela Ban
* @since 5.0.0, 4.2.2
*/
@MBean(description="Implementation of Random Early Drop: messages are discarded when the bundler's queue in " +
"the transport nears exhaustion")
public class RED extends Protocol {
@Property(description="If false, all messages are passed down. Will be set to false if the bundler " +
"returns a queue size of -1")
protected boolean enabled=true;
@ManagedAttribute(description="The capacity of the queue (assumed to be constant)")
protected int queue_capacity;
@Property(description="The min threshold (percentage between 0 and 1.0) below which no message is dropped")
protected double min_threshold=0.5;
protected long min;
@Property(description="The max threshold (percentage between min_threshold and 1.0) above which all messages are dropped")
protected double max_threshold=1.0;
protected long max;
@ManagedAttribute(description="The average number of elements in the bundler's queue. Computed as " +
"o * (1 - 2^-wf) + c * (2^-wf) where o is the old average, c the current queue size amd wf the weight_factor")
protected double avg_queue_size;
@Property(description="The weight used to compute the average queue size. The higher the value is, the less the " +
"current queue size is taken into account. E.g. with 2, 25% of the current queue size and 75% of the old " +
"average is taken to compute the new average. In other words: with a high value, the average will take " +
"longer to reflect the current queueu size.")
protected double weight_factor=2;
protected final LongAdder dropped_msgs=new LongAdder(); // dropped messages
protected final LongAdder total_msgs=new LongAdder(); // total messages looked at
protected Bundler bundler;
protected final Lock lock=new ReentrantLock();
protected long span=max-min; // diff between max and min
protected double weight=Math.pow(2, -weight_factor);
public boolean isEnabled() {return enabled;}
public RED setEnabled(boolean e) {enabled=e; return this;}
public double getMinThreshold() {return min_threshold;}
@ManagedAttribute(description="The number of dropped messages")
public long getDroppedMessages() {return dropped_msgs.sum();}
@ManagedAttribute(description="Total number of messages processed")
public long getTotalMessages() {return total_msgs.sum();}
@ManagedAttribute(description="Percentage of all messages that were dropped")
public double getDropRate() {return dropped_msgs.sum() / (double)total_msgs.sum();}
public void start() throws Exception {
super.start();
bundler=getTransport().getBundler();
enabled=bundler != null && bundler.getQueueSize() >= 0;
if(enabled) {
queue_capacity=getTransport().getBundlerCapacity();
min=(long)(queue_capacity * checkRange(min_threshold, 0, 1, "min_threshold"));
max=(long)(queue_capacity * checkRange(max_threshold, 0, 1, "max_threshold"));
span=max-min;
weight=Math.pow(2, -weight_factor);
}
}
public void resetStats() {
super.resetStats();
avg_queue_size=0;
dropped_msgs.reset();
total_msgs.reset();
}
public Object down(Message msg) {
if(enabled) {
int current_queue_size=bundler.getQueueSize();
double avg;
lock.lock();
try {
avg=avg_queue_size=computeAverage(avg_queue_size, current_queue_size);
}
finally {
lock.unlock();
}
total_msgs.increment();
// don't drop if avg <= min, drop if avg >= max, and drop with a probability p if avg is between min and max
boolean drop=!(avg <= min) && (avg >= max || drop(avg));
if(drop) {
dropped_msgs.increment();
return null;
}
}
return down_prot.down(msg);
}
public String toString() {
return String.format("enabled=%b, queue capacity=%d, min=%d, max=%d, avg-queue-size=%.2f, " +
"total=%d dropped=%d (%d%%)", enabled, queue_capacity, min, max, avg_queue_size,
total_msgs.sum(), dropped_msgs.sum(), (int)(getDropRate()*100.0));
}
protected double computeAverage(double old_avg, int new_queue_size) {
return old_avg * (1 - weight) + new_queue_size * weight;
}
/** Computes a probability P with which the message should get dropped. min_threshold < avg < max_threshold.
* Probability increases linearly with min moving toward max */
protected double computeDropProbability(double avg) {
return Math.min(1, (avg-min) / span);
}
protected boolean drop(double avg) {
// message will be dropped with probability p
double p=computeDropProbability(avg);
return Util.tossWeightedCoin(p); // returns true if message should be dropped, false otherwise
}
protected static double checkRange(double val, double min, double max, String name) {
if(val < min || val > max)
throw new IllegalArgumentException(String.format("%s (%.2f) needs to be in range [%.2f..%.2f]", name, val, min, max));
return val;
}
/* public static void main(String[] args) {
RED red=new RED();
for(int i=0; i <= 1030; i++) {
double p=red.computeDropProbability(i);
System.out.printf("i=%d, drop-p=%.2f\n", i, p);
}
}*/
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy