Java tutorial
/** * Copyright 2016 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 java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.redisson.RedisClientResult; import org.redisson.RedissonReference; import org.redisson.RedissonShutdownException; import org.redisson.SlotCallback; import org.redisson.api.RFuture; import org.redisson.api.RedissonClient; import org.redisson.api.RedissonReactiveClient; import org.redisson.client.RedisAskException; import org.redisson.client.RedisConnection; import org.redisson.client.RedisException; import org.redisson.client.RedisLoadingException; import org.redisson.client.RedisMovedException; import org.redisson.client.RedisTimeoutException; import org.redisson.client.RedisTryAgainException; import org.redisson.client.WriteRedisConnectionException; 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.ScoredEntry; import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.client.protocol.decoder.MapScanResult; import org.redisson.client.protocol.decoder.ScanObjectEntry; import org.redisson.config.MasterSlaveServersConfig; import org.redisson.connection.ConnectionManager; import org.redisson.connection.MasterSlaveEntry; import org.redisson.connection.NodeSource; import org.redisson.connection.NodeSource.Redirect; import org.redisson.misc.LogHelper; import org.redisson.misc.RPromise; import org.redisson.misc.RedissonObjectFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.util.Timeout; import io.netty.util.TimerTask; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; /** * * @author Nikita Koksharov * */ public class CommandAsyncService implements CommandAsyncExecutor { private static final Logger log = LoggerFactory.getLogger(CommandAsyncService.class); final ConnectionManager connectionManager; protected RedissonClient redisson; protected RedissonReactiveClient redissonReactive; public CommandAsyncService(ConnectionManager connectionManager) { this.connectionManager = connectionManager; } @Override public ConnectionManager getConnectionManager() { return connectionManager; } @Override public CommandAsyncExecutor enableRedissonReferenceSupport(RedissonClient redisson) { if (redisson != null) { this.redisson = redisson; this.redissonReactive = null; } return this; } @Override public CommandAsyncExecutor enableRedissonReferenceSupport(RedissonReactiveClient redissonReactive) { if (redissonReactive != null) { this.redissonReactive = redissonReactive; this.redisson = null; } return this; } @Override public boolean isRedissonReferenceSupportEnabled() { return redisson != null || redissonReactive != null; } @Override public void syncSubscription(RFuture<?> future) { MasterSlaveServersConfig config = connectionManager.getConfig(); try { int timeout = config.getTimeout() + config.getRetryInterval() * config.getRetryAttempts(); if (!future.await(timeout)) { throw new RedisTimeoutException("Subscribe timeout: (" + timeout + "ms)"); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } future.syncUninterruptibly(); } @Override public <V> V get(RFuture<V> future) { if (!future.isDone()) { final CountDownLatch l = new CountDownLatch(1); future.addListener(new FutureListener<V>() { @Override public void operationComplete(Future<V> future) throws Exception { l.countDown(); } }); boolean interrupted = false; while (!future.isDone()) { try { l.await(); } catch (InterruptedException e) { interrupted = true; } } if (interrupted) { Thread.currentThread().interrupt(); } } // commented out due to blocking issues up to 200 ms per minute for each thread // future.awaitUninterruptibly(); if (future.isSuccess()) { return future.getNow(); } throw convertException(future); } @Override public boolean await(RFuture<?> future, long timeout, TimeUnit timeoutUnit) throws InterruptedException { final CountDownLatch l = new CountDownLatch(1); future.addListener(new FutureListener<Object>() { @Override public void operationComplete(Future<Object> future) throws Exception { l.countDown(); } }); return l.await(timeout, timeoutUnit); } @Override public <T, R> RFuture<R> readAsync(InetSocketAddress client, MasterSlaveEntry entry, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); async(true, new NodeSource(entry, client), codec, command, params, mainPromise, 0); return mainPromise; } @Override public <T, R> RFuture<R> readAsync(InetSocketAddress client, String key, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); int slot = connectionManager.calcSlot(key); async(true, new NodeSource(slot, client), codec, command, params, mainPromise, 0); return mainPromise; } @Override public <T, R> RFuture<Collection<R>> readAllAsync(RedisCommand<T> command, Object... params) { final RPromise<Collection<R>> mainPromise = connectionManager.newPromise(); final Set<MasterSlaveEntry> nodes = connectionManager.getEntrySet(); final List<R> results = new ArrayList<R>(); final AtomicInteger counter = new AtomicInteger(nodes.size()); FutureListener<R> listener = new FutureListener<R>() { @Override public void operationComplete(Future<R> future) throws Exception { if (!future.isSuccess()) { mainPromise.tryFailure(future.cause()); return; } R result = future.getNow(); if (result instanceof Collection) { synchronized (results) { results.addAll((Collection) result); } } else { synchronized (results) { results.add(result); } } if (counter.decrementAndGet() == 0 && !mainPromise.isDone()) { mainPromise.trySuccess(results); } } }; for (MasterSlaveEntry entry : nodes) { RPromise<R> promise = connectionManager.newPromise(); promise.addListener(listener); async(true, new NodeSource(entry), connectionManager.getCodec(), command, params, promise, 0); } return mainPromise; } @Override public <T, R> RFuture<R> readRandomAsync(RedisCommand<T> command, Object... params) { final RPromise<R> mainPromise = connectionManager.newPromise(); final List<MasterSlaveEntry> nodes = new ArrayList<MasterSlaveEntry>(connectionManager.getEntrySet()); Collections.shuffle(nodes); retryReadRandomAsync(command, mainPromise, nodes, params); return mainPromise; } private <R, T> void retryReadRandomAsync(final RedisCommand<T> command, final RPromise<R> mainPromise, final List<MasterSlaveEntry> nodes, final Object... params) { final RPromise<R> attemptPromise = connectionManager.newPromise(); attemptPromise.addListener(new FutureListener<R>() { @Override public void operationComplete(Future<R> future) throws Exception { if (future.isSuccess()) { if (future.getNow() == null) { if (nodes.isEmpty()) { mainPromise.trySuccess(null); } else { retryReadRandomAsync(command, mainPromise, nodes, params); } } else { mainPromise.trySuccess(future.getNow()); } } else { mainPromise.tryFailure(future.cause()); } } }); MasterSlaveEntry entry = nodes.remove(0); async(true, new NodeSource(entry), connectionManager.getCodec(), command, params, attemptPromise, 0); } @Override public <T> RFuture<Void> writeAllAsync(RedisCommand<T> command, Object... params) { return writeAllAsync(command, null, params); } @Override public <R, T> RFuture<R> writeAllAsync(RedisCommand<T> command, SlotCallback<T, R> callback, Object... params) { return allAsync(false, command, callback, params); } @Override public <R, T> RFuture<R> readAllAsync(RedisCommand<T> command, SlotCallback<T, R> callback, Object... params) { return allAsync(true, command, callback, params); } private <T, R> RFuture<R> allAsync(boolean readOnlyMode, RedisCommand<T> command, final SlotCallback<T, R> callback, Object... params) { final RPromise<R> mainPromise = connectionManager.newPromise(); final Set<MasterSlaveEntry> nodes = connectionManager.getEntrySet(); final AtomicInteger counter = new AtomicInteger(nodes.size()); FutureListener<T> listener = new FutureListener<T>() { @Override public void operationComplete(Future<T> future) throws Exception { if (!future.isSuccess()) { mainPromise.tryFailure(future.cause()); return; } if (callback != null) { callback.onSlotResult(future.getNow()); } if (counter.decrementAndGet() == 0) { if (callback != null) { mainPromise.trySuccess(callback.onFinish()); } else { mainPromise.trySuccess(null); } } } }; for (MasterSlaveEntry entry : nodes) { RPromise<T> promise = connectionManager.newPromise(); promise.addListener(listener); async(readOnlyMode, new NodeSource(entry), connectionManager.getCodec(), command, params, promise, 0); } return mainPromise; } public <V> RedisException convertException(RFuture<V> future) { return future.cause() instanceof RedisException ? (RedisException) future.cause() : new RedisException("Unexpected exception while processing command", future.cause()); } private NodeSource getNodeSource(String key) { int slot = connectionManager.calcSlot(key); MasterSlaveEntry entry = connectionManager.getEntry(slot); return new NodeSource(entry); } @Override public <T, R> RFuture<R> readAsync(String key, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); NodeSource source = getNodeSource(key); async(true, source, codec, command, params, mainPromise, 0); return mainPromise; } public <T, R> RFuture<R> readAsync(MasterSlaveEntry entry, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); async(true, new NodeSource(entry), codec, command, params, mainPromise, 0); return mainPromise; } public <T, R> RFuture<R> readAsync(Integer slot, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); async(true, new NodeSource(slot), codec, command, params, mainPromise, 0); return mainPromise; } @Override public <T, R> RFuture<R> writeAsync(MasterSlaveEntry entry, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); async(false, new NodeSource(entry), codec, command, params, mainPromise, 0); return mainPromise; } @Override public <T, R> RFuture<R> writeAsync(Integer slot, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); async(false, new NodeSource(slot), codec, command, params, mainPromise, 0); return mainPromise; } @Override public <T, R> RFuture<R> readAsync(String key, RedisCommand<T> command, Object... params) { return readAsync(key, connectionManager.getCodec(), command, params); } @Override public <T, R> RFuture<R> evalReadAsync(String key, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { NodeSource source = getNodeSource(key); return evalAsync(source, true, codec, evalCommandType, script, keys, params); } @Override public <T, R> RFuture<R> evalReadAsync(MasterSlaveEntry entry, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { return evalAsync(new NodeSource(entry), true, codec, evalCommandType, script, keys, params); } @Override public <T, R> RFuture<R> evalReadAsync(Integer slot, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { return evalAsync(new NodeSource(slot), true, codec, evalCommandType, script, keys, params); } @Override public <T, R> RFuture<R> evalReadAsync(InetSocketAddress client, String key, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { int slot = connectionManager.calcSlot(key); return evalAsync(new NodeSource(slot, client), true, codec, evalCommandType, script, keys, params); } @Override public <T, R> RFuture<R> evalWriteAsync(String key, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { NodeSource source = getNodeSource(key); return evalAsync(source, false, codec, evalCommandType, script, keys, params); } public <T, R> RFuture<R> evalWriteAsync(MasterSlaveEntry entry, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { return evalAsync(new NodeSource(entry), false, codec, evalCommandType, script, keys, params); } public <T, R> RFuture<R> evalWriteAsync(Integer slot, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { return evalAsync(new NodeSource(slot), false, codec, evalCommandType, script, keys, params); } @Override public <T, R> RFuture<R> evalWriteAllAsync(RedisCommand<T> command, SlotCallback<T, R> callback, String script, List<Object> keys, Object... params) { return evalAllAsync(false, command, callback, script, keys, params); } public <T, R> RFuture<R> evalAllAsync(boolean readOnlyMode, RedisCommand<T> command, final SlotCallback<T, R> callback, String script, List<Object> keys, Object... params) { final RPromise<R> mainPromise = connectionManager.newPromise(); final Set<MasterSlaveEntry> entries = connectionManager.getEntrySet(); final AtomicInteger counter = new AtomicInteger(entries.size()); FutureListener<T> listener = new FutureListener<T>() { @Override public void operationComplete(Future<T> future) throws Exception { if (!future.isSuccess()) { mainPromise.tryFailure(future.cause()); return; } callback.onSlotResult(future.getNow()); if (counter.decrementAndGet() == 0 && !mainPromise.isDone()) { mainPromise.trySuccess(callback.onFinish()); } } }; List<Object> args = new ArrayList<Object>(2 + keys.size() + params.length); args.add(script); args.add(keys.size()); args.addAll(keys); args.addAll(Arrays.asList(params)); for (MasterSlaveEntry entry : entries) { RPromise<T> promise = connectionManager.newPromise(); promise.addListener(listener); async(readOnlyMode, new NodeSource(entry), connectionManager.getCodec(), command, args.toArray(), promise, 0); } return mainPromise; } private <T, R> RFuture<R> evalAsync(NodeSource nodeSource, boolean readOnlyMode, Codec codec, RedisCommand<T> evalCommandType, String script, List<Object> keys, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); List<Object> args = new ArrayList<Object>(2 + keys.size() + params.length); args.add(script); args.add(keys.size()); args.addAll(keys); args.addAll(Arrays.asList(params)); async(readOnlyMode, nodeSource, codec, evalCommandType, args.toArray(), mainPromise, 0); return mainPromise; } @Override public <T, R> RFuture<R> writeAsync(String key, RedisCommand<T> command, Object... params) { return writeAsync(key, connectionManager.getCodec(), command, params); } @Override public <T, R> RFuture<R> writeAsync(String key, Codec codec, RedisCommand<T> command, Object... params) { RPromise<R> mainPromise = connectionManager.newPromise(); NodeSource source = getNodeSource(key); async(false, source, codec, command, params, mainPromise, 0); return mainPromise; } protected <V, R> void async(final boolean readOnlyMode, final NodeSource source, final Codec codec, final RedisCommand<V> command, final Object[] params, final RPromise<R> mainPromise, final int attempt) { if (mainPromise.isCancelled()) { return; } if (!connectionManager.getShutdownLatch().acquire()) { mainPromise.tryFailure(new RedissonShutdownException("Redisson is shutdown")); return; } final AsyncDetails<V, R> details = AsyncDetails.acquire(); if (isRedissonReferenceSupportEnabled()) { try { for (int i = 0; i < params.length; i++) { RedissonReference reference = redisson != null ? RedissonObjectFactory.toReference(redisson, params[i]) : RedissonObjectFactory.toReference(redissonReactive, params[i]); params[i] = reference == null ? params[i] : reference; } } catch (Exception e) { connectionManager.getShutdownLatch().release(); mainPromise.tryFailure(e); return; } } final RFuture<RedisConnection> connectionFuture; if (readOnlyMode) { connectionFuture = connectionManager.connectionReadOp(source, command); } else { connectionFuture = connectionManager.connectionWriteOp(source, command); } final RPromise<R> attemptPromise = connectionManager.newPromise(); details.init(connectionFuture, attemptPromise, readOnlyMode, source, codec, command, params, mainPromise, attempt); final TimerTask retryTimerTask = new TimerTask() { @Override public void run(Timeout t) throws Exception { if (details.getAttemptPromise().isDone()) { return; } if (details.getConnectionFuture().cancel(false)) { connectionManager.getShutdownLatch().release(); } else { if (details.getConnectionFuture().isSuccess()) { ChannelFuture writeFuture = details.getWriteFuture(); if (writeFuture != null && !writeFuture.cancel(false) && writeFuture.isSuccess()) { return; } } } if (details.getMainPromise().isCancelled()) { if (details.getAttemptPromise().cancel(false)) { AsyncDetails.release(details); } return; } if (details.getAttempt() == connectionManager.getConfig().getRetryAttempts()) { if (details.getException() == null) { details.setException(new RedisTimeoutException("Command execution timeout for command: " + command + " with params: " + LogHelper.toString(details.getParams()))); } details.getAttemptPromise().tryFailure(details.getException()); return; } if (!details.getAttemptPromise().cancel(false)) { return; } int count = details.getAttempt() + 1; if (log.isDebugEnabled()) { log.debug("attempt {} for command {} and params {}", count, details.getCommand(), Arrays.toString(details.getParams())); } async(details.isReadOnlyMode(), details.getSource(), details.getCodec(), details.getCommand(), details.getParams(), details.getMainPromise(), count); AsyncDetails.release(details); } }; Timeout timeout = connectionManager.newTimeout(retryTimerTask, connectionManager.getConfig().getRetryInterval(), TimeUnit.MILLISECONDS); details.setTimeout(timeout); connectionFuture.addListener(new FutureListener<RedisConnection>() { @Override public void operationComplete(Future<RedisConnection> connFuture) throws Exception { if (connFuture.isCancelled()) { return; } if (!connFuture.isSuccess()) { connectionManager.getShutdownLatch().release(); details.setException(convertException(connectionFuture)); return; } if (details.getAttemptPromise().isDone() || details.getMainPromise().isDone()) { releaseConnection(source, connectionFuture, details.isReadOnlyMode(), details.getAttemptPromise(), details); return; } final RedisConnection connection = connFuture.getNow(); if (details.getSource().getRedirect() == Redirect.ASK) { List<CommandData<?, ?>> list = new ArrayList<CommandData<?, ?>>(2); RPromise<Void> promise = connectionManager.newPromise(); list.add(new CommandData<Void, Void>(promise, details.getCodec(), RedisCommands.ASKING, new Object[] {})); list.add(new CommandData<V, R>(details.getAttemptPromise(), details.getCodec(), details.getCommand(), details.getParams())); RPromise<Void> main = connectionManager.newPromise(); ChannelFuture future = connection.send(new CommandsData(main, list)); details.setWriteFuture(future); } else { if (log.isDebugEnabled()) { log.debug("aquired connection for command {} and params {} from slot {} using node {}", details.getCommand(), Arrays.toString(details.getParams()), details.getSource(), connection.getRedisClient().getAddr()); } ChannelFuture future = connection.send(new CommandData<V, R>(details.getAttemptPromise(), details.getCodec(), details.getCommand(), details.getParams())); details.setWriteFuture(future); } details.getWriteFuture().addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { checkWriteFuture(details, connection); } }); releaseConnection(source, connectionFuture, details.isReadOnlyMode(), details.getAttemptPromise(), details); } }); attemptPromise.addListener(new FutureListener<R>() { @Override public void operationComplete(Future<R> future) throws Exception { checkAttemptFuture(source, details, future); } }); } private <V, R> void checkWriteFuture(final AsyncDetails<V, R> details, final RedisConnection connection) { ChannelFuture future = details.getWriteFuture(); if (details.getAttemptPromise().isDone() || future.isCancelled()) { return; } if (!future.isSuccess()) { details.setException(new WriteRedisConnectionException( "Can't write command: " + details.getCommand() + ", params: " + LogHelper.toString(details.getParams()) + " to channel: " + future.channel(), future.cause())); return; } details.getTimeout().cancel(); long timeoutTime = connectionManager.getConfig().getTimeout(); if (RedisCommands.BLOCKING_COMMANDS.contains(details.getCommand().getName())) { Long popTimeout = Long.valueOf(details.getParams()[details.getParams().length - 1].toString()); handleBlockingOperations(details, connection, popTimeout); if (popTimeout == 0) { return; } timeoutTime += popTimeout * 1000; // add 1 second due to issue https://github.com/antirez/redis/issues/874 timeoutTime += 1000; } final long timeoutAmount = timeoutTime; TimerTask timeoutTask = new TimerTask() { @Override public void run(Timeout timeout) throws Exception { details.getAttemptPromise().tryFailure(new RedisTimeoutException("Redis server response timeout (" + timeoutAmount + " ms) occured for command: " + details.getCommand() + " with params: " + LogHelper.toString(details.getParams()) + " channel: " + connection.getChannel())); } }; Timeout timeout = connectionManager.newTimeout(timeoutTask, timeoutTime, TimeUnit.MILLISECONDS); details.setTimeout(timeout); } private <R, V> void handleBlockingOperations(final AsyncDetails<V, R> details, final RedisConnection connection, Long popTimeout) { final FutureListener<Boolean> listener = new FutureListener<Boolean>() { @Override public void operationComplete(Future<Boolean> future) throws Exception { details.getMainPromise().tryFailure(new RedissonShutdownException("Redisson is shutdown")); } }; final AtomicBoolean canceledByScheduler = new AtomicBoolean(); final Timeout scheduledFuture; if (popTimeout != 0) { // to handle cases when connection has been lost final Channel orignalChannel = connection.getChannel(); scheduledFuture = connectionManager.newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { // re-connection wasn't made // and connection is still active if (orignalChannel == connection.getChannel() && connection.isActive()) { return; } canceledByScheduler.set(true); details.getAttemptPromise().trySuccess(null); } }, popTimeout, TimeUnit.SECONDS); } else { scheduledFuture = null; } details.getMainPromise().addListener(new FutureListener<R>() { @Override public void operationComplete(Future<R> future) throws Exception { if (scheduledFuture != null) { scheduledFuture.cancel(); } synchronized (listener) { connectionManager.getShutdownPromise().removeListener(listener); } // handling cancel operation for commands from skipTimeout collection if ((future.isCancelled() && details.getAttemptPromise().cancel(true)) || canceledByScheduler.get()) { connection.forceFastReconnectAsync(); return; } if (future.cause() instanceof RedissonShutdownException) { details.getAttemptPromise().tryFailure(future.cause()); } } }); synchronized (listener) { if (!details.getMainPromise().isDone()) { connectionManager.getShutdownPromise().addListener(listener); } } } protected <V, R> void releaseConnection(final NodeSource source, final RFuture<RedisConnection> connectionFuture, final boolean isReadOnly, RPromise<R> attemptPromise, final AsyncDetails<V, R> details) { attemptPromise.addListener(new FutureListener<R>() { @Override public void operationComplete(Future<R> future) throws Exception { if (!connectionFuture.isSuccess()) { return; } RedisConnection connection = connectionFuture.getNow(); connectionManager.getShutdownLatch().release(); if (isReadOnly) { connectionManager.releaseRead(source, connection); } else { connectionManager.releaseWrite(source, connection); } if (log.isDebugEnabled()) { log.debug("connection released for command {} and params {} from slot {} using connection {}", details.getCommand(), Arrays.toString(details.getParams()), details.getSource(), connection); } } }); } private <R, V> void checkAttemptFuture(final NodeSource source, final AsyncDetails<V, R> details, Future<R> future) { details.getTimeout().cancel(); if (future.isCancelled()) { return; } if (future.cause() instanceof RedisMovedException) { RedisMovedException ex = (RedisMovedException) future.cause(); async(details.isReadOnlyMode(), new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.MOVED), details.getCodec(), details.getCommand(), details.getParams(), details.getMainPromise(), details.getAttempt()); AsyncDetails.release(details); return; } if (future.cause() instanceof RedisAskException) { RedisAskException ex = (RedisAskException) future.cause(); async(details.isReadOnlyMode(), new NodeSource(ex.getSlot(), ex.getAddr(), Redirect.ASK), details.getCodec(), details.getCommand(), details.getParams(), details.getMainPromise(), details.getAttempt()); AsyncDetails.release(details); return; } if (future.cause() instanceof RedisLoadingException) { async(details.isReadOnlyMode(), source, details.getCodec(), details.getCommand(), details.getParams(), details.getMainPromise(), details.getAttempt()); AsyncDetails.release(details); return; } if (future.cause() instanceof RedisTryAgainException) { connectionManager.newTimeout(new TimerTask() { @Override public void run(Timeout timeout) throws Exception { async(details.isReadOnlyMode(), source, details.getCodec(), details.getCommand(), details.getParams(), details.getMainPromise(), details.getAttempt()); } }, 1, TimeUnit.SECONDS); AsyncDetails.release(details); return; } if (future.isSuccess()) { R res = future.getNow(); if (res instanceof RedisClientResult) { InetSocketAddress addr = source.getAddr(); if (addr == null) { addr = details.getConnectionFuture().getNow().getRedisClient().getAddr(); } ((RedisClientResult) res).setRedisClient(addr); } if (isRedissonReferenceSupportEnabled()) { handleReference(details.getMainPromise(), res); } else { details.getMainPromise().trySuccess(res); } } else { details.getMainPromise().tryFailure(future.cause()); } AsyncDetails.release(details); } private <R, V> void handleReference(RPromise<R> mainPromise, R res) { if (res instanceof List) { List<Object> r = (List<Object>) res; for (int i = 0; i < r.size(); i++) { if (r.get(i) instanceof RedissonReference) { try { r.set(i, redisson != null ? RedissonObjectFactory.fromReference(redisson, (RedissonReference) r.get(i)) : RedissonObjectFactory.fromReference(redissonReactive, (RedissonReference) r.get(i))); } catch (Exception exception) {//skip and carry on to next one. } } else if (r.get(i) instanceof ScoredEntry && ((ScoredEntry) r.get(i)).getValue() instanceof RedissonReference) { try { ScoredEntry<?> se = ((ScoredEntry<?>) r.get(i)); se = new ScoredEntry(se.getScore(), redisson != null ? RedissonObjectFactory.<R>fromReference(redisson, (RedissonReference) se.getValue()) : RedissonObjectFactory.<R>fromReference(redissonReactive, (RedissonReference) se.getValue())); r.set(i, se); } catch (Exception exception) {//skip and carry on to next one. } } } mainPromise.trySuccess(res); } else if (res instanceof ListScanResult) { List<ScanObjectEntry> r = ((ListScanResult) res).getValues(); for (int i = 0; i < r.size(); i++) { Object obj = r.get(i); if (!(obj instanceof ScanObjectEntry)) { break; } ScanObjectEntry e = r.get(i); if (e.getObj() instanceof RedissonReference) { try { r.set(i, new ScanObjectEntry(e.getBuf(), redisson != null ? RedissonObjectFactory.<R>fromReference(redisson, (RedissonReference) e.getObj()) : RedissonObjectFactory.<R>fromReference(redissonReactive, (RedissonReference) e.getObj()))); } catch (Exception exception) {//skip and carry on to next one. } } else if (e.getObj() instanceof ScoredEntry && ((ScoredEntry<?>) e.getObj()).getValue() instanceof RedissonReference) { try { ScoredEntry<?> se = ((ScoredEntry<?>) e.getObj()); se = new ScoredEntry(se.getScore(), redisson != null ? RedissonObjectFactory.<R>fromReference(redisson, (RedissonReference) se.getValue()) : RedissonObjectFactory.<R>fromReference(redissonReactive, (RedissonReference) se.getValue())); r.set(i, new ScanObjectEntry(e.getBuf(), se)); } catch (Exception exception) {//skip and carry on to next one. } } } mainPromise.trySuccess(res); } else if (res instanceof MapScanResult) { Map<ScanObjectEntry, ScanObjectEntry> map = ((MapScanResult) res).getMap(); HashMap<ScanObjectEntry, ScanObjectEntry> toAdd = null; for (Map.Entry<ScanObjectEntry, ScanObjectEntry> e : map.entrySet()) { if (e.getValue().getObj() instanceof RedissonReference) { try { e.setValue(new ScanObjectEntry(e.getValue().getBuf(), redisson != null ? RedissonObjectFactory.<R>fromReference(redisson, (RedissonReference) e.getValue().getObj()) : RedissonObjectFactory.<R>fromReference(redissonReactive, (RedissonReference) e.getValue().getObj()))); } catch (Exception exception) {//skip and carry on to next one. } } if (e.getKey().getObj() instanceof RedissonReference) { if (toAdd == null) { toAdd = new HashMap<ScanObjectEntry, ScanObjectEntry>(); } toAdd.put(e.getKey(), e.getValue()); } } if (toAdd != null) { for (Map.Entry<ScanObjectEntry, ScanObjectEntry> e : toAdd.entrySet()) { try { map.put(new ScanObjectEntry(e.getValue().getBuf(), (redisson != null ? RedissonObjectFactory.<R>fromReference(redisson, (RedissonReference) e.getKey().getObj()) : RedissonObjectFactory.<R>fromReference(redissonReactive, (RedissonReference) e.getKey().getObj()))), map.remove(e.getKey())); } catch (Exception exception) {//skip and carry on to next one. } } } mainPromise.trySuccess(res); } else if (res instanceof RedissonReference) { try { mainPromise.trySuccess(redisson != null ? RedissonObjectFactory.<R>fromReference(redisson, (RedissonReference) res) : RedissonObjectFactory.<R>fromReference(redissonReactive, (RedissonReference) res)); } catch (Exception exception) { mainPromise.trySuccess(res);//fallback } } else { mainPromise.trySuccess(res); } } }