use of org.apache.cassandra.dht.RangeStreamer.FetchReplica in project cassandra by apache.
the class StorageService method restoreReplicaCount.
/**
* Called when an endpoint is removed from the ring. This function checks
* whether this node becomes responsible for new ranges as a
* consequence and streams data if needed.
*
* This is rather ineffective, but it does not matter so much
* since this is called very seldom
*
* @param endpoint the node that left
*/
private void restoreReplicaCount(InetAddressAndPort endpoint, final InetAddressAndPort notifyEndpoint) {
Map<String, Multimap<InetAddressAndPort, FetchReplica>> replicasToFetch = new HashMap<>();
InetAddressAndPort myAddress = FBUtilities.getBroadcastAddressAndPort();
for (String keyspaceName : Schema.instance.getNonLocalStrategyKeyspaces()) {
logger.debug("Restoring replica count for keyspace {}", keyspaceName);
EndpointsByReplica changedReplicas = getChangedReplicasForLeaving(keyspaceName, endpoint, tokenMetadata, Keyspace.open(keyspaceName).getReplicationStrategy());
Set<LeavingReplica> myNewReplicas = new HashSet<>();
for (Map.Entry<Replica, Replica> entry : changedReplicas.flattenEntries()) {
Replica replica = entry.getValue();
if (replica.endpoint().equals(myAddress)) {
// Maybe we don't technically need to fetch transient data from somewhere
// but it's probably not a lot and it probably makes things a hair more resilient to people
// not running repair when they should.
myNewReplicas.add(new LeavingReplica(entry.getKey(), entry.getValue()));
}
}
logger.debug("Changed replicas for leaving {}, myNewReplicas {}", changedReplicas, myNewReplicas);
replicasToFetch.put(keyspaceName, getNewSourceReplicas(keyspaceName, myNewReplicas));
}
StreamPlan stream = new StreamPlan(StreamOperation.RESTORE_REPLICA_COUNT);
replicasToFetch.forEach((keyspaceName, sources) -> {
logger.debug("Requesting keyspace {} sources", keyspaceName);
sources.asMap().forEach((sourceAddress, fetchReplicas) -> {
logger.debug("Source and our replicas are {}", fetchReplicas);
// Remember whether this node is providing the full or transient replicas for this range. We are going
// to pass streaming the local instance of Replica for the range which doesn't tell us anything about the source
// By encoding it as two separate sets we retain this information about the source.
RangesAtEndpoint full = fetchReplicas.stream().filter(f -> f.remote.isFull()).map(f -> f.local).collect(RangesAtEndpoint.collector(myAddress));
RangesAtEndpoint transientReplicas = fetchReplicas.stream().filter(f -> f.remote.isTransient()).map(f -> f.local).collect(RangesAtEndpoint.collector(myAddress));
if (logger.isDebugEnabled())
logger.debug("Requesting from {} full replicas {} transient replicas {}", sourceAddress, StringUtils.join(full, ", "), StringUtils.join(transientReplicas, ", "));
stream.requestRanges(sourceAddress, keyspaceName, full, transientReplicas);
});
});
StreamResultFuture future = stream.execute();
future.addCallback(new FutureCallback<StreamState>() {
public void onSuccess(StreamState finalState) {
sendReplicationNotification(notifyEndpoint);
}
public void onFailure(Throwable t) {
logger.warn("Streaming to restore replica count failed", t);
// We still want to send the notification
sendReplicationNotification(notifyEndpoint);
}
});
}
use of org.apache.cassandra.dht.RangeStreamer.FetchReplica in project cassandra by apache.
the class StorageService method getNewSourceReplicas.
/**
* Finds living endpoints responsible for the given ranges
*
* @param keyspaceName the keyspace ranges belong to
* @param leavingReplicas the ranges to find sources for
* @return multimap of addresses to ranges the address is responsible for
*/
private Multimap<InetAddressAndPort, FetchReplica> getNewSourceReplicas(String keyspaceName, Set<LeavingReplica> leavingReplicas) {
InetAddressAndPort myAddress = FBUtilities.getBroadcastAddressAndPort();
EndpointsByRange rangeReplicas = Keyspace.open(keyspaceName).getReplicationStrategy().getRangeAddresses(tokenMetadata.cloneOnlyTokenMap());
Multimap<InetAddressAndPort, FetchReplica> sourceRanges = HashMultimap.create();
IFailureDetector failureDetector = FailureDetector.instance;
logger.debug("Getting new source replicas for {}", leavingReplicas);
// find alive sources for our new ranges
for (LeavingReplica leaver : leavingReplicas) {
// We need this to find the replicas from before leaving to supply the data
Replica leavingReplica = leaver.leavingReplica;
// We need this to know what to fetch and what the transient status is
Replica ourReplica = leaver.ourReplica;
// If we are going to be a full replica only consider full replicas
Predicate<Replica> replicaFilter = ourReplica.isFull() ? Replica::isFull : Predicates.alwaysTrue();
Predicate<Replica> notSelf = replica -> !replica.endpoint().equals(myAddress);
EndpointsForRange possibleReplicas = rangeReplicas.get(leavingReplica.range());
logger.info("Possible replicas for newReplica {} are {}", ourReplica, possibleReplicas);
IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch();
EndpointsForRange sortedPossibleReplicas = snitch.sortedByProximity(myAddress, possibleReplicas);
logger.info("Sorted possible replicas starts as {}", sortedPossibleReplicas);
Optional<Replica> myCurrentReplica = tryFind(possibleReplicas, replica -> replica.endpoint().equals(myAddress)).toJavaUtil();
boolean transientToFull = myCurrentReplica.isPresent() && myCurrentReplica.get().isTransient() && ourReplica.isFull();
assert !sortedPossibleReplicas.endpoints().contains(myAddress) || transientToFull : String.format("My address %s, sortedPossibleReplicas %s, myCurrentReplica %s, myNewReplica %s", myAddress, sortedPossibleReplicas, myCurrentReplica, ourReplica);
// Originally this didn't log if it couldn't restore replication and that seems wrong
boolean foundLiveReplica = false;
for (Replica possibleReplica : sortedPossibleReplicas.filter(Predicates.and(replicaFilter, notSelf))) {
if (failureDetector.isAlive(possibleReplica.endpoint())) {
foundLiveReplica = true;
sourceRanges.put(possibleReplica.endpoint(), new FetchReplica(ourReplica, possibleReplica));
break;
} else {
logger.debug("Skipping down replica {}", possibleReplica);
}
}
if (!foundLiveReplica) {
logger.warn("Didn't find live replica to restore replication for " + ourReplica);
}
}
return sourceRanges;
}
use of org.apache.cassandra.dht.RangeStreamer.FetchReplica in project cassandra by apache.
the class BootStrapperTest method testSourceTargetComputation.
private RangeStreamer testSourceTargetComputation(String keyspaceName, int numOldNodes, int replicationFactor) throws UnknownHostException {
StorageService ss = StorageService.instance;
TokenMetadata tmd = ss.getTokenMetadata();
generateFakeEndpoints(numOldNodes);
Token myToken = tmd.partitioner.getRandomToken();
InetAddressAndPort myEndpoint = InetAddressAndPort.getByName("127.0.0.1");
assertEquals(numOldNodes, tmd.sortedTokens().size());
IFailureDetector mockFailureDetector = new IFailureDetector() {
public boolean isAlive(InetAddressAndPort ep) {
return true;
}
public void interpret(InetAddressAndPort ep) {
throw new UnsupportedOperationException();
}
public void report(InetAddressAndPort ep) {
throw new UnsupportedOperationException();
}
public void registerFailureDetectionEventListener(IFailureDetectionEventListener listener) {
throw new UnsupportedOperationException();
}
public void unregisterFailureDetectionEventListener(IFailureDetectionEventListener listener) {
throw new UnsupportedOperationException();
}
public void remove(InetAddressAndPort ep) {
throw new UnsupportedOperationException();
}
public void forceConviction(InetAddressAndPort ep) {
throw new UnsupportedOperationException();
}
};
RangeStreamer s = new RangeStreamer(tmd, null, myEndpoint, StreamOperation.BOOTSTRAP, true, DatabaseDescriptor.getEndpointSnitch(), new StreamStateStore(), mockFailureDetector, false, 1);
assertNotNull(Keyspace.open(keyspaceName));
s.addRanges(keyspaceName, Keyspace.open(keyspaceName).getReplicationStrategy().getPendingAddressRanges(tmd, myToken, myEndpoint));
Multimap<InetAddressAndPort, FetchReplica> toFetch = s.toFetch().get(keyspaceName);
// Check we get get RF new ranges in total
assertEquals(replicationFactor, toFetch.size());
// is used, they will vary.
assert toFetch.values().size() > 0;
assert toFetch.keys().stream().noneMatch(myEndpoint::equals);
return s;
}
Aggregations