org.redisson.command.RedisExecutor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson-all Show documentation
Show all versions of redisson-all Show documentation
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
The newest version!
/**
* Copyright (c) 2013-2024 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 io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.Timeout;
import io.netty.util.TimerTask;
import org.redisson.RedissonShutdownException;
import org.redisson.ScanResult;
import org.redisson.api.NodeType;
import org.redisson.cache.LRUCacheMap;
import org.redisson.client.*;
import org.redisson.client.codec.BaseCodec;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.CommandData;
import org.redisson.client.protocol.CommandsData;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.decoder.ListMultiDecoder2;
import org.redisson.client.protocol.decoder.ObjectListReplayDecoder;
import org.redisson.connection.ClientConnectionsEntry;
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.LogHelper;
import org.redisson.misc.RedisURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
/**
*
* @author Nikita Koksharov
*
* @param type of value
* @param type of returned value
*/
@SuppressWarnings({"NestedIfDepth", "ParameterNumber"})
public class RedisExecutor {
static final Logger log = LoggerFactory.getLogger(RedisExecutor.class);
final boolean readOnlyMode;
final RedisCommand command;
final Object[] params;
final CompletableFuture mainPromise;
final boolean ignoreRedirect;
final RedissonObjectBuilder objectBuilder;
final ConnectionManager connectionManager;
final RedissonObjectBuilder.ReferenceType referenceType;
final boolean noRetry;
final int attempts;
final int retryInterval;
final int responseTimeout;
final boolean trackChanges;
CompletableFuture connectionFuture;
boolean reuseConnection;
NodeSource source;
MasterSlaveEntry entry;
Codec codec;
volatile int attempt;
volatile Optional timeout = Optional.empty();
volatile BiConsumer mainPromiseListener;
volatile ChannelFuture writeFuture;
volatile RedisException exception;
public RedisExecutor(boolean readOnlyMode, NodeSource source, Codec codec, RedisCommand command,
Object[] params, CompletableFuture mainPromise, boolean ignoreRedirect,
ConnectionManager connectionManager, RedissonObjectBuilder objectBuilder,
RedissonObjectBuilder.ReferenceType referenceType, boolean noRetry,
int retryAttempts, int retryInterval, int responseTimeout,
boolean trackChanges) {
super();
this.readOnlyMode = readOnlyMode;
this.source = source;
this.codec = codec;
this.command = command;
this.params = params;
this.mainPromise = mainPromise;
this.ignoreRedirect = ignoreRedirect;
this.connectionManager = connectionManager;
this.objectBuilder = objectBuilder;
this.noRetry = noRetry;
this.attempts = retryAttempts;
this.retryInterval = retryInterval;
this.responseTimeout = responseTimeout;
this.referenceType = referenceType;
this.trackChanges = trackChanges;
}
public void execute() {
if (mainPromise.isCancelled()) {
free();
return;
}
if (getClass() == RedisExecutor.class) {
connectionManager.getServiceManager().addFuture(mainPromise);
}
if (connectionManager.getServiceManager().isShuttingDown()) {
free();
mainPromise.completeExceptionally(new RedissonShutdownException("Redisson is shutdown"));
return;
}
try {
codec = getCodec(codec);
CompletableFuture attemptPromise = new CompletableFuture<>();
CompletableFuture connectionFuture = getConnection(attemptPromise);
mainPromiseListener = (r, e) -> {
if (!mainPromise.isCompletedExceptionally()) {
return;
}
if (connectionFuture.completeExceptionally(new CancellationException())) {
log.debug("Connection obtaining canceled for {}", command);
timeout.ifPresent(Timeout::cancel);
if (attemptPromise.completeExceptionally(new CancellationException())) {
free();
}
return;
}
if (command.isBlockingCommand()) {
if (writeFuture.cancel(false)) {
attemptPromise.completeExceptionally(new CancellationException());
} else {
RedisConnection c = connectionFuture.getNow(null);
c.forceFastReconnectAsync().whenComplete((res, ex) -> {
attemptPromise.completeExceptionally(new CancellationException());
});
}
}
};
if (attempt == 0) {
mainPromise.whenComplete((r, e) -> {
if (this.mainPromiseListener != null) {
this.mainPromiseListener.accept(r, e);
}
});
}
scheduleRetryTimeout(connectionFuture, attemptPromise);
scheduleConnectionTimeout(attemptPromise, connectionFuture);
connectionFuture.whenComplete((connection, e) -> {
if (connectionFuture.isCancelled()) {
return;
}
if (connectionManager.getServiceManager().isShuttingDown()) {
exception = new RedissonShutdownException("Redisson is shutdown");
tryComplete(attemptPromise, exception);
return;
}
if (connectionFuture.isDone() && connectionFuture.isCompletedExceptionally()) {
exception = convertException(connectionFuture);
tryComplete(attemptPromise, exception);
return;
}
try {
sendCommand(attemptPromise, connection);
} catch (Exception ex) {
free();
handleError(connectionFuture, e);
return;
}
scheduleWriteTimeout(attemptPromise);
writeFuture.addListener((ChannelFutureListener) future -> {
checkWriteFuture(writeFuture, attemptPromise, connection);
});
});
attemptPromise.whenComplete((r, e) -> {
releaseConnection(attemptPromise, connectionFuture);
checkAttemptPromise(attemptPromise, connectionFuture);
}).whenComplete((r, e) -> {
if (e != null
&& !attemptPromise.isCompletedExceptionally()) {
log.error(e.getMessage(), e);
}
});
} catch (Exception e) {
free();
handleError(connectionFuture, e);
throw e;
}
}
private void scheduleConnectionTimeout(CompletableFuture attemptPromise, CompletableFuture connectionFuture) {
if (retryInterval > 0 && attempts > 0) {
return;
}
timeout.ifPresent(Timeout::cancel);
TimerTask task = timeout -> {
if (connectionFuture.completeExceptionally(new CancellationException())) {
exception = new RedisTimeoutException("Unable to acquire connection! " + this.connectionFuture +
"Increase connection pool size or timeout. "
+ "Node source: " + source
+ ", " + LogHelper.toString(command, params)
+ " after " + attempt + " retry attempts");
attemptPromise.completeExceptionally(exception);
}
};
timeout = Optional.of(connectionManager.getServiceManager().newTimeout(task, responseTimeout, TimeUnit.MILLISECONDS));
}
private void scheduleWriteTimeout(CompletableFuture attemptPromise) {
if (retryInterval > 0 && attempts > 0) {
return;
}
timeout.ifPresent(Timeout::cancel);
TimerTask task = timeout -> {
if (writeFuture.cancel(false)) {
exception = new RedisTimeoutException("Command still hasn't been written into connection! " +
"Check CPU usage of the JVM. Check that there are no blocking invocations in async/reactive/rx listeners or subscribeOnElements method. Check connection with Redis node: " + connectionFuture.join().getRedisClient().getAddr() +
" for TCP packet drops. Try to increase nettyThreads setting. "
+ " Node source: " + source + ", connection: " + connectionFuture.join()
+ ", " + LogHelper.toString(command, params)
+ " after " + attempt + " retry attempts");
attemptPromise.completeExceptionally(exception);
}
};
timeout = Optional.of(connectionManager.getServiceManager().newTimeout(task, responseTimeout, TimeUnit.MILLISECONDS));
}
private void scheduleRetryTimeout(CompletableFuture connectionFuture, CompletableFuture attemptPromise) {
if (retryInterval == 0 || attempts == 0) {
return;
}
TimerTask retryTimerTask = new TimerTask() {
@Override
public void run(Timeout t) throws Exception {
if (attemptPromise.isDone()) {
return;
}
if (connectionFuture.completeExceptionally(new CancellationException())) {
exception = new RedisTimeoutException("Unable to acquire connection! " + connectionFuture +
"Increase connection pool size. "
+ "Node source: " + source
+ ", " + LogHelper.toString(command, params)
+ " after " + attempt + " retry attempts");
} else {
if (connectionFuture.isDone() && !connectionFuture.isCompletedExceptionally()) {
if (writeFuture == null || !writeFuture.isDone()) {
if (attempt == attempts) {
if (writeFuture != null && writeFuture.cancel(false)) {
if (exception == null) {
exception = new RedisTimeoutException("Command still hasn't been written into connection! " +
"Check CPU usage of the JVM. Check that there are no blocking invocations in async/reactive/rx listeners or subscribeOnElements method. Check connection with Redis node: " + getNow(connectionFuture).getRedisClient().getAddr() +
" for TCP packet drops. Try to increase nettyThreads setting. "
+ " Node source: " + source + ", connection: " + getNow(connectionFuture)
+ ", " + LogHelper.toString(command, params)
+ " after " + attempt + " retry attempts");
}
attemptPromise.completeExceptionally(exception);
}
return;
}
attempt++;
scheduleRetryTimeout(connectionFuture, attemptPromise);
return;
}
if (writeFuture.isSuccess()) {
return;
}
}
}
if (mainPromise.isCompletedExceptionally()) {
Throwable c = cause(mainPromise);
if (c instanceof CancellationException || c instanceof RedissonShutdownException) {
if (attemptPromise.completeExceptionally(new CancellationException())) {
free();
}
}
return;
}
if (attempt == attempts) {
// filled out in connectionFuture or writeFuture handler
if (exception != null) {
attemptPromise.completeExceptionally(exception);
}
return;
}
if (!attemptPromise.completeExceptionally(new CancellationException())) {
return;
}
attempt++;
if (log.isDebugEnabled()) {
log.debug("attempt {} for {} to {}",
attempt, LogHelper.toString(command, params), source);
}
mainPromiseListener = null;
execute();
}
};
timeout = Optional.of(connectionManager.getServiceManager().newTimeout(retryTimerTask, retryInterval, TimeUnit.MILLISECONDS));
}
protected void free() {
free(params);
}
protected void free(Object[] params) {
for (Object obj : params) {
ReferenceCountUtil.safeRelease(obj);
}
}
private void checkWriteFuture(ChannelFuture future, CompletableFuture attemptPromise, RedisConnection connection) {
if (future.isCancelled() || attemptPromise.isDone()) {
return;
}
if (!future.isSuccess()) {
exception = new WriteRedisConnectionException(
"Unable to write command into connection! Check CPU usage of the JVM. Try to increase nettyThreads setting. Node source: "
+ source + ", connection: " + connection +
", " + LogHelper.toString(command, params)
+ " after " + attempt + " retry attempts", future.cause());
tryComplete(attemptPromise, exception);
return;
}
scheduleResponseTimeout(attemptPromise, connection);
}
private void tryComplete(CompletableFuture attemptPromise, RedisException exception) {
if (attempt == attempts) {
attemptPromise.completeExceptionally(exception);
} else if (retryInterval == 0) {
attempt++;
if (log.isDebugEnabled()) {
log.debug("attempt {} for {} to {}",
attempt, LogHelper.toString(command, params), source);
}
mainPromiseListener = null;
execute();
}
}
private void scheduleResponseTimeout(CompletableFuture attemptPromise, RedisConnection connection) {
timeout.ifPresent(Timeout::cancel);
long timeoutTime = responseTimeout;
if (command != null && command.isBlockingCommand()) {
long popTimeout = 0;
if (RedisCommands.BLOCKING_COMMANDS.contains(command)) {
for (int i = 0; i < params.length-1; i++) {
if ("BLOCK".equals(params[i])) {
popTimeout = Long.valueOf(params[i+1].toString());
break;
}
}
} else {
if (RedisCommands.BZMPOP.getName().equals(command.getName())) {
popTimeout = Long.valueOf(params[0].toString()) * 1000;
} else {
popTimeout = Long.valueOf(params[params.length - 1].toString()) * 1000;
}
}
handleBlockingOperations(attemptPromise, connection, popTimeout);
if (popTimeout == 0) {
return;
}
timeoutTime += popTimeout;
// add 1 second due to issue https://github.com/antirez/redis/issues/874
timeoutTime += 1000;
}
long timeoutAmount = timeoutTime;
TimerTask timeoutResponseTask = timeout -> {
if (isResendAllowed(attempt, attempts)) {
if (!attemptPromise.completeExceptionally(new CancellationException())) {
return;
}
connectionManager.getServiceManager().newTimeout(t -> {
attempt++;
if (log.isDebugEnabled()) {
log.debug("response timeout. new attempt {} for {} node {}",
attempt, LogHelper.toString(command, params), source);
}
mainPromiseListener = null;
execute();
}, retryInterval, TimeUnit.MILLISECONDS);
return;
}
attemptPromise.completeExceptionally(
new RedisResponseTimeoutException("Redis server response timeout (" + timeoutAmount + " ms) occured"
+ " after " + attempt + " retry attempts,"
+ " is non-idempotent command: " + (command != null && command.isNoRetry())
+ " Check connection with Redis node: " + connection.getRedisClient().getAddr() + " for TCP packet drops or bandwidth limits. "
+ " Try to increase nettyThreads and/or timeout settings. "
+ LogHelper.toString(command, params) + ", channel: " + connection.getChannel()));
};
timeout = Optional.of(connectionManager.getServiceManager().newTimeout(timeoutResponseTask, timeoutTime, TimeUnit.MILLISECONDS));
}
private boolean isResendAllowed(int attempt, int attempts) {
return attempt < attempts
&& !noRetry
&& (command == null || (!command.isBlockingCommand() && !command.isNoRetry()));
}
private void handleBlockingOperations(CompletableFuture attemptPromise, RedisConnection connection, long popTimeout) {
Timeout scheduledFuture;
if (popTimeout != 0) {
// handling cases when connection has been lost
scheduledFuture = connectionManager.getServiceManager().newTimeout(timeout -> {
R res = null;
if (command.getReplayMultiDecoder() instanceof ObjectListReplayDecoder
|| command.getReplayMultiDecoder() instanceof ListMultiDecoder2) {
res = (R) Collections.emptyList();
}
if (attemptPromise.complete(res)) {
connection.forceFastReconnectAsync();
}
}, popTimeout + 3000, TimeUnit.MILLISECONDS);
} else {
scheduledFuture = null;
}
mainPromise.whenComplete((res, e) -> {
if (scheduledFuture != null) {
scheduledFuture.cancel();
}
// handling cancel operation for blocking commands
if ((mainPromise.isCancelled()
|| e instanceof InterruptedException)
&& !attemptPromise.isDone()) {
log.debug("Canceled blocking operation {} used {}", command, connection);
connection.forceFastReconnectAsync().whenComplete((r, ex) -> {
attemptPromise.completeExceptionally(new CancellationException());
});
return;
}
if (connectionManager.getServiceManager().isShuttingDown(e)) {
attemptPromise.completeExceptionally(e);
}
});
}
protected final Throwable cause(CompletableFuture> future) {
try {
future.getNow(null);
return null;
} catch (CompletionException ex2) {
return ex2.getCause();
} catch (CancellationException ex1) {
return ex1;
}
}
protected void checkAttemptPromise(CompletableFuture attemptFuture, CompletableFuture connectionFuture) {
timeout.ifPresent(Timeout::cancel);
if (attemptFuture.isCancelled()) {
return;
}
try {
mainPromiseListener = null;
Throwable cause = cause(attemptFuture);
if (cause instanceof RedisWrongPasswordException) {
if (attempt < attempts) {
onException();
reuseConnection = true;
CompletionStage f = connectionFuture.join().forceFastReconnectAsync();
f.thenAccept(v -> {
attempt++;
execute();
});
return;
}
}
if (cause instanceof RedisMovedException && !ignoreRedirect) {
RedisMovedException ex = (RedisMovedException) cause;
if (source.getRedirect() == Redirect.MOVED
&& source.getAddr().equals(ex.getUrl())) {
mainPromise.completeExceptionally(new RedisException("MOVED redirection loop detected. Node " + source.getAddr() + " has further redirect to " + ex.getUrl()));
return;
}
onException();
CompletableFuture ipAddrFuture = connectionManager.getServiceManager().resolveIP(ex.getUrl());
ipAddrFuture.whenComplete((ip, e) -> {
if (e != null) {
free();
handleError(connectionFuture, e);
return;
}
source = new NodeSource(ex.getSlot(), ip, Redirect.MOVED);
execute();
});
return;
}
if (cause instanceof RedisAskException && !ignoreRedirect) {
RedisAskException ex = (RedisAskException) cause;
onException();
CompletableFuture ipAddrFuture = connectionManager.getServiceManager().resolveIP(ex.getUrl());
ipAddrFuture.whenComplete((ip, e) -> {
if (e != null) {
free();
handleError(connectionFuture, e);
return;
}
source = new NodeSource(ex.getSlot(), ip, Redirect.ASK);
execute();
});
return;
}
if (cause instanceof RedisLoadingException) {
RedisConnection connection = connectionFuture.getNow(null);
if (connection != null) {
ClientConnectionsEntry ce = entry.getEntry(connection.getRedisClient());
if (ce != null && ce.getNodeType() == NodeType.SLAVE) {
source = new NodeSource(entry.getClient());
execute();
return;
}
}
}
if (cause instanceof RedisRetryException
|| cause instanceof RedisReadonlyException
|| (cause instanceof RedisReconnectedException
&& (writeFuture.cancel(false) || isResendAllowed(attempt, attempts)))) {
if (attempt < attempts) {
onException();
connectionManager.getServiceManager().newTimeout(timeout -> {
attempt++;
execute();
}, retryInterval, TimeUnit.MILLISECONDS);
return;
}
}
free();
handleResult(attemptFuture, connectionFuture);
} catch (Exception e) {
handleError(connectionFuture, e);
}
}
protected void handleResult(CompletableFuture attemptPromise, CompletableFuture connectionFuture) throws ReflectiveOperationException {
R res;
try {
res = attemptPromise.getNow(null);
} catch (CompletionException e) {
handleError(connectionFuture, e.getCause());
return;
} catch (CancellationException e) {
handleError(connectionFuture, e);
return;
}
if (res instanceof ScanResult) {
((ScanResult) res).setRedisClient(getNow(connectionFuture).getRedisClient());
}
handleSuccess(mainPromise, connectionFuture, res);
}
protected void onException() {
}
protected void handleError(CompletableFuture connectionFuture, Throwable cause) {
mainPromise.completeExceptionally(cause);
if (connectionFuture == null) {
return;
}
RedisClient client = connectionFuture.join().getRedisClient();
FailedNodeDetector detector = client.getConfig().getFailedNodeDetector();
detector.onCommandFailed(cause);
if (detector.isNodeFailed()) {
log.error("Redis node {} has been marked as failed as failed according to the detection logic defined in {}",
entry.getClient().getAddr(), detector);
entry.shutdownAndReconnectAsync(client, cause);
}
}
protected void handleSuccess(CompletableFuture promise, CompletableFuture connectionFuture, R res) throws ReflectiveOperationException {
if (objectBuilder != null) {
promise.complete((R) objectBuilder.tryHandleReference(res, referenceType));
} else {
promise.complete(res);
}
connectionFuture.join().getRedisClient().getConfig().getFailedNodeDetector().onCommandSuccessful();
}
protected void sendCommand(CompletableFuture attemptPromise, RedisConnection connection) {
if (source.getRedirect() == Redirect.ASK) {
List> list = new ArrayList<>(2);
CompletableFuture promise = new CompletableFuture<>();
list.add(new CommandData<>(promise, codec, RedisCommands.ASKING, new Object[]{}));
list.add(new CommandData<>(attemptPromise, codec, command, params));
CompletableFuture main = new CompletableFuture<>();
writeFuture = connection.send(new CommandsData(main, list, false, false));
} else {
if (log.isDebugEnabled()) {
String connectionType = " ";
if (connection instanceof RedisPubSubConnection) {
connectionType = " pubsub ";
}
log.debug("acquired{}connection for {} from slot {} using node {}... {}",
connectionType, LogHelper.toString(command, params), source, connection.getRedisClient().getAddr(), connection);
}
writeFuture = connection.send(new CommandData<>(attemptPromise, codec, command, params));
if (connectionManager.getServiceManager().getConfig().getMasterConnectionPoolSize() < 10
&& !command.isBlockingCommand()) {
release(connection);
}
}
}
protected void releaseConnection(CompletableFuture attemptPromise, CompletableFuture connectionFuture) {
if (connectionFuture.isDone() && connectionFuture.isCompletedExceptionally()) {
return;
}
Throwable cause = cause(attemptPromise);
if (cause instanceof RedisWrongPasswordException
&& attempt < attempts) {
return;
}
RedisConnection connection = getNow(connectionFuture);
if (connectionManager.getServiceManager().getConfig().getMasterConnectionPoolSize() < 10) {
if (source.getRedirect() == Redirect.ASK
|| getClass() != RedisExecutor.class
|| (command != null && command.isBlockingCommand())) {
release(connection);
}
} else {
release(connection);
}
if (log.isDebugEnabled()) {
String connectionType = " ";
if (connection instanceof RedisPubSubConnection) {
connectionType = " pubsub ";
}
log.debug("connection{}released for {} from slot {} using connection {}",
connectionType, LogHelper.toString(command, params), source, connection);
}
}
private void release(RedisConnection connection) {
if (readOnlyMode) {
entry.releaseRead(connection);
} else {
entry.releaseWrite(connection);
}
}
public RedisClient getRedisClient() {
return getNow(connectionFuture).getRedisClient();
}
protected CompletableFuture getConnection(CompletableFuture attemptPromise) {
if (reuseConnection) {
reuseConnection = false;
return connectionFuture;
}
if (readOnlyMode) {
connectionFuture = connectionReadOp(command, attemptPromise);
} else {
connectionFuture = connectionWriteOp(command, attemptPromise);
}
return connectionFuture;
}
private static final Map> CODECS = new LRUCacheMap<>(25, 0, 0);
protected final Codec getCodec(Codec codec) {
if (codec == null) {
return null;
}
if (!connectionManager.getServiceManager().getCfg().isUseThreadClassLoader()) {
return codec;
}
for (Class> clazz : BaseCodec.SKIPPED_CODECS) {
if (clazz.isAssignableFrom(codec.getClass())) {
return codec;
}
}
Codec codecToUse = codec;
ClassLoader threadClassLoader = Thread.currentThread().getContextClassLoader();
if (threadClassLoader != null) {
Map map = CODECS.computeIfAbsent(threadClassLoader, k ->
new LRUCacheMap<>(200, 0, 0));
codecToUse = map.get(codec);
if (codecToUse == null) {
try {
codecToUse = codec.getClass().getConstructor(ClassLoader.class, codec.getClass()).newInstance(threadClassLoader, codec);
} catch (NoSuchMethodException e) {
codecToUse = codec;
// skip
} catch (Exception e) {
throw new IllegalStateException(e);
}
map.put(codec, codecToUse);
}
}
return codecToUse;
}
protected final T getNow(CompletableFuture future) {
try {
return future.getNow(null);
} catch (Exception e) {
return null;
}
}
private RedisException convertException(CompletableFuture future) {
Throwable cause = cause(future);
if (cause instanceof RedisException) {
return (RedisException) cause;
}
return new RedisException("Unexpected exception while processing command", cause);
}
final CompletableFuture connectionReadOp(RedisCommand> command, CompletableFuture attemptPromise) {
try {
// TODO make the method async
entry = getEntry(true);
} catch (Exception e) {
attemptPromise.completeExceptionally(e);
CompletableFuture f = new CompletableFuture<>();
f.completeExceptionally(e);
return f;
}
if (entry == null) {
CompletableFuture f = new CompletableFuture<>();
f.completeExceptionally(connectionManager.getServiceManager().createNodeNotFoundException(source));
return f;
}
if (source.getRedirect() != null) {
return entry.connectionReadOp(command, source.getAddr());
}
if (source.getRedisClient() != null) {
return entry.connectionReadOp(command, source.getRedisClient(), trackChanges);
}
return entry.connectionReadOp(command, trackChanges);
}
final CompletableFuture connectionWriteOp(RedisCommand> command, CompletableFuture attemptPromise) {
try {
// TODO make the method async
entry = getEntry(false);
} catch (Exception e) {
attemptPromise.completeExceptionally(e);
CompletableFuture f = new CompletableFuture<>();
f.completeExceptionally(e);
return f;
}
if (entry == null) {
CompletableFuture f = new CompletableFuture<>();
f.completeExceptionally(connectionManager.getServiceManager().createNodeNotFoundException(source));
return f;
}
// fix for https://github.com/redisson/redisson/issues/1548
if (source.getRedirect() != null
&& !source.getAddr().equals(entry.getClient().getAddr())
&& entry.hasSlave(source.getAddr())) {
return entry.redirectedConnectionWriteOp(command, source.getAddr());
}
return entry.connectionWriteOp(command);
}
private MasterSlaveEntry getEntry(boolean read) {
if (source.getRedirect() != null) {
return connectionManager.getEntry(source.getAddr());
}
MasterSlaveEntry entry = source.getEntry();
if (source.getRedisClient() != null) {
entry = connectionManager.getEntry(source.getRedisClient());
}
if (entry == null && source.getSlot() != null) {
if (read) {
entry = connectionManager.getReadEntry(source.getSlot());
} else {
entry = connectionManager.getWriteEntry(source.getSlot());
}
}
return entry;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy