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

org.swisspush.redisques.handler.GetQueuesItemsCountHandler Maven / Gradle / Ivy

There is a newer version: 4.1.11
Show newest version
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