@Override public void notifyTopologyChanged( CacheTopology oldTopology, CacheTopology newTopology, int newTopologyId, boolean pre) { if (!topologyChangedListeners.isEmpty()) { EventImpl<K, V> e = EventImpl.createEvent(cache, TOPOLOGY_CHANGED); e.setPre(pre); if (oldTopology != null) { e.setConsistentHashAtStart(oldTopology.getReadConsistentHash()); } e.setConsistentHashAtEnd(newTopology.getWriteConsistentHash()); e.setNewTopologyId(newTopologyId); for (CacheEntryListenerInvocation<K, V> listener : topologyChangedListeners) listener.invoke(e); } }
private Address findSource(int segmentId, Set<Address> excludedSources) { List<Address> owners = cacheTopology.getReadConsistentHash().locateOwnersForSegment(segmentId); if (owners.size() == 1 && owners.get(0).equals(rpcManager.getAddress())) { return null; } for (int i = owners.size() - 1; i >= 0; i--) { // iterate backwards because we prefer to fetch from newer nodes Address o = owners.get(i); if (!o.equals(rpcManager.getAddress()) && !excludedSources.contains(o)) { return o; } } log.noLiveOwnersFoundForSegment(segmentId, cacheName, owners, excludedSources); return null; }
private int getSegment(Object key) { // there we can use any CH version because the routing table is not involved return cacheTopology.getReadConsistentHash().getSegment(key); }
@Override public void onTopologyUpdate(CacheTopology cacheTopology, boolean isRebalance) { if (trace) log.tracef( "Received new CH %s for cache %s", cacheTopology.getWriteConsistentHash(), cacheName); int numStartedTopologyUpdates = activeTopologyUpdates.incrementAndGet(); if (isRebalance) { rebalanceInProgress.set(true); } final ConsistentHash previousCh = this.cacheTopology != null ? this.cacheTopology.getWriteConsistentHash() : null; // Ensures writes to the data container use the right consistent hash // No need for a try/finally block, since it's just an assignment stateTransferLock.acquireExclusiveTopologyLock(); this.cacheTopology = cacheTopology; if (numStartedTopologyUpdates == 1) { updatedKeys = new ConcurrentHashSet<Object>(); } stateTransferLock.releaseExclusiveTopologyLock(); stateTransferLock.notifyTopologyInstalled(cacheTopology.getTopologyId()); try { // fetch transactions and data segments from other owners if this is enabled if (isTransactional || isFetchEnabled) { Set<Integer> addedSegments; if (previousCh == null) { // we start fresh, without any data, so we need to pull everything we own according to // writeCh addedSegments = getOwnedSegments(cacheTopology.getWriteConsistentHash()); if (trace) { log.tracef("On cache %s we have: added segments: %s", cacheName, addedSegments); } } else { Set<Integer> previousSegments = getOwnedSegments(previousCh); Set<Integer> newSegments = getOwnedSegments(cacheTopology.getWriteConsistentHash()); Set<Integer> removedSegments = new HashSet<Integer>(previousSegments); removedSegments.removeAll(newSegments); // This is a rebalance, we need to request the segments we own in the new CH. addedSegments = new HashSet<Integer>(newSegments); addedSegments.removeAll(previousSegments); if (trace) { log.tracef( "On cache %s we have: removed segments: %s; new segments: %s; old segments: %s; added segments: %s", cacheName, removedSegments, newSegments, previousSegments, addedSegments); } // remove inbound transfers and any data for segments we no longer own cancelTransfers(removedSegments); // If L1.onRehash is enabled, "removed" segments are actually moved to L1. The new (and // old) owners // will automatically add the nodes that no longer own a key to that key's requestors // list. invalidateSegments(newSegments, removedSegments); // check if any of the existing transfers should be restarted from a different source // because the initial source is no longer a member Set<Address> members = new HashSet<Address>(cacheTopology.getReadConsistentHash().getMembers()); synchronized (this) { for (Iterator<Address> it = transfersBySource.keySet().iterator(); it.hasNext(); ) { Address source = it.next(); if (!members.contains(source)) { if (trace) { log.tracef( "Removing inbound transfers from source %s for cache %s", source, cacheName); } List<InboundTransferTask> inboundTransfers = transfersBySource.get(source); it.remove(); for (InboundTransferTask inboundTransfer : inboundTransfers) { // these segments will be restarted if they are still in new write CH if (trace) { log.tracef( "Removing inbound transfers for segments %s from source %s for cache %s", inboundTransfer.getSegments(), source, cacheName); } transfersBySegment.keySet().removeAll(inboundTransfer.getSegments()); addedSegments.addAll(inboundTransfer.getUnfinishedSegments()); } } } // exclude those that are already in progress from a valid source addedSegments.removeAll(transfersBySegment.keySet()); } } if (!addedSegments.isEmpty()) { addTransfers(addedSegments); // add transfers for new or restarted segments } } } finally { stateTransferLock.notifyTransactionDataReceived(cacheTopology.getTopologyId()); if (activeTopologyUpdates.decrementAndGet() == 0) { notifyEndOfTopologyUpdate(cacheTopology.getTopologyId()); } } }