package com.lambdaworks.redis.cluster;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Predicate;
import com.lambdaworks.redis.RedisCommandInterruptedException;
import com.lambdaworks.redis.RedisException;
import com.lambdaworks.redis.RedisFuture;
import com.lambdaworks.redis.api.StatefulConnection;
import com.lambdaworks.redis.api.sync.RedisCommands;
import com.lambdaworks.redis.cluster.api.NodeSelectionSupport;
import com.lambdaworks.redis.cluster.api.StatefulRedisClusterConnection;
import com.lambdaworks.redis.cluster.api.sync.NodeSelection;
import com.lambdaworks.redis.cluster.api.sync.NodeSelectionCommands;
import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode;
/**
* Utility to perform and synchronize command executions on multiple cluster nodes.
*
* @author Mark Paluch
*/
class MultiNodeExecution {
static <T> T execute(Callable<T> function) {
try {
return function.call();
} catch (Exception e) {
throw new RedisException(e);
}
}
/**
* Aggregate (sum) results of the {@link RedisFuture}s.
*
* @param executions mapping of a key to the future
* @return future producing an aggregation result
*/
protected static RedisFuture<Long> aggregateAsync(Map<?, RedisFuture<Long>> executions) {
return new PipelinedRedisFuture<>(executions, objectPipelinedRedisFuture -> {
AtomicLong result = new AtomicLong();
for (RedisFuture<Long> future : executions.values()) {
Long value = execute(() -> future.get());
if (value != null) {
result.getAndAdd(value);
}
}
return result.get();
});
}
/**
* Returns the result of the first {@link RedisFuture} and guarantee that all futures are finished.
*
* @param executions mapping of a key to the future
* @param <T> result type
* @return future returning the first result.
*/
protected static <T> RedisFuture<T> firstOfAsync(Map<?, RedisFuture<T>> executions) {
return new PipelinedRedisFuture<>(executions, objectPipelinedRedisFuture -> {
// make sure, that all futures are executed before returning the result.
for (RedisFuture<T> future : executions.values()) {
execute(() -> future.get());
}
for (RedisFuture<T> future : executions.values()) {
return execute(() -> future.get());
}
return null;
});
}
/**
* Returns always {@literal OK} and guarantee that all futures are finished.
*
* @param executions mapping of a key to the future
* @return future returning the first result.
*/
protected static RedisFuture<String> alwaysOkOfAsync(Map<?, RedisFuture<String>> executions) {
return new PipelinedRedisFuture<>(executions, objectPipelinedRedisFuture -> {
// make sure, that all futures are executed before returning the result.
for (RedisFuture<String> future : executions.values()) {
try {
future.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RedisCommandInterruptedException(e);
} catch (ExecutionException e) {
// swallow exceptions
}
}
return "OK";
} );
}
}