com.lambdaworks.redis.cluster.ClusterScanSupport Maven / Gradle / Ivy
/*
* Copyright 2011-2016 the original author or authors.
*
* 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 com.lambdaworks.redis.cluster;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import com.lambdaworks.redis.*;
import com.lambdaworks.redis.cluster.api.StatefulRedisClusterConnection;
import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode;
import com.lambdaworks.redis.models.role.RedisNodeDescription;
import reactor.core.publisher.Mono;
/**
* Methods to support a Cluster-wide SCAN operation over multiple hosts.
*
* @author Mark Paluch
*/
class ClusterScanSupport {
/**
* Map a {@link RedisFuture} of {@link KeyScanCursor} to a {@link RedisFuture} of {@link ClusterKeyScanCursor}.
*/
static final ScanCursorMapper>> futureKeyScanCursorMapper = new ScanCursorMapper>>() {
@Override
public RedisFuture> map(List nodeIds, String currentNodeId,
RedisFuture> cursor) {
return new PipelinedRedisFuture<>(cursor, new Function, KeyScanCursor>>() {
@Override
public KeyScanCursor> apply(KeyScanCursor> result) {
return new ClusterKeyScanCursor<>(nodeIds, currentNodeId, result);
}
});
}
};
/**
* Map a {@link RedisFuture} of {@link StreamScanCursor} to a {@link RedisFuture} of {@link ClusterStreamScanCursor}.
*/
static final ScanCursorMapper> futureStreamScanCursorMapper = new ScanCursorMapper>() {
@Override
public RedisFuture map(List nodeIds, String currentNodeId,
RedisFuture cursor) {
return new PipelinedRedisFuture<>(cursor, new Function() {
@Override
public StreamScanCursor apply(StreamScanCursor result) {
return new ClusterStreamScanCursor(nodeIds, currentNodeId, result);
}
});
}
};
/**
* Map a {@link Mono} of {@link KeyScanCursor} to a {@link Mono} of {@link ClusterKeyScanCursor}.
*/
static final ScanCursorMapper>> reactiveKeyScanCursorMapper = (nodeIds, currentNodeId,
cursor) -> cursor.map(keyScanCursor -> new ClusterKeyScanCursor<>(nodeIds, currentNodeId, keyScanCursor));
/**
* Map a {@link Mono} of {@link StreamScanCursor} to a {@link Mono} of {@link ClusterStreamScanCursor}.
*/
static final ScanCursorMapper> reactiveStreamScanCursorMapper = (nodeIds, currentNodeId,
cursor) -> cursor.map(new Function() {
@Override
public StreamScanCursor apply(StreamScanCursor streamScanCursor) {
return new ClusterStreamScanCursor(nodeIds, currentNodeId, streamScanCursor);
}
});
/**
* Retrieve the cursor to continue the scan.
*
* @param scanCursor can be {@literal null}.
* @return
*/
static ScanCursor getContinuationCursor(ScanCursor scanCursor) {
if (ScanCursor.INITIAL.equals(scanCursor)) {
return scanCursor;
}
assertClusterScanCursor(scanCursor);
ClusterScanCursor clusterScanCursor = (ClusterScanCursor) scanCursor;
if (clusterScanCursor.isScanOnCurrentNodeFinished()) {
return ScanCursor.INITIAL;
}
return scanCursor;
}
static List getNodeIds(StatefulRedisClusterConnection connection, ScanCursor cursor) {
if (ScanCursor.INITIAL.equals(cursor)) {
List nodeIds = getNodeIds(connection);
assertHasNodes(nodeIds);
return nodeIds;
}
assertClusterScanCursor(cursor);
ClusterScanCursor clusterScanCursor = (ClusterScanCursor) cursor;
return clusterScanCursor.getNodeIds();
}
static String getCurrentNodeId(ScanCursor cursor, List nodeIds) {
if (ScanCursor.INITIAL.equals(cursor)) {
assertHasNodes(nodeIds);
return nodeIds.get(0);
}
assertClusterScanCursor(cursor);
return getNodeIdForNextScanIteration(nodeIds, (ClusterScanCursor) cursor);
}
/**
* Retrieve a list of node Ids to use for the SCAN operation.
*
* @param connection
* @return
*/
private static List getNodeIds(StatefulRedisClusterConnection, ?> connection) {
List nodeIds = new ArrayList<>();
PartitionAccessor partitionAccessor = new PartitionAccessor(connection.getPartitions());
for (RedisClusterNode redisClusterNode : partitionAccessor.getMasters()) {
if (connection.getReadFrom() != null) {
List readCandidates = (List) partitionAccessor.getReadCandidates(redisClusterNode);
List selection = connection.getReadFrom().select(new ReadFrom.Nodes() {
@Override
public List getNodes() {
return readCandidates;
}
@Override
public Iterator iterator() {
return readCandidates.iterator();
}
});
if (!selection.isEmpty()) {
RedisClusterNode selectedNode = (RedisClusterNode) selection.get(0);
nodeIds.add(selectedNode.getNodeId());
continue;
}
}
nodeIds.add(redisClusterNode.getNodeId());
}
return nodeIds;
}
private static String getNodeIdForNextScanIteration(List nodeIds, ClusterScanCursor clusterKeyScanCursor) {
if (clusterKeyScanCursor.isScanOnCurrentNodeFinished()) {
if (clusterKeyScanCursor.isFinished()) {
throw new IllegalStateException("Cluster scan is finished");
}
int nodeIndex = nodeIds.indexOf(clusterKeyScanCursor.getCurrentNodeId());
return nodeIds.get(nodeIndex + 1);
}
return clusterKeyScanCursor.getCurrentNodeId();
}
private static void assertClusterScanCursor(ScanCursor cursor) {
if (!(cursor instanceof ClusterScanCursor)) {
throw new IllegalArgumentException(
"A scan in Redis Cluster mode requires to reuse the resulting cursor from the previous scan invocation");
}
}
private static void assertHasNodes(List nodeIds) {
if (nodeIds.isEmpty()) {
throw new RedisException("No available nodes for a scan");
}
}
static ScanCursorMapper>> asyncClusterKeyScanCursorMapper() {
return (ScanCursorMapper) futureKeyScanCursorMapper;
}
static ScanCursorMapper> asyncClusterStreamScanCursorMapper() {
return futureStreamScanCursorMapper;
}
static ScanCursorMapper>> reactiveClusterKeyScanCursorMapper() {
return (ScanCursorMapper) reactiveKeyScanCursorMapper;
}
static ScanCursorMapper> reactiveClusterStreamScanCursorMapper() {
return reactiveStreamScanCursorMapper;
}
/**
* Mapper between the node operation cursor and the cluster scan cursor.
*
* @param
*/
interface ScanCursorMapper {
T map(List nodeIds, String currentNodeId, T cursor);
}
/**
* Marker for a cluster scan cursor.
*/
interface ClusterScanCursor {
List getNodeIds();
String getCurrentNodeId();
boolean isScanOnCurrentNodeFinished();
boolean isFinished();
}
/**
* State object for a cluster-wide SCAN using Key results.
*
* @param
*/
private static class ClusterKeyScanCursor extends KeyScanCursor implements ClusterScanCursor {
final List nodeIds;
final String currentNodeId;
final KeyScanCursor cursor;
public ClusterKeyScanCursor(List nodeIds, String currentNodeId, KeyScanCursor cursor) {
super();
this.nodeIds = nodeIds;
this.currentNodeId = currentNodeId;
this.cursor = cursor;
setCursor(cursor.getCursor());
getKeys().addAll(cursor.getKeys());
if (cursor.isFinished()) {
int nodeIndex = nodeIds.indexOf(currentNodeId);
if (nodeIndex == -1 || nodeIndex == nodeIds.size() - 1) {
setFinished(true);
}
}
}
@Override
public List getNodeIds() {
return nodeIds;
}
@Override
public String getCurrentNodeId() {
return currentNodeId;
}
public boolean isScanOnCurrentNodeFinished() {
return cursor.isFinished();
}
}
/**
* State object for a cluster-wide SCAN using streaming.
*/
private static class ClusterStreamScanCursor extends StreamScanCursor implements ClusterScanCursor {
final List nodeIds;
final String currentNodeId;
final StreamScanCursor cursor;
public ClusterStreamScanCursor(List nodeIds, String currentNodeId, StreamScanCursor cursor) {
super();
this.nodeIds = nodeIds;
this.currentNodeId = currentNodeId;
this.cursor = cursor;
setCursor(cursor.getCursor());
setCount(cursor.getCount());
if (cursor.isFinished()) {
int nodeIndex = nodeIds.indexOf(currentNodeId);
if (nodeIndex == -1 || nodeIndex == nodeIds.size() - 1) {
setFinished(true);
}
}
}
@Override
public List getNodeIds() {
return nodeIds;
}
@Override
public String getCurrentNodeId() {
return currentNodeId;
}
public boolean isScanOnCurrentNodeFinished() {
return cursor.isFinished();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy