use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class TopologyRefreshIntegrationTests method staticSourcesProvidesClientCountForSeedNodes.
@Test
void staticSourcesProvidesClientCountForSeedNodes() {
ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder().dynamicRefreshSources(false).build();
clusterClient.setOptions(ClusterClientOptions.builder().topologyRefreshOptions(topologyRefreshOptions).build());
RedisAdvancedClusterAsyncCommands<String, String> clusterConnection = clusterClient.connect().async();
Partitions partitions = clusterClient.getPartitions();
RedisClusterNodeSnapshot node1 = (RedisClusterNodeSnapshot) partitions.getPartitionBySlot(0);
assertThat(node1.getConnectedClients()).isGreaterThanOrEqualTo(1);
RedisClusterNodeSnapshot node2 = (RedisClusterNodeSnapshot) partitions.getPartitionBySlot(15000);
assertThat(node2.getConnectedClients()).isNull();
clusterConnection.getStatefulConnection().close();
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class PooledClusterConnectionProvider method getWriteConnection.
private CompletableFuture<StatefulRedisConnection<K, V>> getWriteConnection(int slot) {
// avoid races when reconfiguring partitions.
CompletableFuture<StatefulRedisConnection<K, V>> writer;
synchronized (stateLock) {
writer = writers[slot];
}
if (writer == null) {
RedisClusterNode partition = partitions.getPartitionBySlot(slot);
if (partition == null) {
clusterEventListener.onUncoveredSlot(slot);
return Futures.failed(new PartitionSelectorException("Cannot determine a partition for slot " + slot + ".", partitions.clone()));
}
// Use always host and port for slot-oriented operations. We don't want to get reconnected on a different
// host because the nodeId can be handled by a different host.
RedisURI uri = partition.getUri();
ConnectionKey key = new ConnectionKey(Intent.WRITE, uri.getHost(), uri.getPort());
ConnectionFuture<StatefulRedisConnection<K, V>> future = getConnectionAsync(key);
return future.thenApply(connection -> {
synchronized (stateLock) {
if (writers[slot] == null) {
writers[slot] = CompletableFuture.completedFuture(connection);
}
}
return connection;
}).toCompletableFuture();
}
return writer;
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class RedisAdvancedClusterAsyncCommandsImpl method randomkey.
@Override
public RedisFuture<K> randomkey() {
Partitions partitions = getStatefulConnection().getPartitions();
if (partitions.isEmpty()) {
return super.randomkey();
}
int index = ThreadLocalRandom.current().nextInt(partitions.size());
RedisClusterNode partition = partitions.getPartition(index);
CompletableFuture<K> future = getConnectionAsync(partition.getUri().getHost(), partition.getUri().getPort()).thenCompose(RedisKeyAsyncCommands::randomkey);
return new PipelinedRedisFuture<>(future);
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class RedisClusterClient method connectClusterAsync.
/**
* Create a clustered pub/sub 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<StatefulRedisClusterConnection<K, V>> connectClusterAsync(RedisCodec<K, V> codec) {
if (partitions == null) {
return Futures.failed(new IllegalStateException("Partitions not initialized. Initialize via RedisClusterClient.getPartitions()."));
}
topologyRefreshScheduler.activateTopologyRefreshIfNeeded();
logger.debug("connectCluster(" + initialUris + ")");
DefaultEndpoint endpoint = new DefaultEndpoint(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);
PooledClusterConnectionProvider<K, V> pooledClusterConnectionProvider = new PooledClusterConnectionProvider<>(this, clusterWriter, codec, topologyRefreshScheduler);
clusterWriter.setClusterConnectionProvider(pooledClusterConnectionProvider);
StatefulRedisClusterConnectionImpl<K, V> connection = newStatefulRedisClusterConnection(clusterWriter, pooledClusterConnectionProvider, codec, getFirstUri().getTimeout());
connection.setReadFrom(ReadFrom.UPSTREAM);
connection.setPartitions(partitions);
Supplier<CommandHandler> commandHandlerSupplier = () -> new CommandHandler(getClusterClientOptions(), getResources(), endpoint);
Mono<SocketAddress> socketAddressSupplier = getSocketAddressSupplier(connection::getPartitions, TopologyComparators::sortByClientCount);
Mono<StatefulRedisClusterConnectionImpl<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 -> (StatefulRedisClusterConnection<K, V>) it).toFuture();
}
use of io.lettuce.core.cluster.models.partitions.Partitions in project lettuce-core by lettuce-io.
the class RedisClusterClient method loadPartitionsAsync.
/**
* Retrieve partitions. Nodes within {@link Partitions} are ordered by latency. Lower latency nodes come first.
*
* @return future that emits {@link Partitions} upon a successful topology lookup.
* @since 6.0
*/
protected CompletableFuture<Partitions> loadPartitionsAsync() {
Iterable<RedisURI> topologyRefreshSource = getTopologyRefreshSource();
CompletableFuture<Partitions> future = new CompletableFuture<>();
fetchPartitions(topologyRefreshSource).whenComplete((nodes, throwable) -> {
if (throwable == null) {
future.complete(nodes);
return;
}
// Attempt recovery using initial seed nodes
if (useDynamicRefreshSources() && topologyRefreshSource != initialUris) {
fetchPartitions(initialUris).whenComplete((nextNodes, nextThrowable) -> {
if (nextThrowable != null) {
Throwable exception = Exceptions.unwrap(nextThrowable);
exception.addSuppressed(Exceptions.unwrap(throwable));
future.completeExceptionally(exception);
} else {
future.complete(nextNodes);
}
});
} else {
future.completeExceptionally(Exceptions.unwrap(throwable));
}
});
Predicate<RedisClusterNode> nodeFilter = getClusterClientOptions().getNodeFilter();
if (nodeFilter != ClusterClientOptions.DEFAULT_NODE_FILTER) {
return future.thenApply(partitions -> {
List<RedisClusterNode> toRemove = new ArrayList<>();
for (RedisClusterNode partition : partitions) {
if (!nodeFilter.test(partition)) {
toRemove.add(partition);
}
}
partitions.removeAll(toRemove);
return partitions;
});
}
return future;
}
Aggregations