/**
   * @param nodes Nodes.
   * @param id ID.
   * @throws IgniteCheckedException If failed.
   */
  private void sendAllPartitions(
      Collection<? extends ClusterNode> nodes, GridDhtPartitionExchangeId id)
      throws IgniteCheckedException {
    GridDhtPartitionsFullMessage m =
        new GridDhtPartitionsFullMessage(id, lastVer.get(), id.topologyVersion());

    for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
      if (!cacheCtx.isLocal()) {
        AffinityTopologyVersion startTopVer = cacheCtx.startTopologyVersion();

        boolean ready = startTopVer == null || startTopVer.compareTo(id.topologyVersion()) <= 0;

        if (ready)
          m.addFullPartitionsMap(cacheCtx.cacheId(), cacheCtx.topology().partitionMap(true));
      }
    }

    // It is important that client topologies be added after contexts.
    for (GridClientPartitionTopology top : cctx.exchange().clientTopologies())
      m.addFullPartitionsMap(top.cacheId(), top.partitionMap(true));

    if (log.isDebugEnabled())
      log.debug(
          "Sending full partition map [nodeIds="
              + F.viewReadOnly(nodes, F.node2id())
              + ", exchId="
              + exchId
              + ", msg="
              + m
              + ']');

    cctx.io().safeSend(nodes, m, SYSTEM_POOL, null);
  }
  /** {@inheritDoc} */
  @Override
  public boolean onDone(GridCacheTx tx, Throwable err) {
    if ((initialized() || err != null) && super.onDone(tx, err)) {
      if (error() instanceof GridCacheTxHeuristicException) {
        long topVer = this.tx.topologyVersion();

        for (GridCacheTxEntry<K, V> e : this.tx.writeMap().values()) {
          try {
            if (e.op() != NOOP && !cctx.affinity().localNode(e.key(), topVer)) {
              GridCacheEntryEx<K, V> cacheEntry = cctx.cache().peekEx(e.key());

              if (cacheEntry != null) cacheEntry.invalidate(null, this.tx.xidVersion());
            }
          } catch (Throwable t) {
            U.error(log, "Failed to invalidate entry.", t);

            if (t instanceof Error) throw (Error) t;
          }
        }
      }

      // Don't forget to clean up.
      cctx.mvcc().removeFuture(this);

      return true;
    }

    return false;
  }
  /**
   * @param updateSeq Update sequence.
   * @return {@code True} if entry has been transitioned to state EVICTED.
   */
  boolean tryEvict(boolean updateSeq) {
    if (state.getReference() != RENTING || state.getStamp() != 0 || groupReserved()) return false;

    // Attempt to evict partition entries from cache.
    clearAll();

    if (map.isEmpty() && state.compareAndSet(RENTING, EVICTED, 0, 0)) {
      if (log.isDebugEnabled()) log.debug("Evicted partition: " + this);

      if (!GridQueryProcessor.isEnabled(cctx.config())) clearSwap();

      if (cctx.isDrEnabled()) cctx.dr().partitionEvicted(id);

      cctx.dataStructures().onPartitionEvicted(id);

      rent.onDone();

      ((GridDhtPreloader) cctx.preloader()).onPartitionEvicted(this, updateSeq);

      clearDeferredDeletes();

      return true;
    }

    return false;
  }
  /**
   * @param cctx Context.
   * @param id Partition ID.
   */
  @SuppressWarnings("ExternalizableWithoutPublicNoArgConstructor")
  GridDhtLocalPartition(GridCacheContext cctx, int id) {
    assert cctx != null;

    this.id = id;
    this.cctx = cctx;

    log = U.logger(cctx.kernalContext(), logRef, this);

    rent =
        new GridFutureAdapter<Object>() {
          @Override
          public String toString() {
            return "PartitionRentFuture [part=" + GridDhtLocalPartition.this + ", map=" + map + ']';
          }
        };

    map = new ConcurrentHashMap8<>(cctx.config().getStartSize() / cctx.affinity().partitions());

    int delQueueSize =
        CU.isSystemCache(cctx.name())
            ? 100
            : Math.max(MAX_DELETE_QUEUE_SIZE / cctx.affinity().partitions(), 20);

    rmvQueue = new GridCircularBuffer<>(U.ceilPow2(delQueueSize));
  }
  /** {@inheritDoc} */
  @Override
  public GridDhtPartitionFullMap partitionMap(boolean onlyActive) {
    lock.readLock().lock();

    try {
      assert node2part != null && node2part.valid()
          : "Invalid node2part [node2part: "
              + node2part
              + ", cache="
              + cctx.name()
              + ", started="
              + cctx.started()
              + ", stopping="
              + stopping
              + ", locNodeId="
              + cctx.localNode().id()
              + ", locName="
              + cctx.gridName()
              + ']';

      GridDhtPartitionFullMap m = node2part;

      return new GridDhtPartitionFullMap(
          m.nodeId(), m.nodeOrder(), m.updateSequence(), m, onlyActive);
    } finally {
      lock.readLock().unlock();
    }
  }
  /**
   * Updates partition map in all caches.
   *
   * @param msg Partitions single message.
   */
  private void updatePartitionSingleMap(GridDhtPartitionsSingleMessage msg) {
    for (Map.Entry<Integer, GridDhtPartitionMap> entry : msg.partitions().entrySet()) {
      Integer cacheId = entry.getKey();
      GridCacheContext cacheCtx = cctx.cacheContext(cacheId);

      GridDhtPartitionTopology top =
          cacheCtx != null ? cacheCtx.topology() : cctx.exchange().clientTopology(cacheId, this);

      top.update(exchId, entry.getValue());
    }
  }
  /**
   * @param cacheCtx Cache context.
   * @throws IgniteCheckedException If failed.
   */
  private void initTopology(GridCacheContext cacheCtx) throws IgniteCheckedException {
    if (stopping(cacheCtx.cacheId())) return;

    if (canCalculateAffinity(cacheCtx)) {
      if (log.isDebugEnabled())
        log.debug(
            "Will recalculate affinity [locNodeId="
                + cctx.localNodeId()
                + ", exchId="
                + exchId
                + ']');

      cacheCtx.affinity().calculateAffinity(exchId.topologyVersion(), discoEvt);
    } else {
      if (log.isDebugEnabled())
        log.debug(
            "Will request affinity from remote node [locNodeId="
                + cctx.localNodeId()
                + ", exchId="
                + exchId
                + ']');

      // Fetch affinity assignment from remote node.
      GridDhtAssignmentFetchFuture fetchFut =
          new GridDhtAssignmentFetchFuture(
              cacheCtx,
              exchId.topologyVersion(),
              CU.affinityNodes(cacheCtx, exchId.topologyVersion()));

      fetchFut.init();

      List<List<ClusterNode>> affAssignment = fetchFut.get();

      if (log.isDebugEnabled())
        log.debug(
            "Fetched affinity from remote node, initializing affinity assignment [locNodeId="
                + cctx.localNodeId()
                + ", topVer="
                + exchId.topologyVersion()
                + ']');

      if (affAssignment == null) {
        affAssignment = new ArrayList<>(cacheCtx.affinity().partitions());

        List<ClusterNode> empty = Collections.emptyList();

        for (int i = 0; i < cacheCtx.affinity().partitions(); i++) affAssignment.add(empty);
      }

      cacheCtx.affinity().initializeAffinity(exchId.topologyVersion(), affAssignment);
    }
  }
  /**
   * @param cacheId Cache ID to check.
   * @param topVer Topology version.
   * @return {@code True} if cache was added during this exchange.
   */
  public boolean isCacheAdded(int cacheId, AffinityTopologyVersion topVer) {
    if (!F.isEmpty(reqs)) {
      for (DynamicCacheChangeRequest req : reqs) {
        if (req.start() && !req.clientStartOnly()) {
          if (CU.cacheId(req.cacheName()) == cacheId) return true;
        }
      }
    }

    GridCacheContext<?, ?> cacheCtx = cctx.cacheContext(cacheId);

    return cacheCtx != null && F.eq(cacheCtx.startTopologyVersion(), topVer);
  }
  /**
   * Updates partition map in all caches.
   *
   * @param msg Partitions full messages.
   */
  private void updatePartitionFullMap(GridDhtPartitionsFullMessage msg) {
    for (Map.Entry<Integer, GridDhtPartitionFullMap> entry : msg.partitions().entrySet()) {
      Integer cacheId = entry.getKey();

      GridCacheContext cacheCtx = cctx.cacheContext(cacheId);

      if (cacheCtx != null) cacheCtx.topology().update(exchId, entry.getValue());
      else {
        ClusterNode oldest = CU.oldestAliveCacheServerNode(cctx, AffinityTopologyVersion.NONE);

        if (oldest != null && oldest.isLocal())
          cctx.exchange().clientTopology(cacheId, this).update(exchId, entry.getValue());
      }
    }
  }
  /** {@inheritDoc} */
  @Override
  public Collection<ClusterNode> nodes(int p, AffinityTopologyVersion topVer) {
    Collection<ClusterNode> affNodes = cctx.affinity().nodes(p, topVer);

    lock.readLock().lock();

    try {
      assert node2part != null && node2part.valid()
          : "Invalid node-to-partitions map [topVer1="
              + topVer
              + ", topVer2="
              + this.topVer
              + ", cache="
              + cctx.name()
              + ", node2part="
              + node2part
              + ']';

      Collection<ClusterNode> nodes = null;

      Collection<UUID> nodeIds = part2node.get(p);

      if (!F.isEmpty(nodeIds)) {
        Collection<UUID> affIds = new HashSet<>(F.viewReadOnly(affNodes, F.node2id()));

        for (UUID nodeId : nodeIds) {
          if (!affIds.contains(nodeId) && hasState(p, nodeId, OWNING, MOVING, RENTING)) {
            ClusterNode n = cctx.discovery().node(nodeId);

            if (n != null
                && (topVer.topologyVersion() < 0 || n.order() <= topVer.topologyVersion())) {
              if (nodes == null) {
                nodes = new ArrayList<>(affNodes.size() + 2);

                nodes.addAll(affNodes);
              }

              nodes.add(n);
            }
          }
        }
      }

      return nodes != null ? nodes : affNodes;
    } finally {
      lock.readLock().unlock();
    }
  }
Example #11
0
  /**
   * @param ctx Cache registry.
   * @param implicit Implicit flag.
   * @param implicitSingle Implicit with one key flag.
   * @param concurrency Concurrency.
   * @param isolation Isolation.
   * @param timeout Timeout.
   * @param invalidate Invalidation policy.
   * @param syncCommit Synchronous commit flag.
   * @param syncRollback Synchronous rollback flag.
   * @param swapEnabled Whether to use swap storage.
   * @param storeEnabled Whether to use read/write through.
   */
  GridNearTxLocal(
      GridCacheContext<K, V> ctx,
      boolean implicit,
      boolean implicitSingle,
      GridCacheTxConcurrency concurrency,
      GridCacheTxIsolation isolation,
      long timeout,
      boolean invalidate,
      boolean syncCommit,
      boolean syncRollback,
      boolean swapEnabled,
      boolean storeEnabled) {
    super(
        ctx,
        ctx.versions().next(),
        implicit,
        implicitSingle,
        concurrency,
        isolation,
        timeout,
        invalidate,
        swapEnabled,
        storeEnabled);

    assert ctx != null;

    this.syncCommit = syncCommit;
    this.syncRollback = syncRollback;
  }
  /**
   * Constructor.
   *
   * @param name Latch name.
   * @param cnt Current count.
   * @param initCnt Initial count.
   * @param autoDel Auto delete flag.
   * @param key Latch key.
   * @param latchView Latch projection.
   * @param ctx Cache context.
   */
  public GridCacheCountDownLatchImpl(
      String name,
      int cnt,
      int initCnt,
      boolean autoDel,
      GridCacheInternalKey key,
      GridCacheProjection<GridCacheInternalKey, GridCacheCountDownLatchValue> latchView,
      GridCacheContext ctx) {
    assert name != null;
    assert cnt >= 0;
    assert initCnt >= 0;
    assert key != null;
    assert latchView != null;
    assert ctx != null;

    this.name = name;
    this.cnt = cnt;
    this.initCnt = initCnt;
    this.autoDel = autoDel;
    this.key = key;
    this.latchView = latchView;
    this.ctx = ctx;

    log = ctx.gridConfig().getGridLogger().getLogger(getClass());
  }
  /** @param cctx Context. */
  GridDhtPartitionTopologyImpl(GridCacheContext<?, ?> cctx) {
    assert cctx != null;

    this.cctx = cctx;

    log = cctx.logger(getClass());
  }
  /** {@inheritDoc} */
  @Override
  public void printMemoryStats(int threshold) {
    X.println(
        ">>>  Cache partition topology stats [grid="
            + cctx.gridName()
            + ", cache="
            + cctx.name()
            + ']');

    for (GridDhtLocalPartition part : locParts.values()) {
      int size = part.size();

      if (size >= threshold)
        X.println(">>>   Local partition [part=" + part.id() + ", size=" + size + ']');
    }
  }
  /**
   * @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(CU.affinityNodes(cctx, topVer)) : null;

    lock.readLock().lock();

    try {
      assert node2part != null && node2part.valid()
          : "Invalid node-to-partitions map [topVer="
              + topVer
              + ", allIds="
              + allIds
              + ", node2part="
              + node2part
              + ", cache="
              + cctx.name()
              + ']';

      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 = cctx.discovery().node(id);

          if (n != null && (topVer.topologyVersion() < 0 || n.order() <= topVer.topologyVersion()))
            nodes.add(n);
        }
      }

      return nodes;
    } finally {
      lock.readLock().unlock();
    }
  }
  /** @param m Mapping. */
  @SuppressWarnings({"unchecked"})
  private void finish(GridDistributedTxMapping<K, V> m) {
    GridRichNode n = m.node();

    assert !m.empty();

    GridNearTxFinishRequest req =
        new GridNearTxFinishRequest<K, V>(
            futId,
            tx.xidVersion(),
            tx.commitVersion(),
            tx.threadId(),
            commit,
            tx.isInvalidate(),
            m.explicitLock(),
            tx.topologyVersion(),
            null,
            null,
            null,
            commit && tx.pessimistic() ? m.writes() : null,
            tx.syncCommit() && commit || tx.syncRollback() && !commit);

    // If this is the primary node for the keys.
    if (n.isLocal()) {
      req.miniId(GridUuid.randomUuid());

      if (CU.DHT_ENABLED) {
        GridFuture<GridCacheTx> fut =
            commit ? dht().commitTx(n.id(), req) : dht().rollbackTx(n.id(), req);

        // Add new future.
        add(fut);
      } else
        // Add done future for testing.
        add(new GridFinishedFuture<GridCacheTx>(ctx));
    } else {
      MiniFuture fut = new MiniFuture(m);

      req.miniId(fut.futureId());

      add(fut); // Append new future.

      try {
        cctx.io().send(n, req);

        // If we don't wait for result, then mark future as done.
        if (!isSync() && !m.explicitLock()) fut.onDone();
      } catch (GridTopologyException e) {
        // Remove previous mapping.
        mappings.remove(m.node().id());

        fut.onResult(e);
      } catch (GridException e) {
        // Fail the whole thing.
        fut.onResult(e);
      }
    }
  }
  /**
   * @param p Partition number.
   * @param topVer Topology version.
   * @param create Create flag.
   * @param updateSeq Update sequence.
   * @return Local partition.
   */
  private GridDhtLocalPartition localPartition(
      int p, AffinityTopologyVersion topVer, boolean create, boolean updateSeq) {
    while (true) {
      boolean belongs = cctx.affinity().localNode(p, topVer);

      GridDhtLocalPartition loc = locParts.get(p);

      if (loc != null && loc.state() == EVICTED) {
        locParts.remove(p, loc);

        if (!create) return null;

        if (!belongs)
          throw new GridDhtInvalidPartitionException(
              p,
              "Adding entry to evicted partition [part="
                  + p
                  + ", topVer="
                  + topVer
                  + ", this.topVer="
                  + this.topVer
                  + ']');

        continue;
      }

      if (loc == null && create) {
        if (!belongs)
          throw new GridDhtInvalidPartitionException(
              p,
              "Creating partition which does not belong [part="
                  + p
                  + ", topVer="
                  + topVer
                  + ", this.topVer="
                  + this.topVer
                  + ']');

        lock.writeLock().lock();

        try {
          GridDhtLocalPartition old =
              locParts.putIfAbsent(p, loc = new GridDhtLocalPartition(cctx, p));

          if (old != null) loc = old;
          else {
            if (updateSeq) this.updateSeq.incrementAndGet();

            if (log.isDebugEnabled()) log.debug("Created local partition: " + loc);
          }
        } finally {
          lock.writeLock().unlock();
        }
      }

      return loc;
    }
  }
  /** Clears swap entries for evicted partition. */
  private void clearSwap() {
    assert state() == EVICTED;
    assert !GridQueryProcessor.isEnabled(cctx.config())
        : "Indexing needs to have unswapped values.";

    try {
      GridCloseableIterator<Map.Entry<byte[], GridCacheSwapEntry>> it = cctx.swap().iterator(id);

      boolean isLocStore = cctx.store().isLocal();

      if (it != null) {
        // We can safely remove these values because no entries will be created for evicted
        // partition.
        while (it.hasNext()) {
          Map.Entry<byte[], GridCacheSwapEntry> entry = it.next();

          byte[] keyBytes = entry.getKey();

          KeyCacheObject key = cctx.toCacheKeyObject(keyBytes);

          cctx.swap().remove(key);

          if (isLocStore) cctx.store().remove(null, key.value(cctx.cacheObjectContext(), false));
        }
      }
    } catch (IgniteCheckedException e) {
      U.error(log, "Failed to clear swap for evicted partition: " + this, e);
    }
  }
  /**
   * @param entry Transaction entry.
   * @param nodes Nodes.
   */
  private void map(GridCacheTxEntry<K, V> entry, Collection<GridRichNode> nodes) {
    GridRichNode primary = CU.primary0(cctx.affinity(entry.key(), nodes));

    GridDistributedTxMapping<K, V> t = mappings.get(primary.id());

    if (t == null) mappings.put(primary.id(), t = new GridDistributedTxMapping<K, V>(primary));

    t.add(entry);
  }
  /** {@inheritDoc} */
  @Override
  public boolean onDone(GridCacheTx tx, Throwable err) {
    if (initialized() && super.onDone(tx, err)) {
      // Don't forget to clean up.
      cctx.mvcc().removeFuture(this);

      return true;
    }

    return false;
  }
  /** {@inheritDoc} */
  @Override
  public GridDhtPartitionMap localPartitionMap() {
    lock.readLock().lock();

    try {
      return new GridDhtPartitionMap(
          cctx.nodeId(), updateSeq.get(), F.viewReadOnly(locParts, CU.part2state()), true);
    } finally {
      lock.readLock().unlock();
    }
  }
  /**
   * @param cacheCtx Cache context.
   * @return {@code True} if local node can calculate affinity on it's own for this partition map
   *     exchange.
   */
  private boolean canCalculateAffinity(GridCacheContext cacheCtx) {
    AffinityFunction affFunc = cacheCtx.config().getAffinity();

    // Do not request affinity from remote nodes if affinity function is not centralized.
    if (!U.hasAnnotation(affFunc, AffinityCentralizedFunction.class)) return true;

    // If local node did not initiate exchange or local node is the only cache node in grid.
    Collection<ClusterNode> affNodes = CU.affinityNodes(cacheCtx, exchId.topologyVersion());

    return !exchId.nodeId().equals(cctx.localNodeId())
        || (affNodes.size() == 1 && affNodes.contains(cctx.localNode()));
  }
 /** {@inheritDoc} */
 @Override
 public GridFuture<Boolean> awaitAsync(final long timeout, final TimeUnit unit) {
   return ctx.closures()
       .callLocalSafe(
           new Callable<Boolean>() {
             @Override
             public Boolean call() throws Exception {
               return await(timeout, unit);
             }
           },
           true);
 }
  /**
   * @param key Removed key.
   * @param ver Removed version.
   * @throws IgniteCheckedException If failed.
   */
  public void onDeferredDelete(KeyCacheObject key, GridCacheVersion ver)
      throws IgniteCheckedException {
    try {
      T2<KeyCacheObject, GridCacheVersion> evicted = rmvQueue.add(new T2<>(key, ver));

      if (evicted != null) cctx.dht().removeVersionedEntry(evicted.get1(), evicted.get2());
    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();

      throw new IgniteInterruptedCheckedException(e);
    }
  }
  /**
   * @param updateSeq Update sequence.
   * @return Future for evict attempt.
   */
  IgniteInternalFuture<Boolean> tryEvictAsync(boolean updateSeq) {
    if (map.isEmpty()
        && !GridQueryProcessor.isEnabled(cctx.config())
        && state.compareAndSet(RENTING, EVICTED, 0, 0)) {
      if (log.isDebugEnabled()) log.debug("Evicted partition: " + this);

      clearSwap();

      if (cctx.isDrEnabled()) cctx.dr().partitionEvicted(id);

      cctx.dataStructures().onPartitionEvicted(id);

      rent.onDone();

      ((GridDhtPreloader) cctx.preloader()).onPartitionEvicted(this, updateSeq);

      clearDeferredDeletes();

      return new GridFinishedFuture<>(true);
    }

    return cctx.closures()
        .callLocalSafe(
            new GPC<Boolean>() {
              @Override
              public Boolean call() {
                return tryEvict(true);
              }
            }, /*system pool*/
            true);
  }
  /**
   * Default constructor.
   *
   * @param name Sequence name.
   * @param key Sequence key.
   * @param seqView Sequence projection.
   * @param ctx CacheContext.
   * @param locVal Local counter.
   * @param upBound Upper bound.
   */
  public GridCacheAtomicSequenceImpl(
      String name,
      GridCacheInternalStorableKey key,
      GridCacheProjection<GridCacheInternalStorableKey, GridCacheAtomicSequenceValue> seqView,
      GridCacheContext ctx,
      long locVal,
      long upBound) {
    assert key != null;
    assert seqView != null;
    assert ctx != null;
    assert locVal <= upBound;

    batchSize = ctx.config().getAtomicSequenceReserveSize();
    this.ctx = ctx;
    this.key = key;
    this.seqView = seqView;
    this.upBound = upBound;
    this.locVal = locVal;
    this.name = name;

    log = ctx.gridConfig().getGridLogger().getLogger(getClass());
  }
  /** {@inheritDoc} */
  @Override
  public void onRemoved(GridDhtCacheEntry e) {
    /*
     * Make sure not to acquire any locks here as this method
     * may be called from sensitive synchronization blocks.
     * ===================================================
     */

    GridDhtLocalPartition loc =
        localPartition(cctx.affinity().partition(e.key()), topologyVersion(), false);

    if (loc != null) loc.onRemoved(e);
  }
  /** {@inheritDoc} */
  @Override
  public AffinityTopologyVersion topologyVersion() {
    lock.readLock().lock();

    try {
      assert topVer.topologyVersion() > 0
          : "Invalid topology version [topVer=" + topVer + ", cacheName=" + cctx.name() + ']';

      return topVer;
    } finally {
      lock.readLock().unlock();
    }
  }
  /** @param nodeId Node to remove. */
  private void removeNode(UUID nodeId) {
    assert nodeId != null;
    assert lock.writeLock().isHeldByCurrentThread();

    ClusterNode oldest = CU.oldestAliveCacheServerNode(cctx.shared(), topVer);

    assert oldest != null;

    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();
    }
  }
  /**
   * @param node Node.
   * @param id ID.
   * @throws IgniteCheckedException If failed.
   */
  private void sendLocalPartitions(ClusterNode node, @Nullable GridDhtPartitionExchangeId id)
      throws IgniteCheckedException {
    GridDhtPartitionsSingleMessage m =
        new GridDhtPartitionsSingleMessage(
            id, cctx.kernalContext().clientNode(), cctx.versions().last());

    for (GridCacheContext cacheCtx : cctx.cacheContexts()) {
      if (!cacheCtx.isLocal())
        m.addLocalPartitionMap(cacheCtx.cacheId(), cacheCtx.topology().localPartitionMap());
    }

    if (log.isDebugEnabled())
      log.debug(
          "Sending local partitions [nodeId="
              + node.id()
              + ", exchId="
              + exchId
              + ", msg="
              + m
              + ']');

    cctx.io().send(node, m, SYSTEM_POOL);
  }