
org.swisspush.redisques.handler.GetQueuesItemsCountHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisques Show documentation
Show all versions of redisques Show documentation
A highly scalable redis-persistent queuing system for vertx
package org.swisspush.redisques.handler;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.redis.client.Command;
import io.vertx.redis.client.RedisAPI;
import io.vertx.redis.client.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.swisspush.redisques.performance.UpperBoundParallel;
import org.swisspush.redisques.util.HandlerUtil;
import org.swisspush.redisques.util.RedisProvider;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Semaphore;
import java.util.function.BiConsumer;
import java.util.regex.Pattern;
import org.swisspush.redisques.exception.RedisQuesExceptionFactory;
import static java.lang.System.currentTimeMillis;
import static org.swisspush.redisques.util.RedisquesAPI.ERROR;
import static org.swisspush.redisques.util.RedisquesAPI.MONITOR_QUEUE_NAME;
import static org.swisspush.redisques.util.RedisquesAPI.MONITOR_QUEUE_SIZE;
import static org.swisspush.redisques.util.RedisquesAPI.OK;
import static org.swisspush.redisques.util.RedisquesAPI.QUEUES;
import static org.swisspush.redisques.util.RedisquesAPI.STATUS;
public class GetQueuesItemsCountHandler implements Handler> {
private final Logger log = LoggerFactory.getLogger(GetQueuesItemsCountHandler.class);
private final Vertx vertx;
private final Message event;
private final Optional filterPattern;
private final String queuesPrefix;
private final RedisProvider redisProvider;
private final UpperBoundParallel upperBoundParallel;
private final RedisQuesExceptionFactory exceptionFactory;
private final Semaphore redisRequestQuota;
public GetQueuesItemsCountHandler(
Vertx vertx,
Message event,
Optional filterPattern,
String queuesPrefix,
RedisProvider redisProvider,
RedisQuesExceptionFactory exceptionFactory,
Semaphore redisRequestQuota
) {
this.vertx = vertx;
this.event = event;
this.filterPattern = filterPattern;
this.queuesPrefix = queuesPrefix;
this.redisProvider = redisProvider;
this.upperBoundParallel = new UpperBoundParallel(vertx, exceptionFactory);
this.exceptionFactory = exceptionFactory;
this.redisRequestQuota = redisRequestQuota;
}
@Override
public void handle(AsyncResult handleQueues) {
if (!handleQueues.succeeded()) {
log.warn("Concealed error", exceptionFactory.newException(handleQueues.cause()));
event.reply(new JsonObject().put(STATUS, ERROR));
return;
}
var ctx = new Object(){
RedisAPI redis;
Iterator iter;
List queues = HandlerUtil.filterByPattern(handleQueues.result(), filterPattern);
int iNumberResult;
int[] queueLengths;
};
if (ctx.queues.isEmpty()) {
log.debug("Queue count evaluation with empty queues");
event.reply(new JsonObject().put(STATUS, OK).put(QUEUES, new JsonArray()));
return;
}
if (redisRequestQuota.availablePermits() <= 0) {
event.reply(exceptionFactory.newReplyException(429,
"Too many simultaneous '" + GetQueuesItemsCountHandler.class.getSimpleName() + "' requests in progress", null));
return;
}
redisProvider.redis().compose((RedisAPI redis_) -> {
ctx.redis = redis_;
ctx.queueLengths = new int[ctx.queues.size()];
ctx.iter = ctx.queues.iterator();
var p = Promise.promise();
upperBoundParallel.request(redisRequestQuota, null, new UpperBoundParallel.Mentor() {
@Override public boolean runOneMore(BiConsumer onLLenDone, Void unused) {
if (ctx.iter.hasNext()) {
String queue = ctx.iter.next();
int iNum = ctx.iNumberResult++;
ctx.redis.send(Command.LLEN, queuesPrefix + queue).onSuccess((Response rsp) -> {
ctx.queueLengths[iNum] = rsp.toInteger();
onLLenDone.accept(null, null);
}).onFailure((Throwable ex) -> onLLenDone.accept(ex, null));
}
return ctx.iter.hasNext();
}
@Override public boolean onError(Throwable ex, Void ctx_) {
log.error("Unexpected queue length result", exceptionFactory.newException(ex));
event.reply(new JsonObject().put(STATUS, ERROR));
return false;
}
@Override public void onDone(Void ctx_) {
p.complete();
}
});
return p.future();
}).compose((Void v) -> {
/*going to waste another threads time to produce those garbage objects*/
return vertx.executeBlocking((Promise workerPromise) -> {
assert !Thread.currentThread().getName().toUpperCase().contains("EVENTLOOP");
long beginEpchMs = currentTimeMillis();
JsonArray result = new JsonArray();
for (int i = 0; i < ctx.queueLengths.length; ++i) {
String queueName = ctx.queues.get(i);
result.add(new JsonObject()
.put(MONITOR_QUEUE_NAME, queueName)
.put(MONITOR_QUEUE_SIZE, ctx.queueLengths[i]));
}
var obj = new JsonObject().put(STATUS, OK).put(QUEUES, result);
long jsonCreateDurationMs = currentTimeMillis() - beginEpchMs;
if (jsonCreateDurationMs > 10) {
log.info("Creating JSON with {} entries did block this thread for {}ms",
ctx.queueLengths.length, jsonCreateDurationMs);
}else{
log.debug("Creating JSON with {} entries did block this thread for {}ms",
ctx.queueLengths.length, jsonCreateDurationMs);
}
workerPromise.complete(obj);
}, false);
}).onSuccess((JsonObject json) -> {
log.trace("call event.reply(json)");
event.reply(json);
}).onFailure((Throwable ex) -> {
log.warn("Redis: Failed to get queue length.", exceptionFactory.newException(ex));
event.reply(new JsonObject().put(STATUS, ERROR));
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy