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

bboss.org.jgroups.stack.RangeBasedRetransmitter Maven / Gradle / Ivy

The newest version!

package bboss.org.jgroups.stack;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import bboss.org.jgroups.Address;
import bboss.org.jgroups.util.Range;
import bboss.org.jgroups.util.Seqno;
import bboss.org.jgroups.util.SeqnoComparator;
import bboss.org.jgroups.util.SeqnoRange;
import bboss.org.jgroups.util.TimeScheduler;


/**
 * This retransmitter is specialized in maintaining ranges of seqnos, e.g. [3-20, [89-89], [100-120].
 * The ranges are stored in a sorted hashmap and the {@link Comparable#compareTo(Object)} method compares both ranges
 * again ranges, and ranges against seqnos. The latter helps to find a range given a seqno, e.g. seqno 105 will find
 * range [100-120].

* Each range is implemented by {@link bboss.org.jgroups.util.SeqnoRange}, which has a bitset of all missing seqnos. When * a seqno is received, that bit set is updated; the bit corresponding to the seqno is set to 1. A task linked to * the range periodically retransmits missing messages.

* When all bits are 1 (= all messages have been received), the range is removed from the hashmap and the retransmission * task is cancelled. * * @author Bela Ban * @version $Id: RangeBasedRetransmitter.java,v 1.6 2009/11/30 12:36:52 belaban Exp $ */ public class RangeBasedRetransmitter extends Retransmitter { // todo: when JDK 6 is the baseline, convert the TreeMap to a TreeSet or ConcurrentSkipListSet and use ceiling() /** Sorted hashmap storing the ranges */ private final Map ranges=Collections.synchronizedSortedMap(new TreeMap(new SeqnoComparator())); /** Association between ranges and retransmission tasks */ private final Map tasks=new ConcurrentHashMap(); private final AtomicLong num_missing_seqnos=new AtomicLong(0); private final AtomicLong num_ranges=new AtomicLong(0); private final AtomicLong num_single_msgs=new AtomicLong(0); /** * Create a new Retransmitter associated with the given sender address * @param sender the address from which retransmissions are expected or to which retransmissions are sent * @param cmd the retransmission callback reference * @param sched retransmissions scheduler */ public RangeBasedRetransmitter(Address sender, RetransmitCommand cmd, TimeScheduler sched) { super(sender, cmd, sched); } /** * Add the given range [first_seqno, last_seqno] in the list of * entries eligible for retransmission. If first_seqno > last_seqno, * then the range [last_seqno, first_seqno] is added instead */ public void add(long first_seqno, long last_seqno) { if(first_seqno > last_seqno) { long tmp=first_seqno; first_seqno=last_seqno; last_seqno=tmp; } num_missing_seqnos.addAndGet(last_seqno - first_seqno +1); // create a single seqno if we have no range or else a SeqnoRange Seqno range=first_seqno == last_seqno? new Seqno(first_seqno) : new SeqnoRange(first_seqno, last_seqno); if(range instanceof SeqnoRange) num_ranges.incrementAndGet(); else num_single_msgs.incrementAndGet(); // each task needs its own retransmission interval, as they are stateful *and* mutable, so we *need* to copy ! RangeTask new_task=new RangeTask(range, RETRANSMIT_TIMEOUTS.copy(), cmd, sender); Seqno old_range=ranges.put(range, range); if(old_range != null) log.error("new range " + range + " overlaps with old range " + old_range); tasks.put(range, new_task); new_task.doSchedule(); // Entry adds itself to the timer if(log.isTraceEnabled()) log.trace("added range " + sender + " [" + range + "]"); } /** * Remove the given sequence number from the list of seqnos eligible * for retransmission. If there are no more seqno intervals in the * respective entry, cancel the entry from the retransmission * scheduler and remove it from the pending entries */ public int remove(long seqno) { int retval=0; Seqno range=ranges.get(new Seqno(seqno, true)); if(range == null) return 0; range.set(seqno); if(log.isTraceEnabled()) log.trace("removed " + sender + " #" + seqno + " from retransmitter"); // if the range has no missing messages, get the associated task and cancel it if(range.getNumberOfMissingMessages() == 0) { Task task=tasks.remove(range); if(task != null) { task.cancel(); retval=task.getNumRetransmits(); } else log.error("task for range " + range + " not found"); ranges.remove(range); if(log.isTraceEnabled()) log.trace("all messages for " + sender + " [" + range + "] have been received; removing range"); } return retval; } /** * Reset the retransmitter: clear all msgs and cancel all the * respective tasks */ public void reset() { synchronized(ranges) { for(Seqno range: ranges.keySet()) { // get task associated with range and cancel it Task task=tasks.get(range); if(task != null) { task.cancel(); tasks.remove(range); } } ranges.clear(); } for(Task task: tasks.values()) task.cancel(); num_missing_seqnos.set(0); num_ranges.set(0); num_single_msgs.set(0); } public String toString() { int missing_msgs=0; synchronized(ranges) { for(Seqno range: ranges.keySet()) { missing_msgs+=range.getNumberOfMissingMessages(); } } StringBuilder sb=new StringBuilder(); sb.append(missing_msgs).append(" messages to retransmit"); if(missing_msgs < 50) { Collection all_missing_msgs=new LinkedList(); for(Seqno range: ranges.keySet()) { all_missing_msgs.addAll(range.getMessagesToRetransmit()); } sb.append(": ").append(all_missing_msgs); } return sb.toString(); } public int size() { int retval=0; synchronized(ranges) { for(Seqno range: ranges.keySet()) { retval+=range.getNumberOfMissingMessages(); } } return retval; } public String printStats() { StringBuilder sb=new StringBuilder(); sb.append("total seqnos=" + num_missing_seqnos); sb.append(", single seqnos=" + num_single_msgs); sb.append(", ranges=" + num_ranges); double avg_seqnos_per_range=(double)(num_missing_seqnos.get() - num_single_msgs.get()) / num_ranges.get(); sb.append(", seqnos / range: " + avg_seqnos_per_range); return sb.toString(); } protected class RangeTask extends Task { protected final Seqno range; protected RangeTask(Seqno range, Interval intervals, RetransmitCommand cmd, Address msg_sender) { super(intervals, cmd, msg_sender); this.range=range; } public String toString() { return range.toString(); } protected void callRetransmissionCommand() { Collection missing=range.getMessagesToRetransmit(); if(missing.isEmpty()) { cancel(); } else { for(Range range: missing) { command.retransmit(range.low, range.high, msg_sender); } } } } /* ------------------------------- Private Methods -------------------------------------- */ /* ---------------------------- End of Private Methods ------------------------------------ */ }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy