package api.routing.metrics import java.util.Comparator import java.util.concurrent.ConcurrentSkipListSet import java.util.concurrent.atomic.AtomicLong /** * This buffer is intended to restrict size of metrics and avoid OutOfMemory * Created by dkondratiuk on 6/7/15. */ final class ConcurrentRingSet[K](val maxSize: Int)(val onRemove: K => Unit) extends Iterable[K] { @volatile private var isActive = true private lazy val set = new ConcurrentSkipListSet[PositionedKey](new Comparator[PositionedKey] { override def compare(o1: PositionedKey, o2: PositionedKey): Int = if (o1.key == o2.key) 0 else if (o1.position > o2.position) 1 else -1 }) private case class PositionedKey(position: Long, key: K) private val count = new AtomicLong(0) def put(k: K): Boolean = if (isActive) { // $COVERAGE-OFF$ if (count.get() == Long.MaxValue + 1) { set.clear(); count.set(0) } //Just in case... // $COVERAGE-ON$ val index = count.incrementAndGet() while (set.size >= maxSize) Option(set.pollFirst()).map(_.key).foreach(onRemove(_)) set.add(PositionedKey(index, k)) } else false def close() = { isActive = false this } import scala.collection.JavaConverters._ override def iterator: Iterator[K] = set.descendingIterator().asScala.map(_.key) }