/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.ignite.internal.processors.cache.distributed.dht;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.events.DiscoveryEvent;
import org.apache.ignite.internal.IgniteInterruptedCheckedException;
import org.apache.ignite.internal.managers.discovery.DiscoCache;
import org.apache.ignite.internal.processors.affinity.AffinityAssignment;
import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion;
import org.apache.ignite.internal.processors.cache.GridCacheSharedContext;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionExchangeId;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionFullMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionMap;
import org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture;
import org.apache.ignite.internal.util.F0;
import org.apache.ignite.internal.util.GridAtomicLong;
import org.apache.ignite.internal.util.tostring.GridToStringExclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.T2;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.jetbrains.annotations.Nullable;
import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.EVICTED;
import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.MOVING;
import static org.apache.ignite.internal.processors.cache.distributed.dht.GridDhtPartitionState.OWNING;
/**
* Partition topology for node which does not have any local partitions.
*/
@GridToStringExclude
public class GridClientPartitionTopology implements GridDhtPartitionTopology {
/** If true, then check consistency. */
private static final boolean CONSISTENCY_CHECK = false;
/** Flag to control amount of output for full map. */
private static final boolean FULL_MAP_DEBUG = false;
/** */
private static final Long ZERO = 0L;
/** Cache shared context. */
private GridCacheSharedContext cctx;
/** Cache ID. */
private int cacheId;
/** Logger. */
private final IgniteLogger log;
/** Node to partition map. */
private GridDhtPartitionFullMap node2part;
/** Partition to node map. */
private Map<Integer, Set<UUID>> part2node = new HashMap<>();
/** */
private GridDhtPartitionExchangeId lastExchangeId;
/** */
private AffinityTopologyVersion topVer = AffinityTopologyVersion.NONE;
/** */
private volatile boolean stopping;
/** A future that will be completed when topology with version topVer will be ready to use. */
private volatile GridDhtTopologyFuture topReadyFut;
/** */
private final GridAtomicLong updateSeq = new GridAtomicLong(1);
/** Lock. */
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/** Partition update counters. */
private Map<Integer, T2<Long, Long>> cntrMap = new HashMap<>();
/** */
private final Object similarAffKey;
/** */
private volatile DiscoCache discoCache;
/**
* @param cctx Context.
* @param cacheId Cache ID.
* @param exchFut Exchange ID.
* @param similarAffKey Key to find caches with similar affinity.
*/
public GridClientPartitionTopology(
GridCacheSharedContext cctx,
int cacheId,
GridDhtPartitionsExchangeFuture exchFut,
Object similarAffKey
) {
this.cctx = cctx;
this.cacheId = cacheId;
this.similarAffKey = similarAffKey;
topVer = exchFut.topologyVersion();
discoCache = exchFut.discoCache();
log = cctx.logger(getClass());
lock.writeLock().lock();
try {
beforeExchange0(cctx.localNode(), exchFut);
}
finally {
lock.writeLock().unlock();
}
}
/**
* @return Key to find caches with similar affinity.
*/
@Nullable public Object similarAffinityKey() {
return similarAffKey;
}
/**
* @return Full map string representation.
*/
@SuppressWarnings({"ConstantConditions"})
private String fullMapString() {
return node2part == null ? "null" : FULL_MAP_DEBUG ? node2part.toFullString() : node2part.toString();
}
/**
* @param map Map to get string for.
* @return Full map string representation.
*/
@SuppressWarnings({"ConstantConditions"})
private String mapString(GridDhtPartitionMap map) {
return map == null ? "null" : FULL_MAP_DEBUG ? map.toFullString() : map.toString();
}
/** {@inheritDoc} */
@Override public int cacheId() {
return cacheId;
}
/** {@inheritDoc} */
@SuppressWarnings({"LockAcquiredButNotSafelyReleased"})
@Override public void readLock() {
lock.readLock().lock();
}
/** {@inheritDoc} */
@Override public void readUnlock() {
lock.readLock().unlock();
}
/** {@inheritDoc} */
@Override public void updateTopologyVersion(
GridDhtPartitionExchangeId exchId,
GridDhtPartitionsExchangeFuture exchFut,
long updSeq,
boolean stopping
) throws IgniteInterruptedCheckedException {
U.writeLock(lock);
try {
assert exchId.topologyVersion().compareTo(topVer) > 0 : "Invalid topology version [topVer=" + topVer +
", exchId=" + exchId + ']';
this.stopping = stopping;
topVer = exchId.topologyVersion();
discoCache = exchFut.discoCache();
updateSeq.setIfGreater(updSeq);
topReadyFut = exchFut;
}
finally {
lock.writeLock().unlock();
}
}
/** {@inheritDoc} */
@Override public AffinityTopologyVersion topologyVersion() {
lock.readLock().lock();
try {
assert topVer.topologyVersion() > 0;
return topVer;
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@Override public GridDhtTopologyFuture topologyVersionFuture() {
assert topReadyFut != null;
return topReadyFut;
}
/** {@inheritDoc} */
@Override public boolean stopping() {
return stopping;
}
/** {@inheritDoc} */
@Override public void initPartitions(GridDhtPartitionsExchangeFuture exchFut) {
// No-op.
}
/** {@inheritDoc} */
@Override public void beforeExchange(GridDhtPartitionsExchangeFuture exchFut, boolean initParts)
throws IgniteCheckedException {
ClusterNode loc = cctx.localNode();
U.writeLock(lock);
try {
if (stopping)
return;
beforeExchange0(loc, exchFut);
}
finally {
lock.writeLock().unlock();
}
}
/**
* @param loc Local node.
* @param exchFut Exchange future.
*/
private void beforeExchange0(ClusterNode loc, GridDhtPartitionsExchangeFuture exchFut) {
GridDhtPartitionExchangeId exchId = exchFut.exchangeId();
assert topVer.equals(exchId.topologyVersion()) : "Invalid topology version [topVer=" +
topVer + ", exchId=" + exchId + ']';
if (!exchId.isJoined())
removeNode(exchId.nodeId());
// In case if node joins, get topology at the time of joining node.
ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache();
assert oldest != null;
if (log.isDebugEnabled())
log.debug("Partition map beforeExchange [exchId=" + exchId + ", fullMap=" + fullMapString() + ']');
long updateSeq = this.updateSeq.incrementAndGet();
// If this is the oldest node.
if (oldest.id().equals(loc.id()) || exchFut.isCacheAdded(cacheId, exchId.topologyVersion())) {
if (node2part == null) {
node2part = new GridDhtPartitionFullMap(oldest.id(), oldest.order(), updateSeq);
if (log.isDebugEnabled())
log.debug("Created brand new full topology map on oldest node [exchId=" +
exchId + ", fullMap=" + fullMapString() + ']');
}
else if (!node2part.valid()) {
node2part = new GridDhtPartitionFullMap(oldest.id(), oldest.order(), updateSeq, node2part, false);
if (log.isDebugEnabled())
log.debug("Created new full topology map on oldest node [exchId=" + exchId + ", fullMap=" +
node2part + ']');
}
else if (!node2part.nodeId().equals(loc.id())) {
node2part = new GridDhtPartitionFullMap(oldest.id(), oldest.order(), updateSeq, node2part, false);
if (log.isDebugEnabled())
log.debug("Copied old map into new map on oldest node (previous oldest node left) [exchId=" +
exchId + ", fullMap=" + fullMapString() + ']');
}
}
consistencyCheck();
if (log.isDebugEnabled())
log.debug("Partition map after beforeExchange [exchId=" + exchId + ", fullMap=" +
fullMapString() + ']');
}
/** {@inheritDoc} */
@Override public boolean afterExchange(GridDhtPartitionsExchangeFuture exchFut) throws IgniteCheckedException {
AffinityTopologyVersion topVer = exchFut.topologyVersion();
lock.writeLock().lock();
try {
assert topVer.equals(exchFut.topologyVersion()) : "Invalid topology version [topVer=" +
topVer + ", exchId=" + exchFut.exchangeId() + ']';
if (log.isDebugEnabled())
log.debug("Partition map before afterExchange [exchId=" + exchFut.exchangeId() + ", fullMap=" +
fullMapString() + ']');
updateSeq.incrementAndGet();
consistencyCheck();
}
finally {
lock.writeLock().unlock();
}
return false;
}
/** {@inheritDoc} */
@Nullable @Override public GridDhtLocalPartition localPartition(
int p,
AffinityTopologyVersion topVer,
boolean create
)
throws GridDhtInvalidPartitionException {
if (!create)
return null;
throw new GridDhtInvalidPartitionException(p, "Adding entry to evicted partition (often may be caused by " +
"inconsistent 'key.hashCode()' implementation) " +
"[part=" + p + ", topVer=" + topVer + ", this.topVer=" + this.topVer + ']');
}
/** {@inheritDoc} */
@Override public GridDhtLocalPartition localPartition(Object key, boolean create) {
return localPartition(1, AffinityTopologyVersion.NONE, create);
}
/** {@inheritDoc} */
@Override public void releasePartitions(int... parts) {
// No-op.
}
/** {@inheritDoc} */
@Override public List<GridDhtLocalPartition> localPartitions() {
return Collections.emptyList();
}
/** {@inheritDoc} */
@Override public Collection<GridDhtLocalPartition> currentLocalPartitions() {
return Collections.emptyList();
}
/** {@inheritDoc} */
@Override public void onRemoved(GridDhtCacheEntry e) {
assert false : "Entry should not be removed from client topology: " + e;
}
/** {@inheritDoc} */
@Override public GridDhtPartitionMap localPartitionMap() {
lock.readLock().lock();
try {
return new GridDhtPartitionMap(cctx.localNodeId(), updateSeq.get(), topVer,
Collections.<Integer, GridDhtPartitionState>emptyMap(), true);
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@Override public GridDhtPartitionState partitionState(UUID nodeId, int part) {
lock.readLock().lock();
try {
GridDhtPartitionMap partMap = node2part.get(nodeId);
if (partMap != null) {
GridDhtPartitionState state = partMap.get(part);
return state == null ? EVICTED : state;
}
return EVICTED;
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@Nullable @Override public List<ClusterNode> nodes(int p,
AffinityAssignment affAssignment,
List<ClusterNode> affNodes) {
throw new UnsupportedOperationException();
}
/** {@inheritDoc} */
@Override public List<ClusterNode> nodes(int p, AffinityTopologyVersion topVer) {
lock.readLock().lock();
try {
assert node2part != null && node2part.valid() : "Invalid node-to-partitions map [topVer=" + topVer +
", node2part=" + node2part + ']';
List<ClusterNode> nodes = null;
Collection<UUID> nodeIds = part2node.get(p);
if (!F.isEmpty(nodeIds)) {
for (UUID nodeId : nodeIds) {
ClusterNode n = discoCache.node(nodeId);
if (n != null && (topVer.topologyVersion() < 0 || n.order() <= topVer.topologyVersion())) {
if (nodes == null)
nodes = new ArrayList<>(nodeIds.size());
nodes.add(n);
}
}
}
return nodes;
}
finally {
lock.readLock().unlock();
}
}
/**
* @param p Partition.
* @param topVer Topology version ({@code -1} for all nodes).
* @param state Partition state.
* @param states Additional partition states.
* @return List of nodes for the partition.
*/
private List<ClusterNode> nodes(int p, AffinityTopologyVersion topVer, GridDhtPartitionState state, GridDhtPartitionState... states) {
Collection<UUID> allIds = topVer.topologyVersion() > 0 ? F.nodeIds(discoCache.allNodesWithCaches()) : null;
lock.readLock().lock();
try {
assert node2part != null && node2part.valid() : "Invalid node-to-partitions map [topVer=" + topVer +
", allIds=" + allIds + ", node2part=" + node2part + ']';
Collection<UUID> nodeIds = part2node.get(p);
// Node IDs can be null if both, primary and backup, nodes disappear.
int size = nodeIds == null ? 0 : nodeIds.size();
if (size == 0)
return Collections.emptyList();
List<ClusterNode> nodes = new ArrayList<>(size);
for (UUID id : nodeIds) {
if (topVer.topologyVersion() > 0 && !allIds.contains(id))
continue;
if (hasState(p, id, state, states)) {
ClusterNode n = discoCache.node(id);
if (n != null && (topVer.topologyVersion() < 0 || n.order() <= topVer.topologyVersion()))
nodes.add(n);
}
}
return nodes;
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@Override public List<ClusterNode> owners(int p, AffinityTopologyVersion topVer) {
return nodes(p, topVer, OWNING);
}
/** {@inheritDoc} */
@Override public List<ClusterNode> owners(int p) {
return owners(p, AffinityTopologyVersion.NONE);
}
/** {@inheritDoc} */
@Override public List<ClusterNode> moving(int p) {
return nodes(p, AffinityTopologyVersion.NONE, MOVING);
}
/**
* @param p Partition.
* @param topVer Topology version.
* @return List of nodes in state OWNING or MOVING.
*/
private List<ClusterNode> ownersAndMoving(int p, AffinityTopologyVersion topVer) {
return nodes(p, topVer, OWNING, MOVING);
}
/** {@inheritDoc} */
@Override public long updateSequence() {
return updateSeq.get();
}
/**
* @return Last update sequence.
*/
public long lastUpdateSequence() {
lock.writeLock().lock();
try {
return updateSeq.incrementAndGet();
}
finally {
lock.writeLock().unlock();
}
}
/** {@inheritDoc} */
@Override public GridDhtPartitionFullMap partitionMap(boolean onlyActive) {
lock.readLock().lock();
try {
assert node2part != null && node2part.valid() : "Invalid node2part [node2part: " + node2part +
", locNodeId=" + cctx.localNodeId() +
", igniteInstanceName=" + cctx.igniteInstanceName() + ']';
GridDhtPartitionFullMap m = node2part;
return new GridDhtPartitionFullMap(m.nodeId(), m.nodeOrder(), m.updateSequence(), m, onlyActive);
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
@Nullable @Override public GridDhtPartitionMap update(@Nullable GridDhtPartitionExchangeId exchId,
GridDhtPartitionFullMap partMap,
Map<Integer, T2<Long, Long>> cntrMap) {
if (log.isDebugEnabled())
log.debug("Updating full partition map [exchId=" + exchId + ", parts=" + fullMapString() + ']');
lock.writeLock().lock();
try {
if (exchId != null && lastExchangeId != null && lastExchangeId.compareTo(exchId) >= 0) {
if (log.isDebugEnabled())
log.debug("Stale exchange id for full partition map update (will ignore) [lastExchId=" +
lastExchangeId + ", exchId=" + exchId + ']');
return null;
}
if (node2part != null && node2part.compareTo(partMap) >= 0) {
if (log.isDebugEnabled())
log.debug("Stale partition map for full partition map update (will ignore) [lastExchId=" +
lastExchangeId + ", exchId=" + exchId + ", curMap=" + node2part + ", newMap=" + partMap + ']');
return null;
}
updateSeq.incrementAndGet();
if (exchId != null)
lastExchangeId = exchId;
if (node2part != null) {
for (GridDhtPartitionMap part : node2part.values()) {
GridDhtPartitionMap newPart = partMap.get(part.nodeId());
// If for some nodes current partition has a newer map,
// then we keep the newer value.
if (newPart != null && newPart.updateSequence() < part.updateSequence()) {
if (log.isDebugEnabled())
log.debug("Overriding partition map in full update map [exchId=" + exchId + ", curPart=" +
mapString(part) + ", newPart=" + mapString(newPart) + ']');
partMap.put(part.nodeId(), part);
}
}
for (Iterator<UUID> it = partMap.keySet().iterator(); it.hasNext(); ) {
UUID nodeId = it.next();
if (!cctx.discovery().alive(nodeId)) {
if (log.isDebugEnabled())
log.debug("Removing left node from full map update [nodeId=" + nodeId + ", partMap=" +
partMap + ']');
it.remove();
}
}
}
node2part = partMap;
Map<Integer, Set<UUID>> p2n = new HashMap<>();
for (Map.Entry<UUID, GridDhtPartitionMap> e : partMap.entrySet()) {
for (Integer p : e.getValue().keySet()) {
Set<UUID> ids = p2n.get(p);
if (ids == null)
// Initialize HashSet to size 3 in anticipation that there won't be
// more than 3 nodes per partitions.
p2n.put(p, ids = U.newHashSet(3));
ids.add(e.getKey());
}
}
part2node = p2n;
if (cntrMap != null)
this.cntrMap = new HashMap<>(cntrMap);
consistencyCheck();
if (log.isDebugEnabled())
log.debug("Partition map after full update: " + fullMapString());
return null;
}
finally {
lock.writeLock().unlock();
}
}
/** {@inheritDoc} */
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
@Nullable @Override public GridDhtPartitionMap update(
@Nullable GridDhtPartitionExchangeId exchId,
GridDhtPartitionMap parts,
Map<Integer, T2<Long, Long>> cntrMap
) {
if (log.isDebugEnabled())
log.debug("Updating single partition map [exchId=" + exchId + ", parts=" + mapString(parts) + ']');
if (!cctx.discovery().alive(parts.nodeId())) {
if (log.isDebugEnabled())
log.debug("Received partition update for non-existing node (will ignore) [exchId=" + exchId +
", parts=" + parts + ']');
return null;
}
lock.writeLock().lock();
try {
if (stopping)
return null;
if (lastExchangeId != null && exchId != null && lastExchangeId.compareTo(exchId) > 0) {
if (log.isDebugEnabled())
log.debug("Stale exchange id for single partition map update (will ignore) [lastExchId=" +
lastExchangeId + ", exchId=" + exchId + ']');
return null;
}
if (exchId != null)
lastExchangeId = exchId;
if (node2part == null)
// Create invalid partition map.
node2part = new GridDhtPartitionFullMap();
GridDhtPartitionMap cur = node2part.get(parts.nodeId());
if (cur != null && cur.updateSequence() >= parts.updateSequence()) {
if (log.isDebugEnabled())
log.debug("Stale update sequence for single partition map update (will ignore) [exchId=" + exchId +
", curSeq=" + cur.updateSequence() + ", newSeq=" + parts.updateSequence() + ']');
return null;
}
long updateSeq = this.updateSeq.incrementAndGet();
node2part = new GridDhtPartitionFullMap(node2part, updateSeq);
boolean changed = false;
if (cur == null || !cur.equals(parts))
changed = true;
node2part.put(parts.nodeId(), parts);
part2node = new HashMap<>(part2node);
// Add new mappings.
for (Integer p : parts.keySet()) {
Set<UUID> ids = part2node.get(p);
if (ids == null)
// Initialize HashSet to size 3 in anticipation that there won't be
// more than 3 nodes per partition.
part2node.put(p, ids = U.newHashSet(3));
changed |= ids.add(parts.nodeId());
}
// Remove obsolete mappings.
if (cur != null) {
for (Integer p : F.view(cur.keySet(), F0.notIn(parts.keySet()))) {
Set<UUID> ids = part2node.get(p);
if (ids != null)
changed |= ids.remove(parts.nodeId());
}
}
if (cntrMap != null) {
for (Map.Entry<Integer, T2<Long, Long>> e : cntrMap.entrySet()) {
T2<Long, Long> cntr = this.cntrMap.get(e.getKey());
if (cntr == null || cntr.get2() < e.getValue().get2())
this.cntrMap.put(e.getKey(), e.getValue());
}
}
consistencyCheck();
if (log.isDebugEnabled())
log.debug("Partition map after single update: " + fullMapString());
return changed ? localPartitionMap() : null;
}
finally {
lock.writeLock().unlock();
}
}
/** {@inheritDoc} */
@Override public boolean detectLostPartitions(DiscoveryEvent discoEvt) {
assert false : "detectLostPartitions should never be called on client topology";
return false;
}
/** {@inheritDoc} */
@Override public void resetLostPartitions() {
assert false : "resetLostPartitions should never be called on client topology";
}
/** {@inheritDoc} */
@Override public Collection<Integer> lostPartitions() {
assert false : "lostPartitions should never be called on client topology";
return Collections.emptyList();
}
/** {@inheritDoc} */
@Override public void checkEvictions() {
// No-op.
}
/**
* Updates value for single partition.
*
* @param p Partition.
* @param nodeId Node ID.
* @param state State.
* @param updateSeq Update sequence.
*/
private void updateLocal(int p, UUID nodeId, GridDhtPartitionState state, long updateSeq) {
assert lock.isWriteLockedByCurrentThread();
assert nodeId.equals(cctx.localNodeId());
// In case if node joins, get topology at the time of joining node.
ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache();
// If this node became the oldest node.
if (oldest.id().equals(cctx.localNodeId())) {
long seq = node2part.updateSequence();
if (seq != updateSeq) {
if (seq > updateSeq) {
if (this.updateSeq.get() < seq) {
// Update global counter if necessary.
boolean b = this.updateSeq.compareAndSet(this.updateSeq.get(), seq + 1);
assert b : "Invalid update sequence [updateSeq=" + updateSeq + ", seq=" + seq +
", curUpdateSeq=" + this.updateSeq.get() + ", node2part=" + node2part.toFullString() + ']';
updateSeq = seq + 1;
}
else
updateSeq = seq;
}
node2part.updateSequence(updateSeq);
}
}
GridDhtPartitionMap map = node2part.get(nodeId);
if (map == null)
node2part.put(nodeId, map = new GridDhtPartitionMap(nodeId, updateSeq, topVer,
Collections.<Integer, GridDhtPartitionState>emptyMap(), false));
map.updateSequence(updateSeq, topVer);
map.put(p, state);
Set<UUID> ids = part2node.get(p);
if (ids == null)
part2node.put(p, ids = U.newHashSet(3));
ids.add(nodeId);
}
/**
* @param nodeId Node to remove.
*/
private void removeNode(UUID nodeId) {
assert nodeId != null;
assert lock.writeLock().isHeldByCurrentThread();
ClusterNode oldest = discoCache.oldestAliveServerNodeWithCache();
ClusterNode loc = cctx.localNode();
if (node2part != null) {
if (oldest.equals(loc) && !node2part.nodeId().equals(loc.id())) {
updateSeq.setIfGreater(node2part.updateSequence());
node2part = new GridDhtPartitionFullMap(loc.id(), loc.order(), updateSeq.incrementAndGet(),
node2part, false);
}
else
node2part = new GridDhtPartitionFullMap(node2part, node2part.updateSequence());
part2node = new HashMap<>(part2node);
GridDhtPartitionMap parts = node2part.remove(nodeId);
if (parts != null) {
for (Integer p : parts.keySet()) {
Set<UUID> nodeIds = part2node.get(p);
if (nodeIds != null) {
nodeIds.remove(nodeId);
if (nodeIds.isEmpty())
part2node.remove(p);
}
}
}
consistencyCheck();
}
}
/** {@inheritDoc} */
@Override public boolean own(GridDhtLocalPartition part) {
assert false : "Client topology should never own a partition: " + part;
return false;
}
/** {@inheritDoc} */
@Override public void onEvicted(GridDhtLocalPartition part, boolean updateSeq) {
assert updateSeq || lock.isWriteLockedByCurrentThread();
lock.writeLock().lock();
try {
if (stopping)
return;
assert part.state() == EVICTED;
long seq = updateSeq ? this.updateSeq.incrementAndGet() : this.updateSeq.get();
updateLocal(part.id(), cctx.localNodeId(), part.state(), seq);
consistencyCheck();
}
finally {
lock.writeLock().unlock();
}
}
/** {@inheritDoc} */
@Nullable @Override public GridDhtPartitionMap partitions(UUID nodeId) {
lock.readLock().lock();
try {
return node2part.get(nodeId);
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@Override public void setOwners(int p, Set<UUID> owners, boolean updateSeq) {
lock.writeLock().lock();
try {
for (Map.Entry<UUID, GridDhtPartitionMap> e : node2part.entrySet()) {
if (!e.getValue().containsKey(p))
continue;
if (e.getValue().get(p) == OWNING && !owners.contains(e.getKey()))
e.getValue().put(p, MOVING);
else if (owners.contains(e.getKey()))
e.getValue().put(p, OWNING);
}
part2node.put(p, owners);
if (updateSeq)
this.updateSeq.incrementAndGet();
}
finally {
lock.writeLock().unlock();
}
}
/** {@inheritDoc} */
@Override public Map<Integer, T2<Long, Long>> updateCounters(boolean skipZeros) {
lock.readLock().lock();
try {
if (skipZeros) {
Map<Integer, T2<Long, Long>> res = U.newHashMap(cntrMap.size());
for (Map.Entry<Integer, T2<Long, Long>> e : cntrMap.entrySet()) {
if (!e.getValue().equals(ZERO))
res.put(e.getKey(), e.getValue());
}
return res;
}
else
return new HashMap<>(cntrMap);
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@Override public boolean rebalanceFinished(AffinityTopologyVersion topVer) {
assert false : "Should not be called on non-affinity node";
return false;
}
/** {@inheritDoc} */
@Override public boolean hasMovingPartitions() {
lock.readLock().lock();
try {
assert node2part != null && node2part.valid() : "Invalid node2part [node2part: " + node2part +
", locNodeId=" + cctx.localNodeId() +
", igniteInstanceName=" + cctx.igniteInstanceName() + ']';
for (GridDhtPartitionMap map : node2part.values()) {
if (map.hasMovingPartitions())
return true;
}
return false;
}
finally {
lock.readLock().unlock();
}
}
/** {@inheritDoc} */
@Override public void printMemoryStats(int threshold) {
X.println(">>> Cache partition topology stats [igniteInstanceName=" + cctx.igniteInstanceName() +
", cacheId=" + cacheId + ']');
}
/**
* @param p Partition.
* @param nodeId Node ID.
* @param match State to match.
* @param matches Additional states.
* @return Filter for owners of this partition.
*/
private boolean hasState(final int p, @Nullable UUID nodeId, final GridDhtPartitionState match,
final GridDhtPartitionState... matches) {
if (nodeId == null)
return false;
GridDhtPartitionMap parts = node2part.get(nodeId);
// Set can be null if node has been removed.
if (parts != null) {
GridDhtPartitionState state = parts.get(p);
if (state == match)
return true;
if (matches != null && matches.length > 0)
for (GridDhtPartitionState s : matches)
if (state == s)
return true;
}
return false;
}
/**
* Checks consistency after all operations.
*/
private void consistencyCheck() {
if (CONSISTENCY_CHECK) {
assert lock.writeLock().isHeldByCurrentThread();
if (node2part == null)
return;
for (Map.Entry<UUID, GridDhtPartitionMap> e : node2part.entrySet()) {
for (Integer p : e.getValue().keySet()) {
Set<UUID> nodeIds = part2node.get(p);
assert nodeIds != null : "Failed consistency check [part=" + p + ", nodeId=" + e.getKey() + ']';
assert nodeIds.contains(e.getKey()) : "Failed consistency check [part=" + p + ", nodeId=" +
e.getKey() + ", nodeIds=" + nodeIds + ']';
}
}
for (Map.Entry<Integer, Set<UUID>> e : part2node.entrySet()) {
for (UUID nodeId : e.getValue()) {
GridDhtPartitionMap map = node2part.get(nodeId);
assert map != null : "Failed consistency check [part=" + e.getKey() + ", nodeId=" + nodeId + ']';
assert map.containsKey(e.getKey()) : "Failed consistency check [part=" + e.getKey() +
", nodeId=" + nodeId + ']';
}
}
}
}
}