diff --git a/src/java/org/apache/cassandra/gms/Gossiper.java b/src/java/org/apache/cassandra/gms/Gossiper.java index b77064d5e6..8c36223986 100644 --- a/src/java/org/apache/cassandra/gms/Gossiper.java +++ b/src/java/org/apache/cassandra/gms/Gossiper.java @@ -678,6 +678,12 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } } + /** + * 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); @@ -688,6 +694,30 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean 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 unsafeStatuses = new ArrayList() {{ + 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()) @@ -1008,34 +1038,31 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean 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 epStateMap) diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index a256ce71c5..745fe4c922 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -455,8 +455,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE 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.",