org.infinispan.topology.ClusterCacheStatus Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.infinispan.topology;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.infinispan.distribution.ch.ConsistentHash;
import org.infinispan.remoting.transport.Address;
import org.infinispan.util.Immutables;
import org.infinispan.util.InfinispanCollections;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* Keeps track of a cache's status: members, current/pending consistent hashes, and rebalance status
*
* @author Dan Berindei
* @since 5.2
*/
public class ClusterCacheStatus {
private static final Log log = LogFactory.getLog(ClusterCacheStatus.class);
private static boolean trace = log.isTraceEnabled();
private final String cacheName;
private final CacheJoinInfo joinInfo;
// Cache members, some of which may not have received state yet
private volatile List members;
// Cache members that have not yet received state. Always included in the members list.
private volatile List joiners;
// Cache topology. Its consistent hashes contain only members that did receive/are receiving state
// The members of both consistent hashes must be included in the members list.
private volatile CacheTopology cacheTopology;
private volatile RebalanceConfirmationCollector rebalanceStatus;
public ClusterCacheStatus(String cacheName, CacheJoinInfo joinInfo) {
this.cacheName = cacheName;
this.joinInfo = joinInfo;
this.cacheTopology = new CacheTopology(-1, null, null);
this.members = InfinispanCollections.emptyList();
this.joiners = InfinispanCollections.emptyList();
if (trace) log.tracef("Cache %s initialized, join info is %s", cacheName, joinInfo);
}
public CacheJoinInfo getJoinInfo() {
return joinInfo;
}
public List getMembers() {
return members;
}
public boolean hasMembers() {
return !members.isEmpty();
}
public List getJoiners() {
return joiners;
}
public boolean hasJoiners() {
return !joiners.isEmpty();
}
public void setMembers(List newMembers) {
synchronized (this) {
members = Immutables.immutableListCopy(newMembers);
ConsistentHash currentCH = cacheTopology.getCurrentCH();
if (currentCH != null) {
joiners = immutableRemoveAll(members, currentCH.getMembers());
} else {
joiners = members;
}
if (trace) log.tracef("Cache %s members list updated, members = %s, joiners = %s", cacheName,
members, joiners);
}
}
/**
* @return {@code true} if the joiner was not already a member, {@code false} otherwise
*/
public boolean addMember(Address joiner) {
synchronized (this) {
if (members.contains(joiner)) {
if (trace) log.tracef("Trying to add node %s to cache %s, but it is already a member: " +
"members = %s, joiners = %s", joiner, cacheName, members, joiners);
return false;
}
members = immutableAdd(members, joiner);
joiners = immutableAdd(joiners, joiner);
if (trace) log.tracef("Added joiner %s to cache %s: members = %s, joiners = %s", joiner, cacheName,
members, joiners);
return true;
}
}
/**
* @return {@code true} if the leaver was a member, {@code false} otherwise
*/
public boolean removeMember(Address leaver) {
synchronized (this) {
if (!members.contains(leaver)) {
if (trace) log.tracef("Trying to remove node %s from cache %s, but it is not a member: " +
"members = %s", leaver, cacheName, members);
return false;
}
members = immutableRemove(members, leaver);
joiners = immutableRemove(joiners, leaver);
if (trace) log.tracef("Removed node %s from cache %s: members = %s, joiners = %s", leaver,
cacheName, members, joiners);
return true;
}
}
/**
* @return {@code true} if the members list has changed, {@code false} otherwise
*/
public boolean updateClusterMembers(List newClusterMembers) {
synchronized (this) {
if (newClusterMembers.containsAll(members)) {
if (trace) log.tracef("Cluster members updated for cache %s, no leavers detected: " +
"cache members = %s", cacheName, newClusterMembers);
return false;
}
members = immutableRetainAll(members, newClusterMembers);
joiners = immutableRetainAll(joiners, newClusterMembers);
if (trace) log.tracef("Cluster members updated for cache %s: members = %s, joiners = %s", cacheName,
members, joiners);
return true;
}
}
public CacheTopology getCacheTopology() {
return cacheTopology;
}
public void updateCacheTopology(CacheTopology newTopology) {
synchronized (this) {
this.cacheTopology = newTopology;
if (!members.containsAll(cacheTopology.getMembers())) {
throw new IllegalStateException(String.format("Trying to set a topology with invalid members " +
"for cache %s: members = %s, topology = %s", cacheName, members, cacheTopology));
}
// update the joiners list
if (newTopology.getCurrentCH() != null) {
joiners = immutableRemoveAll(members, newTopology.getCurrentCH().getMembers());
}
if (trace) log.tracef("Cache %s topology updated: members = %s, joiners = %s, topology = %s",
cacheName, members, joiners, cacheTopology);
}
}
public boolean needConsistentHashUpdate() {
// The list of current members is always included in the list of pending members,
// so we only need to check one list.
// Also returns false if both CHs are null
return !members.containsAll(cacheTopology.getMembers());
}
public List pruneInvalidMembers(List possibleMembers) {
return immutableRetainAll(possibleMembers, members);
}
public boolean isRebalanceInProgress() {
return rebalanceStatus != null;
}
/**
* @return {@code true} if a rebalance was started, {@code false} if a rebalance was already in progress
*/
public boolean startRebalance(CacheTopology newTopology) {
synchronized (this) {
if (rebalanceStatus != null)
return false;
rebalanceStatus = new RebalanceConfirmationCollector(cacheName, newTopology.getTopologyId(),
newTopology.getMembers());
this.cacheTopology = newTopology;
return true;
}
}
/**
* @return {@code true} if this was the last confirmation needed, {@code false} if more confirmations
* are needed or if the rebalance was already confirmed in another way (e.g. members list update)
*/
public boolean confirmRebalanceOnNode(Address member, int receivedTopologyId) {
synchronized (this) {
if (rebalanceStatus == null)
return false;
return rebalanceStatus.confirmRebalance(member, receivedTopologyId);
}
}
/**
* Should be called after the members list was updated in any other way ({@link #removeMember(Address)},
* {@link #updateClusterMembers} etc.)
*
* @return {@code true} if the rebalance was confirmed with this update, {@code false} if more confirmations
* are needed or if the rebalance was already confirmed in another way (e.g. the last member confirmed)
*/
public boolean updateRebalanceMembersList() {
synchronized (this) {
if (rebalanceStatus == null)
return false;
return rebalanceStatus.updateMembers(members);
}
}
public void endRebalance() {
synchronized (this) {
if (rebalanceStatus == null) {
throw new IllegalStateException("Can't end rebalance, there is no rebalance in progress");
}
rebalanceStatus = null;
}
}
// Helpers for working with immutable lists
private List immutableAdd(List list, T element) {
List result = new ArrayList(list);
result.add(element);
return Collections.unmodifiableList(result);
}
private List immutableRemove(List list, T element) {
List result = new ArrayList(list);
result.remove(element);
return Collections.unmodifiableList(result);
}
private List immutableRemoveAll(List list, List otherList) {
List result = new ArrayList(list);
result.removeAll(otherList);
return Collections.unmodifiableList(result);
}
private List immutableRetainAll(List list, List otherList) {
List result = new ArrayList(list);
result.retainAll(otherList);
return Collections.unmodifiableList(result);
}
@Override
public String toString() {
return "ClusterCacheStatus{" +
"cacheName='" + cacheName + '\'' +
", members=" + members +
", joiners=" + joiners +
", cacheTopology=" + cacheTopology +
", rebalanceStatus=" + rebalanceStatus +
'}';
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy