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

io.scalecube.cluster.gossip.SequenceIdCollector Maven / Gradle / Ivy

package io.scalecube.cluster.gossip;

import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.stream.Collectors;

/**
 * A collector that efficiently store sequence of incremented numbers which go in a row without
 * missed elements.
 */
public class SequenceIdCollector {

  // store closed intervals [a, b] without intersections
  // if there is an intersection between tow intervals then they will be merged into one
  private final TreeMap processedInterval = new TreeMap<>();

  // [a, b]
  private static boolean isInClosedRange(Entry range, long element) {
    return range != null && range.getKey() <= element && element <= range.getValue();
  }

  private static boolean isNextToClosedRange(Entry range, long element) {
    return range != null && (element + 1 == range.getKey() || element - 1 == range.getValue());
  }

  /**
   * Returns true if this set contains the specified element.
   *
   * @param sequenceId element whose presence in this set is to be tested
   * @return true if this set contains the specified element
   */
  public boolean contains(long sequenceId) {
    // floor: returns the entry for the greatest key less than or equal to the specified key
    return isInClosedRange(processedInterval.floorEntry(sequenceId), sequenceId);
  }

  /**
   * Adds the specified element to this holder if it is not already present.
   *
   * @param sequenceId element to be added
   * @return true if this holder did not already contain the specified element
   */
  public boolean add(long sequenceId) {
    // floor: returns the entry for the greatest key less than or equal to the specified key
    final Entry floorEntry = processedInterval.floorEntry(sequenceId);

    if (isInClosedRange(floorEntry, sequenceId)) {
      return false;
    }

    // ceiling: returns the entry for the least key greater than or equal to the specified key
    final Entry ceilingEntry = processedInterval.ceilingEntry(sequenceId);

    final boolean nextToFloor = isNextToClosedRange(floorEntry, sequenceId);
    final boolean nextToCeiling = isNextToClosedRange(ceilingEntry, sequenceId);

    if (nextToFloor && nextToCeiling) {
      processedInterval.remove(floorEntry.getKey());
      processedInterval.remove(ceilingEntry.getKey());
      processedInterval.put(floorEntry.getKey(), ceilingEntry.getValue());
    } else if (nextToFloor) {
      processedInterval.remove(floorEntry.getKey());
      processedInterval.put(floorEntry.getKey(), sequenceId);
    } else if (nextToCeiling) {
      processedInterval.remove(ceilingEntry.getKey());
      processedInterval.put(sequenceId, ceilingEntry.getValue());
    } else {
      processedInterval.put(sequenceId, sequenceId);
    }

    return true;
  }

  /**
   * Returns the number of intervals in this collector.
   *
   * @return the number of intervals in this collector
   */
  public int size() {
    return processedInterval.size();
  }

  /** Removes all of the elements from this collector. */
  public void clear() {
    processedInterval.clear();
  }

  @Override
  public String toString() {
    return processedInterval.entrySet().stream()
        .map(entry -> "[" + entry.getKey() + "," + entry.getValue() + "]")
        .collect(Collectors.joining(",", "{", "}"));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy