use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class RedisClusterClient method connectClusterPubSubAsync.
/**
* Create a clustered connection with command distributor.
*
* @param codec Use this codec to encode/decode keys and values, must not be {@code null}
* @param <K> Key type
* @param <V> Value type
* @return a new connection
*/
private <K, V> CompletableFuture<StatefulRedisClusterPubSubConnection<K, V>> connectClusterPubSubAsync(RedisCodec<K, V> codec) {
if (partitions == null) {
return Futures.failed(new IllegalStateException("Partitions not initialized. Initialize via RedisClusterClient.getPartitions()."));
}
topologyRefreshScheduler.activateTopologyRefreshIfNeeded();
logger.debug("connectClusterPubSub(" + initialUris + ")");
PubSubClusterEndpoint<K, V> endpoint = new PubSubClusterEndpoint<>(getClusterClientOptions(), getResources());
RedisChannelWriter writer = endpoint;
if (CommandExpiryWriter.isSupported(getClusterClientOptions())) {
writer = new CommandExpiryWriter(writer, getClusterClientOptions(), getResources());
}
if (CommandListenerWriter.isSupported(getCommandListeners())) {
writer = new CommandListenerWriter(writer, getCommandListeners());
}
ClusterDistributionChannelWriter clusterWriter = new ClusterDistributionChannelWriter(writer, getClusterClientOptions(), topologyRefreshScheduler);
ClusterPubSubConnectionProvider<K, V> pooledClusterConnectionProvider = new ClusterPubSubConnectionProvider<>(this, clusterWriter, codec, endpoint.getUpstreamListener(), topologyRefreshScheduler);
StatefulRedisClusterPubSubConnectionImpl<K, V> connection = new StatefulRedisClusterPubSubConnectionImpl<>(endpoint, pooledClusterConnectionProvider, clusterWriter, codec, getFirstUri().getTimeout());
clusterWriter.setClusterConnectionProvider(pooledClusterConnectionProvider);
connection.setPartitions(partitions);
Supplier<CommandHandler> commandHandlerSupplier = () -> new PubSubCommandHandler<>(getClusterClientOptions(), getResources(), codec, endpoint);
Mono<SocketAddress> socketAddressSupplier = getSocketAddressSupplier(connection::getPartitions, TopologyComparators::sortByClientCount);
Mono<StatefulRedisClusterPubSubConnectionImpl<K, V>> connectionMono = Mono.defer(() -> connect(socketAddressSupplier, endpoint, connection, commandHandlerSupplier));
for (int i = 1; i < getConnectionAttempts(); i++) {
connectionMono = connectionMono.onErrorResume(t -> connect(socketAddressSupplier, endpoint, connection, commandHandlerSupplier));
}
return connectionMono.doOnNext(c -> connection.registerCloseables(closeableResources, clusterWriter, pooledClusterConnectionProvider)).map(it -> (StatefulRedisClusterPubSubConnection<K, V>) it).toFuture();
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class DefaultClusterTopologyRefresh method loadViews.
/**
* Load partition views from a collection of {@link RedisURI}s and return the view per {@link RedisURI}. Partitions contain
* an ordered list of {@link RedisClusterNode}s. The sort key is latency. Nodes with lower latency come first.
*
* @param seed collection of {@link RedisURI}s
* @param connectTimeout connect timeout
* @param discovery {@code true} to discover additional nodes
* @return mapping between {@link RedisURI} and {@link Partitions}
*/
@Override
public CompletionStage<Map<RedisURI, Partitions>> loadViews(Iterable<RedisURI> seed, Duration connectTimeout, boolean discovery) {
if (!isEventLoopActive()) {
return CompletableFuture.completedFuture(Collections.emptyMap());
}
long commandTimeoutNs = getCommandTimeoutNs(seed);
ConnectionTracker tracker = new ConnectionTracker();
long connectionTimeout = commandTimeoutNs + connectTimeout.toNanos();
openConnections(tracker, seed, connectionTimeout, TimeUnit.NANOSECONDS);
CompletableFuture<NodeTopologyViews> composition = tracker.whenComplete(map -> {
return new Connections(clientResources, map);
}).thenCompose(connections -> {
Requests requestedTopology = connections.requestTopology(commandTimeoutNs, TimeUnit.NANOSECONDS);
Requests requestedInfo = connections.requestInfo(commandTimeoutNs, TimeUnit.NANOSECONDS);
return CompletableFuture.allOf(requestedTopology.allCompleted(), requestedInfo.allCompleted()).thenApplyAsync(ignore -> getNodeSpecificViews(requestedTopology, requestedInfo), clientResources.eventExecutorGroup()).thenCompose(views -> {
if (discovery && isEventLoopActive()) {
Set<RedisURI> allKnownUris = views.getClusterNodes();
Set<RedisURI> discoveredNodes = difference(allKnownUris, toSet(seed));
if (discoveredNodes.isEmpty()) {
return CompletableFuture.completedFuture(views);
}
openConnections(tracker, discoveredNodes, connectionTimeout, TimeUnit.NANOSECONDS);
return tracker.whenComplete(map -> {
return new Connections(clientResources, map).retainAll(discoveredNodes);
}).thenCompose(newConnections -> {
Requests additionalTopology = newConnections.requestTopology(commandTimeoutNs, TimeUnit.NANOSECONDS).mergeWith(requestedTopology);
Requests additionalInfo = newConnections.requestInfo(commandTimeoutNs, TimeUnit.NANOSECONDS).mergeWith(requestedInfo);
return CompletableFuture.allOf(additionalTopology.allCompleted(), additionalInfo.allCompleted()).thenApplyAsync(ignore2 -> getNodeSpecificViews(additionalTopology, additionalInfo), clientResources.eventExecutorGroup());
});
}
return CompletableFuture.completedFuture(views);
}).whenComplete((ignore, throwable) -> {
if (throwable != null) {
try {
tracker.close();
} catch (Exception e) {
logger.debug("Cannot close ClusterTopologyRefresh connections", e);
}
}
}).thenCompose((it) -> tracker.close().thenApply(ignore -> it)).thenCompose(it -> {
if (it.isEmpty()) {
Exception exception = tryFail(requestedTopology, tracker, seed);
return Futures.failed(exception);
}
return CompletableFuture.completedFuture(it);
});
});
return composition.thenApply(NodeTopologyViews::toMap);
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class RoundRobinSocketAddressSupplier method get.
@Override
public SocketAddress get() {
Partitions partitions = this.partitions.get();
if (!roundRobin.isConsistent(partitions)) {
resetRoundRobin(partitions);
}
RedisClusterNode redisClusterNode = roundRobin.next();
return getSocketAddress(redisClusterNode);
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class AdvancedClusterClientIntegrationTests method routeCommandToForbiddenHostOnRedirect.
@Test
@Inject
void routeCommandToForbiddenHostOnRedirect(@Connection(requiresNew = true) StatefulRedisClusterConnection<String, String> connectionUnderTest) {
RedisAdvancedClusterCommands<String, String> sync = connectionUnderTest.sync();
try {
Partitions partitions = clusterClient.getPartitions();
for (RedisClusterNode partition : partitions) {
partition.setSlots(Collections.singletonList(0));
if (partition.getUri().getPort() == 7380) {
partition.setSlots(Collections.singletonList(6373));
} else {
partition.setUri(RedisURI.create("redis://non.existent.host:1234"));
}
}
partitions.updateCache();
// 6373
sync.set("A", "value");
} catch (Exception e) {
assertThat(e).isInstanceOf(RedisException.class).hasMessageContaining("not allowed");
} finally {
clusterClient.getPartitions().clear();
clusterClient.reloadPartitions();
}
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class AdvancedClusterClientIntegrationTests method routeCommandToNoAddrPartition.
@Test
@Inject
void routeCommandToNoAddrPartition(@New StatefulRedisClusterConnection<String, String> connectionUnderTest) {
RedisAdvancedClusterCommands<String, String> sync = connectionUnderTest.sync();
try {
Partitions partitions = clusterClient.getPartitions();
for (RedisClusterNode partition : partitions) {
partition.setUri(RedisURI.create("redis://non.existent.host:1234"));
}
// 6373
sync.set("A", "value");
} catch (Exception e) {
assertThat(e).isInstanceOf(RedisException.class).hasMessageContaining("Unable to connect to");
} finally {
clusterClient.getPartitions().clear();
clusterClient.reloadPartitions();
}
}
Aggregations