Skip to content

Commit

Permalink
checkForEndpointCollision fails for legitimate collisions
Browse files Browse the repository at this point in the history
patch by stefania; reviewed by jasobrown for CASSANDRA-9765
  • Loading branch information
jasobrown committed Jul 22, 2015
2 parents 0ef1888 + ba9a69e commit 046ff1e
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 21 deletions.
65 changes: 46 additions & 19 deletions src/java/org/apache/cassandra/gms/Gossiper.java
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,12 @@ private void doGossipToSeed(MessageOut<GossipDigestSyn> prod)
}
}

/**
* A fat client is a node that has not joined the ring, therefore acting as a coordinator only.
*
* @param endpoint - the endpoint to check
* @return true if it is a fat client
*/
public boolean isFatClient(InetAddress endpoint)
{
EndpointState epState = endpointStateMap.get(endpoint);
Expand All @@ -688,6 +694,30 @@ public boolean isFatClient(InetAddress endpoint)
return !isDeadState(epState) && !StorageService.instance.getTokenMetadata().isMember(endpoint);
}

/**
* Check if this endpoint can safely bootstrap into the cluster.
*
* @param endpoint - the endpoint to check
* @return true if the endpoint can join the cluster
*/
public boolean isSafeForBootstrap(InetAddress endpoint)
{
EndpointState epState = endpointStateMap.get(endpoint);

// if there's no previous state, or the node was previously removed from the cluster, we're good
if (epState == null || isDeadState(epState))
return true;

String status = getGossipStatus(epState);

// these states are not allowed to join the cluster as it would not be safe
final List<String> unsafeStatuses = new ArrayList<String>() {{
add(""); // failed bootstrap but we did start gossiping
add(VersionedValue.STATUS_NORMAL); // node is legit in the cluster or it was stopped with kill -9
add(VersionedValue.SHUTDOWN); }}; // node was shutdown
return !unsafeStatuses.contains(status);
}

private void doStatusCheck()
{
if (logger.isTraceEnabled())
Expand Down Expand Up @@ -1008,34 +1038,31 @@ private void handleMajorStateChange(InetAddress ep, EndpointState epState)

public boolean isDeadState(EndpointState epState)
{
if (epState.getApplicationState(ApplicationState.STATUS) == null)
String status = getGossipStatus(epState);
if (status.isEmpty())
return false;
String value = epState.getApplicationState(ApplicationState.STATUS).value;
String[] pieces = value.split(VersionedValue.DELIMITER_STR, -1);
assert (pieces.length > 0);
String state = pieces[0];
for (String deadstate : DEAD_STATES)
{
if (state.equals(deadstate))
return true;
}
return false;

return DEAD_STATES.contains(status);
}

public boolean isSilentShutdownState(EndpointState epState)
{
if (epState.getApplicationState(ApplicationState.STATUS) == null)
String status = getGossipStatus(epState);
if (status.isEmpty())
return false;

return SILENT_SHUTDOWN_STATES.contains(status);
}

private static String getGossipStatus(EndpointState epState)
{
if (epState == null || epState.getApplicationState(ApplicationState.STATUS) == null)
return "";

String value = epState.getApplicationState(ApplicationState.STATUS).value;
String[] pieces = value.split(VersionedValue.DELIMITER_STR, -1);
assert (pieces.length > 0);
String state = pieces[0];
for (String deadstate : SILENT_SHUTDOWN_STATES)
{
if (state.equals(deadstate))
return true;
}
return false;
return pieces[0];
}

void applyStateLocally(Map<InetAddress, EndpointState> epStateMap)
Expand Down
3 changes: 1 addition & 2 deletions src/java/org/apache/cassandra/service/StorageService.java
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,7 @@ public synchronized void checkForEndpointCollision() throws ConfigurationExcepti
if (!MessagingService.instance().isListening())
MessagingService.instance().listen(FBUtilities.getLocalAddress());
Gossiper.instance.doShadowRound();
EndpointState epState = Gossiper.instance.getEndpointStateForEndpoint(FBUtilities.getBroadcastAddress());
if (epState != null && !Gossiper.instance.isDeadState(epState) && !Gossiper.instance.isFatClient(FBUtilities.getBroadcastAddress()))
if (!Gossiper.instance.isSafeForBootstrap(FBUtilities.getBroadcastAddress()))
{
throw new RuntimeException(String.format("A node with address %s already exists, cancelling join. " +
"Use cassandra.replace_address if you want to replace this node.",
Expand Down

0 comments on commit 046ff1e

Please sign in to comment.