package cz.cuni.mff.d3s.been.objectrepository; import cz.cuni.mff.d3s.been.persistence.SuccessAction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A lingering drain thread (doesn't die once drainable objects run out) * * @param <T> */ public class LingeringConsumer<T> extends Consumer<T> { private static final Logger log = LoggerFactory.getLogger(LingeringConsumer.class); protected final Take<T> take; protected final Float failRateThreshold; protected final Long suspendTimeOnHighFailRate; LingeringConsumer(Take<T> take, SuccessAction<T> successAction, FailAction<T> failAction, FailRate failRateMonitor, Float failRateThreshold, Long suspendTimeOnHighFailRate) { super(successAction, failAction, failRateMonitor); this.take = take; this.failRateThreshold = failRateThreshold; this.suspendTimeOnHighFailRate = suspendTimeOnHighFailRate; } @Override public void run() { try { innerRun(); } catch (Throwable e) { log.error(e.getMessage(), e); } } private void innerRun() { log.debug("Thread starting."); while (!Thread.currentThread().isInterrupted()) { try { final T item = take.perform(); if (item == null) { // The documentation doesn't say this, but Hazelcast queue yields null // rather than throwing an exception when interrupted // This throw is not entirely correct, as internally, Hazelcast // only uses a big timeout for take(). However, the timeout being // Long.MAX_VALUE, this implementation is unlikely to return null in any // other circumstances than during interruption. throw new InterruptedException("Queue take yielded null item."); } if (!act(item) && (failRateMonitor.getFailRate() >= failRateThreshold)) { Thread.sleep(suspendTimeOnHighFailRate); } } catch (InterruptedException e) { // the take has been interrupted - a signal that this objectrepository is being terminated log.warn("Lingering queue consumer interrupted."); Thread.currentThread().interrupt(); } } log.debug("Thread terminating."); } }