use of io.lettuce.core.cluster.ClusterConnectionProvider.Intent in project lettuce-core by lettuce-io.
the class ClusterDistributionChannelWriter method getIntent.
/**
* Optimization: Determine command intents and optimize for bulk execution preferring one node.
* <p>
* If there is only one intent, then we take the intent derived from the commands. If there is more than one intent, then
* use {@link Intent#WRITE}.
*
* @param commands {@link Collection} of {@link RedisCommand commands}.
* @return the intent.
*/
static Intent getIntent(Collection<? extends RedisCommand<?, ?, ?>> commands) {
boolean w = false;
boolean r = false;
Intent singleIntent = Intent.WRITE;
for (RedisCommand<?, ?, ?> command : commands) {
if (command instanceof ClusterCommand) {
continue;
}
singleIntent = getIntent(command.getType());
if (singleIntent == Intent.READ) {
r = true;
}
if (singleIntent == Intent.WRITE) {
w = true;
}
if (r && w) {
return Intent.WRITE;
}
}
return singleIntent;
}
use of io.lettuce.core.cluster.ClusterConnectionProvider.Intent in project lettuce-core by lettuce-io.
the class ClusterDistributionChannelWriter method write.
@SuppressWarnings("unchecked")
@Override
public <K, V> Collection<RedisCommand<K, V, ?>> write(Collection<? extends RedisCommand<K, V, ?>> commands) {
LettuceAssert.notNull(commands, "Commands must not be null");
if (closed) {
commands.forEach(it -> it.completeExceptionally(new RedisException("Connection is closed")));
return (Collection<RedisCommand<K, V, ?>>) commands;
}
List<ClusterCommand<K, V, ?>> clusterCommands = new ArrayList<>(commands.size());
List<ClusterCommand<K, V, ?>> defaultCommands = new ArrayList<>(commands.size());
Map<SlotIntent, List<ClusterCommand<K, V, ?>>> partitions = new HashMap<>();
// TODO: Retain order or retain Intent preference?
// Currently: Retain order
Intent intent = getIntent(commands);
for (RedisCommand<K, V, ?> cmd : commands) {
if (cmd instanceof ClusterCommand) {
clusterCommands.add((ClusterCommand) cmd);
continue;
}
CommandArgs<K, V> args = cmd.getArgs();
ByteBuffer firstEncodedKey = args != null ? args.getFirstEncodedKey() : null;
if (firstEncodedKey == null) {
defaultCommands.add(new ClusterCommand<>(cmd, this, executionLimit));
continue;
}
int hash = getSlot(args.getFirstEncodedKey());
List<ClusterCommand<K, V, ?>> commandPartition = partitions.computeIfAbsent(SlotIntent.of(intent, hash), slotIntent -> new ArrayList<>());
commandPartition.add(new ClusterCommand<>(cmd, this, executionLimit));
}
for (Map.Entry<SlotIntent, List<ClusterCommand<K, V, ?>>> entry : partitions.entrySet()) {
SlotIntent slotIntent = entry.getKey();
RedisChannelHandler<K, V> connection = (RedisChannelHandler<K, V>) clusterConnectionProvider.getConnection(slotIntent.intent, slotIntent.slotHash);
RedisChannelWriter channelWriter = connection.getChannelWriter();
if (channelWriter instanceof ClusterDistributionChannelWriter) {
ClusterDistributionChannelWriter writer = (ClusterDistributionChannelWriter) channelWriter;
channelWriter = writer.defaultWriter;
}
if (channelWriter != null && channelWriter != this && channelWriter != defaultWriter) {
channelWriter.write(entry.getValue());
}
}
clusterCommands.forEach(this::write);
defaultCommands.forEach(defaultWriter::write);
return (Collection) commands;
}
use of io.lettuce.core.cluster.ClusterConnectionProvider.Intent in project lettuce-core by lettuce-io.
the class ClusterDistributionChannelWriter method doWrite.
private <K, V, T> RedisCommand<K, V, T> doWrite(RedisCommand<K, V, T> command) {
if (command instanceof ClusterCommand && !command.isDone()) {
ClusterCommand<K, V, T> clusterCommand = (ClusterCommand<K, V, T>) command;
if (clusterCommand.isMoved() || clusterCommand.isAsk()) {
HostAndPort target;
boolean asking;
ByteBuffer firstEncodedKey = clusterCommand.getArgs().getFirstEncodedKey();
String keyAsString = null;
int slot = -1;
if (firstEncodedKey != null) {
firstEncodedKey.mark();
keyAsString = StringCodec.UTF8.decodeKey(firstEncodedKey);
firstEncodedKey.reset();
slot = getSlot(firstEncodedKey);
}
if (clusterCommand.isMoved()) {
target = getMoveTarget(clusterCommand.getError());
clusterEventListener.onMovedRedirection();
asking = false;
publish(new MovedRedirectionEvent(clusterCommand.getType().name(), keyAsString, slot, clusterCommand.getError()));
} else {
target = getAskTarget(clusterCommand.getError());
asking = true;
clusterEventListener.onAskRedirection();
publish(new AskRedirectionEvent(clusterCommand.getType().name(), keyAsString, slot, clusterCommand.getError()));
}
command.getOutput().setError((String) null);
CompletableFuture<StatefulRedisConnection<K, V>> connectFuture = asyncClusterConnectionProvider.getConnectionAsync(Intent.WRITE, target.getHostText(), target.getPort());
if (isSuccessfullyCompleted(connectFuture)) {
writeCommand(command, asking, connectFuture.join(), null);
} else {
connectFuture.whenComplete((connection, throwable) -> writeCommand(command, asking, connection, throwable));
}
return command;
}
}
ClusterCommand<K, V, T> commandToSend = getCommandToSend(command);
CommandArgs<K, V> args = command.getArgs();
// exclude CLIENT commands from cluster routing
if (args != null && !CommandType.CLIENT.equals(commandToSend.getType())) {
ByteBuffer encodedKey = args.getFirstEncodedKey();
if (encodedKey != null) {
int hash = getSlot(encodedKey);
Intent intent = getIntent(command.getType());
CompletableFuture<StatefulRedisConnection<K, V>> connectFuture = ((AsyncClusterConnectionProvider) clusterConnectionProvider).getConnectionAsync(intent, hash);
if (isSuccessfullyCompleted(connectFuture)) {
writeCommand(commandToSend, false, connectFuture.join(), null);
} else {
connectFuture.whenComplete((connection, throwable) -> writeCommand(commandToSend, false, connection, throwable));
}
return commandToSend;
}
}
writeCommand(commandToSend, defaultWriter);
return commandToSend;
}
Aggregations