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

org.redisson.command.RedisQueuedBatchExecutor Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

There is a newer version: 3.43.0
Show newest version
/**
 * Copyright (c) 2013-2020 Nikita Koksharov
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.redisson.command;

import org.redisson.api.BatchOptions;
import org.redisson.api.BatchOptions.ExecutionMode;
import org.redisson.api.RFuture;
import org.redisson.client.RedisConnection;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.*;
import org.redisson.command.CommandBatchService.ConnectionEntry;
import org.redisson.command.CommandBatchService.Entry;
import org.redisson.connection.ConnectionManager;
import org.redisson.connection.MasterSlaveEntry;
import org.redisson.connection.NodeSource;
import org.redisson.connection.NodeSource.Redirect;
import org.redisson.liveobject.core.RedissonObjectBuilder;
import org.redisson.misc.AsyncCountDownLatch;
import org.redisson.misc.LogHelper;
import org.redisson.misc.RPromise;
import org.redisson.misc.RedissonPromise;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 * @author Nikita Koksharov
 *
 * @param  type of value
 * @param  type of returned value
 */
public class RedisQueuedBatchExecutor extends BaseRedisBatchExecutor {

    private final ConcurrentMap connections;
    private final AsyncCountDownLatch latch;
    
    @SuppressWarnings("ParameterNumber")
    public RedisQueuedBatchExecutor(boolean readOnlyMode, NodeSource source, Codec codec, RedisCommand command,
            Object[] params, RPromise mainPromise, boolean ignoreRedirect, ConnectionManager connectionManager,
            RedissonObjectBuilder objectBuilder, ConcurrentMap commands,
            ConcurrentMap connections, BatchOptions options, AtomicInteger index,
            AtomicBoolean executed, AsyncCountDownLatch latch) {
        super(readOnlyMode, source, codec, command, params, mainPromise, ignoreRedirect, connectionManager, objectBuilder,
                commands, options, index, executed);
        
        this.connections = connections;
        this.latch = latch;
    }
    
    @Override
    public void execute() {
        addBatchCommandData(null);
        
        if (!readOnlyMode && this.options.getExecutionMode() == ExecutionMode.REDIS_READ_ATOMIC) {
            throw new IllegalStateException("Data modification commands can't be used with queueStore=REDIS_READ_ATOMIC");
        }

        super.execute();
    }

    
    @Override
    protected void releaseConnection(RPromise attemptPromise, RFuture connectionFuture) {
        if (RedisCommands.EXEC.getName().equals(command.getName())) {
            super.releaseConnection(attemptPromise, connectionFuture);
        }
        if (RedisCommands.DISCARD.getName().equals(command.getName())) {
            super.releaseConnection(attemptPromise, connectionFuture);
        }
    }
    
    @Override
    protected void handleSuccess(RPromise promise, RFuture connectionFuture, R res)
            throws ReflectiveOperationException {
        if (RedisCommands.EXEC.getName().equals(command.getName())) {
            super.handleSuccess(promise, connectionFuture, res);
            return;
        }
        if (RedisCommands.DISCARD.getName().equals(command.getName())) {
            super.handleSuccess(promise, connectionFuture, null);
            if (executed.compareAndSet(false, true)) {
                connectionFuture.getNow().forceFastReconnectAsync().onComplete((r, e) -> {
                    releaseConnection(promise, connectionFuture);
                });
            }
            return;
        }

        try {
            BatchPromise batchPromise = (BatchPromise) promise;
            RPromise sentPromise = (RPromise) batchPromise.getSentPromise();
            super.handleSuccess(sentPromise, connectionFuture, null);
        } finally {
            latch.countDown();
        }
    }
    
    @Override
    protected void handleError(RFuture connectionFuture, Throwable cause) {
        try {
            if (mainPromise instanceof BatchPromise) {
                BatchPromise batchPromise = (BatchPromise) mainPromise;
                RPromise sentPromise = (RPromise) batchPromise.getSentPromise();
                sentPromise.tryFailure(cause);
                mainPromise.tryFailure(cause);
                if (executed.compareAndSet(false, true)) {
                    connectionFuture.getNow().forceFastReconnectAsync().onComplete((res, e) -> {
                        RedisQueuedBatchExecutor.super.releaseConnection(mainPromise, connectionFuture);
                    });
                }
                return;
            }

            super.handleError(connectionFuture, cause);
        } finally {
            latch.countDown();
        }
    }
    
    @Override
    protected void sendCommand(RPromise attemptPromise, RedisConnection connection) {
        MasterSlaveEntry msEntry = getEntry(source);
        ConnectionEntry connectionEntry = connections.get(msEntry);

        boolean syncSlaves = options.getSyncSlaves() > 0;

        if (source.getRedirect() == Redirect.ASK) {
            List> list = new ArrayList>(2);
            RPromise promise = new RedissonPromise();
            list.add(new CommandData(promise, codec, RedisCommands.ASKING, new Object[]{}));
            if (connectionEntry.isFirstCommand()) {
                list.add(new CommandData(promise, codec, RedisCommands.MULTI, new Object[]{}));
                connectionEntry.setFirstCommand(false);
            }
            list.add(new CommandData(attemptPromise, codec, command, params));
            RPromise main = new RedissonPromise();
            writeFuture = connection.send(new CommandsData(main, list, true, syncSlaves));
        } else {
            if (log.isDebugEnabled()) {
                log.debug("acquired connection for command {} and params {} from slot {} using node {}... {}",
                        command, LogHelper.toString(params), source, connection.getRedisClient().getAddr(), connection);
            }
            
            if (connectionEntry.isFirstCommand()) {
                List> list = new ArrayList>(2);
                list.add(new CommandData(new RedissonPromise(), codec, RedisCommands.MULTI, new Object[]{}));
                list.add(new CommandData(attemptPromise, codec, command, params));
                RPromise main = new RedissonPromise();
                writeFuture = connection.send(new CommandsData(main, list, true, syncSlaves));
                connectionEntry.setFirstCommand(false);
            } else {
                if (RedisCommands.EXEC.getName().equals(command.getName())) {
                    Entry entry = commands.get(msEntry);

                    List> list = new ArrayList<>();

                    if (options.isSkipResult()) {
                        list.add(new CommandData(new RedissonPromise(), codec, RedisCommands.CLIENT_REPLY, new Object[]{ "OFF" }));
                    }
                    
                    list.add(new CommandData(attemptPromise, codec, command, params));
                    
                    if (options.isSkipResult()) {
                        list.add(new CommandData(new RedissonPromise(), codec, RedisCommands.CLIENT_REPLY, new Object[]{ "ON" }));
                    }
                    if (options.getSyncSlaves() > 0) {
                        BatchCommandData waitCommand = new BatchCommandData(RedisCommands.WAIT, 
                                new Object[] { this.options.getSyncSlaves(), this.options.getSyncTimeout() }, index.incrementAndGet());
                        list.add(waitCommand);
                        entry.getCommands().add(waitCommand);
                    }

                    RPromise main = new RedissonPromise();
                    writeFuture = connection.send(new CommandsData(main, list, new ArrayList(entry.getCommands()),
                                options.isSkipResult(), false, true, syncSlaves));
                } else {
                    RPromise main = new RedissonPromise();
                    List> list = new ArrayList<>();
                    list.add(new CommandData(attemptPromise, codec, command, params));
                    writeFuture = connection.send(new CommandsData(main, list, true, syncSlaves));
                }
            }
        }
    }
    
    @Override
    protected RFuture getConnection() {
        MasterSlaveEntry msEntry = getEntry(source);
        ConnectionEntry entry = connections.get(msEntry);
        if (entry == null) {
            entry = new ConnectionEntry();
            ConnectionEntry oldEntry = connections.putIfAbsent(msEntry, entry);
            if (oldEntry != null) {
                entry = oldEntry;
            }
        }

        
        if (entry.getConnectionFuture() != null) {
            return entry.getConnectionFuture();
        }
        
        synchronized (this) {
            if (entry.getConnectionFuture() != null) {
                return entry.getConnectionFuture();
            }
        
            RFuture connectionFuture;
            if (this.options.getExecutionMode() == ExecutionMode.REDIS_WRITE_ATOMIC) {
                connectionFuture = connectionManager.connectionWriteOp(source, null);
            } else {
                connectionFuture = connectionManager.connectionReadOp(source, null);
            }
            connectionFuture.syncUninterruptibly();
            entry.setConnectionFuture(connectionFuture);
            return connectionFuture;
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy