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

io.quarkus.redis.runtime.datasource.ReactiveRedisDataSourceImpl Maven / Gradle / Ivy

There is a newer version: 3.17.5
Show newest version
package io.quarkus.redis.runtime.datasource;

import static io.quarkus.redis.runtime.datasource.Validation.notNullOrEmpty;
import static io.smallrye.mutiny.helpers.ParameterValidation.doesNotContainNull;
import static io.smallrye.mutiny.helpers.ParameterValidation.nonNull;
import static io.smallrye.mutiny.helpers.ParameterValidation.positiveOrZero;

import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;

import com.fasterxml.jackson.core.type.TypeReference;

import io.quarkus.redis.datasource.ReactiveRedisDataSource;
import io.quarkus.redis.datasource.autosuggest.ReactiveAutoSuggestCommands;
import io.quarkus.redis.datasource.bitmap.ReactiveBitMapCommands;
import io.quarkus.redis.datasource.bloom.ReactiveBloomCommands;
import io.quarkus.redis.datasource.countmin.ReactiveCountMinCommands;
import io.quarkus.redis.datasource.cuckoo.ReactiveCuckooCommands;
import io.quarkus.redis.datasource.geo.ReactiveGeoCommands;
import io.quarkus.redis.datasource.graph.ReactiveGraphCommands;
import io.quarkus.redis.datasource.hash.ReactiveHashCommands;
import io.quarkus.redis.datasource.hyperloglog.ReactiveHyperLogLogCommands;
import io.quarkus.redis.datasource.json.ReactiveJsonCommands;
import io.quarkus.redis.datasource.keys.ReactiveKeyCommands;
import io.quarkus.redis.datasource.list.ReactiveListCommands;
import io.quarkus.redis.datasource.pubsub.ReactivePubSubCommands;
import io.quarkus.redis.datasource.search.ReactiveSearchCommands;
import io.quarkus.redis.datasource.set.ReactiveSetCommands;
import io.quarkus.redis.datasource.sortedset.ReactiveSortedSetCommands;
import io.quarkus.redis.datasource.stream.ReactiveStreamCommands;
import io.quarkus.redis.datasource.string.ReactiveStringCommands;
import io.quarkus.redis.datasource.timeseries.ReactiveTimeSeriesCommands;
import io.quarkus.redis.datasource.topk.ReactiveTopKCommands;
import io.quarkus.redis.datasource.transactions.OptimisticLockingTransactionResult;
import io.quarkus.redis.datasource.transactions.ReactiveTransactionalRedisDataSource;
import io.quarkus.redis.datasource.transactions.TransactionResult;
import io.quarkus.redis.datasource.value.ReactiveValueCommands;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.redis.client.Command;
import io.vertx.mutiny.redis.client.Redis;
import io.vertx.mutiny.redis.client.RedisAPI;
import io.vertx.mutiny.redis.client.RedisConnection;
import io.vertx.mutiny.redis.client.Request;
import io.vertx.mutiny.redis.client.Response;

public class ReactiveRedisDataSourceImpl implements ReactiveRedisDataSource, RedisCommandExecutor {

    final Redis redis;
    final RedisConnection connection;
    private final Vertx vertx;

    public ReactiveRedisDataSourceImpl(Vertx vertx, Redis redis, RedisAPI api) {
        nonNull(redis, "redis");
        nonNull(api, "api");
        nonNull(vertx, "vertx");
        this.vertx = vertx;
        this.redis = redis;
        this.connection = null;
    }

    public ReactiveRedisDataSourceImpl(Vertx vertx, Redis redis, RedisConnection connection) {
        nonNull(redis, "redis");
        nonNull(connection, "connection");
        nonNull(vertx, "vertx");
        this.vertx = vertx;
        this.redis = redis;
        this.connection = connection;
    }

    @Override
    public Uni execute(Request request) {
        if (connection != null) {
            return connection.send(request);
        }
        return redis.send(request);
    }

    @Override
    public Uni withTransaction(Function> function) {
        nonNull(function, "function");
        return redis.connect()
                .onItem().transformToUni(connection -> {
                    ReactiveRedisDataSourceImpl singleConnectionDS = new ReactiveRedisDataSourceImpl(vertx, redis, connection);
                    TransactionHolder th = new TransactionHolder();
                    return connection.send(Request.cmd(Command.MULTI))
                            .chain(x -> function.apply(new ReactiveTransactionalRedisDataSourceImpl(singleConnectionDS, th)))
                            .chain(ignored -> {
                                if (!th.discarded()) {
                                    return connection.send(Request.cmd(Command.EXEC));
                                } else {
                                    return Uni.createFrom().nullItem();
                                }
                            })
                            .onTermination().call(connection::close)
                            .map(r -> toTransactionResult(r, th));
                });
    }

    @Override
    public Uni withTransaction(Function> function,
            String... keys) {
        nonNull(function, "function");
        notNullOrEmpty(keys, "keys");
        doesNotContainNull(keys, "keys");
        return redis.connect()
                .onItem().transformToUni(connection -> {
                    ReactiveRedisDataSourceImpl singleConnectionDS = new ReactiveRedisDataSourceImpl(vertx, redis, connection);
                    TransactionHolder th = new TransactionHolder();
                    return watch(connection, keys) // WATCH keys
                            .chain(() -> connection.send(Request.cmd(Command.MULTI))
                                    .chain(x -> function
                                            .apply(new ReactiveTransactionalRedisDataSourceImpl(singleConnectionDS, th)))
                                    .onItemOrFailure().transformToUni((x, failure) -> {
                                        if (!th.discarded() && failure == null) {
                                            return connection.send(Request.cmd(Command.EXEC));
                                        } else {
                                            if (!th.discarded()) {
                                                return connection.send(Request.cmd(Command.DISCARD));
                                            }
                                            return Uni.createFrom().nullItem();
                                        }
                                    })
                                    .onTermination().call(connection::close)
                                    .map(r -> toTransactionResult(r, th)));
                });
    }

    private Uni watch(RedisConnection connection, String... keys) {
        List watched = List.of(keys);
        Request request = Request.cmd(Command.WATCH);
        for (String s : watched) {
            request.arg(s);
        }
        return connection.send(request)
                .replaceWithVoid();
    }

    @Override
    public  Uni> withTransaction(Function> preTxBlock,
            BiFunction> tx, String... watchedKeys) {
        nonNull(tx, "tx");
        notNullOrEmpty(watchedKeys, "watchedKeys");
        doesNotContainNull(watchedKeys, "watchedKeys");
        nonNull(preTxBlock, "preTxBlock");

        return redis.connect()
                .onItem().transformToUni(connection -> {
                    ReactiveRedisDataSourceImpl singleConnectionDS = new ReactiveRedisDataSourceImpl(vertx, redis, connection);
                    TransactionHolder th = new TransactionHolder();
                    return watch(connection, watchedKeys) // WATCH keys
                            .chain(x -> preTxBlock.apply(new ReactiveRedisDataSourceImpl(vertx, redis, connection)))// Execute the pre-tx-block
                            .chain(input -> connection.send(Request.cmd(Command.MULTI))
                                    .chain(x -> tx
                                            .apply(input, new ReactiveTransactionalRedisDataSourceImpl(singleConnectionDS, th)))
                                    .onItemOrFailure().transformToUni((x, failure) -> {
                                        if (!th.discarded() && failure == null) {
                                            return connection.send(Request.cmd(Command.EXEC));
                                        } else {
                                            if (!th.discarded()) {
                                                return connection.send(Request.cmd(Command.DISCARD))
                                                        .replaceWithNull();
                                            }
                                            return Uni.createFrom().nullItem();
                                        }
                                    })
                                    .onTermination().call(connection::close)
                                    .map(r -> toTransactionResult(r, input, th)));
                });
    }

    public static TransactionResult toTransactionResult(Response response, TransactionHolder th) {
        if (response == null) {
            // Discarded
            return TransactionResultImpl.DISCARDED;
        }
        return new TransactionResultImpl(th.discarded(), th.map(response));
    }

    public static  OptimisticLockingTransactionResult toTransactionResult(Response response, I input,
            TransactionHolder th) {
        if (response == null) {
            // Discarded
            return OptimisticLockingTransactionResultImpl.discarded(input);
        }
        return new OptimisticLockingTransactionResultImpl<>(th.discarded(), input, th.map(response));
    }

    @Override
    public Uni execute(String command, String... args) {
        nonNull(command, "command");
        return execute(CommandMap.normalize(Command.create(command)), args);
    }

    @Override
    public Uni execute(Command command, String... args) {
        nonNull(command, "command");
        command = CommandMap.normalize(command);
        Request request = Request.cmd(command);
        for (String arg : args) {
            request.arg(arg);
        }
        return execute(request);
    }

    @Override
    public Uni execute(io.vertx.redis.client.Command command, String... args) {
        nonNull(command, "command");
        command = CommandMap.normalize(command);
        Request request = Request.newInstance(io.vertx.redis.client.Request.cmd(command));
        for (String arg : args) {
            request.arg(arg);
        }
        return execute(request);
    }

    @Override
    public Uni withConnection(Function> function) {
        if (connection != null) {
            // We are already on a connection, keep this one
            return function.apply(this);
        }
        return redis.connect()
                .onItem().transformToUni(connection -> {
                    ReactiveRedisDataSourceImpl singleConnectionDS = new ReactiveRedisDataSourceImpl(vertx, redis, connection);
                    return function.apply(singleConnectionDS)
                            .onTermination().call(connection::close);
                });
    }

    @Override
    public Uni select(long index) {
        positiveOrZero(index, "index");
        return execute(Request.cmd(Command.SELECT).arg(index)).replaceWithVoid();
    }

    @Override
    public Uni flushall() {
        return execute(Request.cmd(Command.FLUSHALL)).replaceWithVoid();
    }

    @Override
    public  ReactiveHashCommands hash(Class redisKeyType, Class fieldType, Class valueType) {
        return new ReactiveHashCommandsImpl<>(this, redisKeyType, fieldType, valueType);
    }

    @Override
    public  ReactiveHashCommands hash(TypeReference redisKeyType, TypeReference fieldType,
            TypeReference valueType) {
        return new ReactiveHashCommandsImpl<>(this, redisKeyType.getType(), fieldType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveGeoCommands geo(Class redisKeyType, Class memberType) {
        return new ReactiveGeoCommandsImpl<>(this, redisKeyType, memberType);
    }

    @Override
    public  ReactiveGeoCommands geo(TypeReference redisKeyType, TypeReference memberType) {
        return new ReactiveGeoCommandsImpl<>(this, redisKeyType.getType(), memberType.getType());
    }

    @Override
    public  ReactiveKeyCommands key(Class redisKeyType) {
        return new ReactiveKeyCommandsImpl<>(this, redisKeyType);
    }

    @Override
    public  ReactiveKeyCommands key(TypeReference redisKeyType) {
        return new ReactiveKeyCommandsImpl<>(this, redisKeyType.getType());
    }

    @Override
    public  ReactiveSortedSetCommands sortedSet(Class redisKeyType, Class valueType) {
        return new ReactiveSortedSetCommandsImpl<>(this, redisKeyType, valueType);
    }

    @Override
    public  ReactiveSortedSetCommands sortedSet(TypeReference redisKeyType, TypeReference valueType) {
        return new ReactiveSortedSetCommandsImpl<>(this, redisKeyType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveStringCommands string(Class redisKeyType, Class valueType) {
        return new ReactiveStringCommandsImpl<>(this, redisKeyType, valueType);
    }

    @Override
    public  ReactiveValueCommands value(Class redisKeyType, Class valueType) {
        return new ReactiveStringCommandsImpl<>(this, redisKeyType, valueType);
    }

    @Override
    public  ReactiveValueCommands value(TypeReference redisKeyType, TypeReference valueType) {
        return new ReactiveStringCommandsImpl<>(this, redisKeyType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveSetCommands set(Class redisKeyType, Class memberType) {
        return new ReactiveSetCommandsImpl<>(this, redisKeyType, memberType);
    }

    @Override
    public  ReactiveSetCommands set(TypeReference redisKeyType, TypeReference memberType) {
        return new ReactiveSetCommandsImpl<>(this, redisKeyType.getType(), memberType.getType());
    }

    @Override
    public  ReactiveListCommands list(Class redisKeyType, Class memberType) {
        return new ReactiveListCommandsImpl<>(this, redisKeyType, memberType);
    }

    @Override
    public  ReactiveListCommands list(TypeReference redisKeyType, TypeReference memberType) {
        return new ReactiveListCommandsImpl<>(this, redisKeyType.getType(), memberType.getType());
    }

    @Override
    public  ReactiveHyperLogLogCommands hyperloglog(Class redisKeyType, Class memberType) {
        return new ReactiveHyperLogLogCommandsImpl<>(this, redisKeyType, memberType);
    }

    @Override
    public  ReactiveHyperLogLogCommands hyperloglog(TypeReference redisKeyType, TypeReference memberType) {
        return new ReactiveHyperLogLogCommandsImpl<>(this, redisKeyType.getType(), memberType.getType());
    }

    @Override
    public  ReactiveBitMapCommands bitmap(Class redisKeyType) {
        return new ReactiveBitMapCommandsImpl<>(this, redisKeyType);
    }

    @Override
    public  ReactiveBitMapCommands bitmap(TypeReference redisKeyType) {
        return new ReactiveBitMapCommandsImpl<>(this, redisKeyType.getType());
    }

    @Override
    public  ReactiveStreamCommands stream(Class redisKeyType, Class fieldType, Class valueType) {
        return new ReactiveStreamCommandsImpl<>(this, redisKeyType, fieldType, valueType);
    }

    @Override
    public  ReactiveStreamCommands stream(TypeReference redisKeyType, TypeReference fieldType,
            TypeReference valueType) {
        return new ReactiveStreamCommandsImpl<>(this, redisKeyType.getType(), fieldType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveJsonCommands json(Class redisKeyType) {
        return new ReactiveJsonCommandsImpl<>(this, redisKeyType);
    }

    @Override
    public  ReactiveJsonCommands json(TypeReference redisKeyType) {
        return new ReactiveJsonCommandsImpl<>(this, redisKeyType.getType());
    }

    @Override
    public  ReactiveBloomCommands bloom(Class redisKeyType, Class valueType) {
        return new ReactiveBloomCommandsImpl<>(this, redisKeyType, valueType);
    }

    @Override
    public  ReactiveBloomCommands bloom(TypeReference redisKeyType, TypeReference valueType) {
        return new ReactiveBloomCommandsImpl<>(this, redisKeyType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveCuckooCommands cuckoo(Class redisKeyType, Class valueType) {
        return new ReactiveCuckooCommandsImpl<>(this, redisKeyType, valueType);
    }

    @Override
    public  ReactiveCuckooCommands cuckoo(TypeReference redisKeyType, TypeReference valueType) {
        return new ReactiveCuckooCommandsImpl<>(this, redisKeyType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveCountMinCommands countmin(Class redisKeyType, Class valueType) {
        return new ReactiveCountMinCommandsImpl<>(this, redisKeyType, valueType);
    }

    @Override
    public  ReactiveCountMinCommands countmin(TypeReference redisKeyType, TypeReference valueType) {
        return new ReactiveCountMinCommandsImpl<>(this, redisKeyType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveTopKCommands topk(Class redisKeyType, Class valueType) {
        return new ReactiveTopKCommandsImpl<>(this, redisKeyType, valueType);
    }

    @Override
    public  ReactiveTopKCommands topk(TypeReference redisKeyType, TypeReference valueType) {
        return new ReactiveTopKCommandsImpl<>(this, redisKeyType.getType(), valueType.getType());
    }

    @Override
    public  ReactiveGraphCommands graph(Class redisKeyType) {
        return new ReactiveGraphCommandsImpl<>(this, redisKeyType);
    }

    @Override
    public  ReactivePubSubCommands pubsub(Class messageType) {
        return new ReactivePubSubCommandsImpl<>(this, messageType);
    }

    @Override
    public  ReactivePubSubCommands pubsub(TypeReference messageType) {
        return new ReactivePubSubCommandsImpl<>(this, messageType.getType());
    }

    @Override
    public  ReactiveSearchCommands search(Class redisKeyType) {
        return new ReactiveSearchCommandsImpl<>(this, redisKeyType);
    }

    @Override
    public  ReactiveAutoSuggestCommands autosuggest(Class redisKeyType) {
        return new ReactiveAutoSuggestCommandsImpl<>(this, redisKeyType);
    }

    @Override
    public  ReactiveAutoSuggestCommands autosuggest(TypeReference redisKeyType) {
        return new ReactiveAutoSuggestCommandsImpl<>(this, redisKeyType.getType());
    }

    @Override
    public  ReactiveTimeSeriesCommands timeseries(Class redisKeyType) {
        return new ReactiveTimeSeriesCommandsImpl<>(this, redisKeyType);
    }

    @Override
    public  ReactiveTimeSeriesCommands timeseries(TypeReference redisKeyType) {
        return new ReactiveTimeSeriesCommandsImpl<>(this, redisKeyType.getType());
    }

    @Override
    public Redis getRedis() {
        return redis;
    }

    public Vertx getVertx() {
        return vertx;
    }
}