package com.lambdaworks.redis.cluster.models.partitions; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Set; import com.lambdaworks.redis.RedisURI; import com.lambdaworks.redis.internal.LettuceAssert; import com.lambdaworks.redis.internal.LettuceSets; import com.lambdaworks.redis.models.role.RedisNodeDescription; /** * Representation of a Redis Cluster node. A {@link RedisClusterNode} is identified by its {@code nodeId}. A * {@link RedisClusterNode} can be a {@link #getRole() responsible master} for zero to * {@link com.lambdaworks.redis.cluster.SlotHash#SLOT_COUNT 16384} slots, a slave of one {@link #getSlaveOf() master} of carry * different {@link com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode.NodeFlag flags}. * * @author Mark Paluch * @since 3.0 */ @SuppressWarnings("serial") public class RedisClusterNode implements Serializable, RedisNodeDescription { private RedisURI uri; private String nodeId; private boolean connected; private String slaveOf; private long pingSentTimestamp; private long pongReceivedTimestamp; private long configEpoch; private List<Integer> slots; private Set<NodeFlag> flags; public RedisClusterNode() { } public RedisClusterNode(RedisURI uri, String nodeId, boolean connected, String slaveOf, long pingSentTimestamp, long pongReceivedTimestamp, long configEpoch, List<Integer> slots, Set<NodeFlag> flags) { this.uri = uri; this.nodeId = nodeId; this.connected = connected; this.slaveOf = slaveOf; this.pingSentTimestamp = pingSentTimestamp; this.pongReceivedTimestamp = pongReceivedTimestamp; this.configEpoch = configEpoch; this.slots = slots; this.flags = flags; } public RedisClusterNode(RedisClusterNode redisClusterNode) { this.uri = redisClusterNode.uri; this.nodeId = redisClusterNode.nodeId; this.connected = redisClusterNode.connected; this.slaveOf = redisClusterNode.slaveOf; this.pingSentTimestamp = redisClusterNode.pingSentTimestamp; this.pongReceivedTimestamp = redisClusterNode.pongReceivedTimestamp; this.configEpoch = redisClusterNode.configEpoch; this.slots = new ArrayList<>(redisClusterNode.slots); this.flags = LettuceSets.newHashSet(redisClusterNode.flags); } /** * Create a new instance of {@link RedisClusterNode} by passing the {@code nodeId} * * @param nodeId the nodeId * @return a new instance of {@link RedisClusterNode} */ public static RedisClusterNode of(String nodeId) { RedisClusterNode redisClusterNode = new RedisClusterNode(); redisClusterNode.setNodeId(nodeId); return redisClusterNode; } public RedisURI getUri() { return uri; } /** * Sets thhe connection point details. Usually the host/ip/port where a particular Redis Cluster node server is running. * * @param uri the {@link RedisURI}, must not be {@literal null} */ public void setUri(RedisURI uri) { LettuceAssert.notNull(uri, "RedisURI must not be null"); this.uri = uri; } public String getNodeId() { return nodeId; } /** * Sets {@code nodeId}. * * @param nodeId the {@code nodeId} */ public void setNodeId(String nodeId) { LettuceAssert.notNull(nodeId, "NodeId must not be null"); this.nodeId = nodeId; } public boolean isConnected() { return connected; } /** * Sets the {@code connected} flag. The {@code connected} flag describes whether the node which provided details about the * node is connected to the particular {@link RedisClusterNode}. * * @param connected the {@code connected} flag */ public void setConnected(boolean connected) { this.connected = connected; } public String getSlaveOf() { return slaveOf; } /** * Sets the replication source. * * @param slaveOf the replication source, can be {@literal null} */ public void setSlaveOf(String slaveOf) { this.slaveOf = slaveOf; } public long getPingSentTimestamp() { return pingSentTimestamp; } /** * Sets the last {@code pingSentTimestamp}. * * @param pingSentTimestamp the last {@code pingSentTimestamp} */ public void setPingSentTimestamp(long pingSentTimestamp) { this.pingSentTimestamp = pingSentTimestamp; } public long getPongReceivedTimestamp() { return pongReceivedTimestamp; } /** * Sets the last {@code pongReceivedTimestamp}. * * @param pongReceivedTimestamp the last {@code pongReceivedTimestamp} */ public void setPongReceivedTimestamp(long pongReceivedTimestamp) { this.pongReceivedTimestamp = pongReceivedTimestamp; } public long getConfigEpoch() { return configEpoch; } /** * Sets the {@code configEpoch}. * * @param configEpoch the {@code configEpoch} */ public void setConfigEpoch(long configEpoch) { this.configEpoch = configEpoch; } public List<Integer> getSlots() { return slots; } /** * Sets the list of slots for which this {@link RedisClusterNode} is the * {@link com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode.NodeFlag#MASTER}. The list is empty if this node * is not a master or the node is not responsible for any slots at all. * * @param slots list of slots, must not be {@literal null} but may be empty */ public void setSlots(List<Integer> slots) { LettuceAssert.notNull(slots, "Slots must not be null"); this.slots = slots; } public Set<NodeFlag> getFlags() { return flags; } /** * Set of {@link com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode.NodeFlag node flags}. * * @param flags the set of node flags. */ public void setFlags(Set<NodeFlag> flags) { this.flags = flags; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof RedisClusterNode)) { return false; } RedisClusterNode that = (RedisClusterNode) o; if (nodeId != null ? !nodeId.equals(that.nodeId) : that.nodeId != null) { return false; } return true; } @Override public int hashCode() { int result = 31 * (nodeId != null ? nodeId.hashCode() : 0); return result; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" [uri=").append(uri); sb.append(", nodeId='").append(nodeId).append('\''); sb.append(", connected=").append(connected); sb.append(", slaveOf='").append(slaveOf).append('\''); sb.append(", pingSentTimestamp=").append(pingSentTimestamp); sb.append(", pongReceivedTimestamp=").append(pongReceivedTimestamp); sb.append(", configEpoch=").append(configEpoch); sb.append(", flags=").append(flags); if (slots != null) { sb.append(", slot count=").append(slots.size()); } sb.append(']'); return sb.toString(); } /** * * @param nodeFlag the node flag * @return true if the {@linkplain NodeFlag} is contained within the flags. */ public boolean is(NodeFlag nodeFlag) { return getFlags().contains(nodeFlag); } /** * * @param slot the slot hash * @return true if the slot is contained within the handled slots. */ public boolean hasSlot(int slot) { return getSlots().contains(slot); } /** * Returns the {@link com.lambdaworks.redis.models.role.RedisInstance.Role} of the Redis Cluster node based on the * {@link #getFlags() flags}. * * @return the Redis Cluster node role */ @Override public Role getRole() { return is(NodeFlag.MASTER) ? Role.MASTER : Role.SLAVE; } /** * Redis Cluster node flags. */ public enum NodeFlag { NOFLAGS, MYSELF, SLAVE, MASTER, EVENTUAL_FAIL, FAIL, HANDSHAKE, NOADDR; } }