This is an automated email from the ASF dual-hosted git repository.

dcapwell pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 2d80e99  Unable to ALTER KEYSPACE while decommissioned/assassinated 
nodes are in gossip
2d80e99 is described below

commit 2d80e9904b46474b3a7cc135690c27511a234713
Author: Jon Meredith <[email protected]>
AuthorDate: Thu Feb 11 14:20:57 2021 -0800

    Unable to ALTER KEYSPACE while decommissioned/assassinated nodes are in 
gossip
    
    patch by Jon Meredith; reviewed by Brandon Williams, David Capwell for 
CASSANDRA-16422
---
 CHANGES.txt                                         |  1 +
 .../statements/schema/AlterKeyspaceStatement.java   |  6 +++++-
 src/java/org/apache/cassandra/gms/Gossiper.java     | 21 ++++++++++++++++++++-
 .../AssassinateAbruptDownedNodeTest.java            | 20 ++++++++++++++++++++
 .../test/hostreplacement/BaseAssassinatedCase.java  |  7 +++++++
 5 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 0883a8f..9f6ee1b 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0-beta5
+ * Unable to ALTER KEYSPACE while decommissioned/assassinated nodes are in 
gossip (CASSANDRA-16422)
  * Metrics backward compatibility restored after CASSANDRA-15066 
(CASSANDRA-16083)
  * Reduce new reserved keywords introduced since 3.0 (CASSANDRA-16439)
  * Improve system tables handling in case of disk failures (CASSANDRA-14793)
diff --git 
a/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
 
b/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
index 2f0c188..2ef890e 100644
--- 
a/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
+++ 
b/src/java/org/apache/cassandra/cql3/statements/schema/AlterKeyspaceStatement.java
@@ -112,7 +112,11 @@ public final class AlterKeyspaceStatement extends 
AlterSchemaStatement
         if (allow_alter_rf_during_range_movement)
             return;
 
-        Stream<InetAddressAndPort> endpoints = 
Stream.concat(Gossiper.instance.getLiveMembers().stream(), 
Gossiper.instance.getUnreachableMembers().stream());
+        Stream<InetAddressAndPort> unreachableNotAdministrativelyInactive =
+            Gossiper.instance.getUnreachableMembers().stream().filter(endpoint 
-> !FBUtilities.getBroadcastAddressAndPort().equals(endpoint) &&
+                                                                      
!Gossiper.instance.isAdministrativelyInactiveState(endpoint));
+        Stream<InetAddressAndPort> endpoints = 
Stream.concat(Gossiper.instance.getLiveMembers().stream(),
+                                                             
unreachableNotAdministrativelyInactive);
         List<InetAddressAndPort> notNormalEndpoints = 
endpoints.filter(endpoint -> 
!FBUtilities.getBroadcastAddressAndPort().equals(endpoint) &&
                                                                                
    !Gossiper.instance.getEndpointStateForEndpoint(endpoint).isNormalState())
                                                                
.collect(Collectors.toList());
diff --git a/src/java/org/apache/cassandra/gms/Gossiper.java 
b/src/java/org/apache/cassandra/gms/Gossiper.java
index 74abea3..1129c29 100644
--- a/src/java/org/apache/cassandra/gms/Gossiper.java
+++ b/src/java/org/apache/cassandra/gms/Gossiper.java
@@ -105,7 +105,9 @@ public class Gossiper implements 
IFailureDetectionEventListener, GossiperMBean
         SILENT_SHUTDOWN_STATES.add(VersionedValue.STATUS_BOOTSTRAPPING);
         
SILENT_SHUTDOWN_STATES.add(VersionedValue.STATUS_BOOTSTRAPPING_REPLACE);
     }
-
+    private static List<String> ADMINISTRATIVELY_INACTIVE_STATES = 
Arrays.asList(VersionedValue.HIBERNATE,
+                                                                               
  VersionedValue.REMOVED_TOKEN,
+                                                                               
  VersionedValue.STATUS_LEFT);
     private volatile ScheduledFuture<?> scheduledGossipTask;
     private static final ReentrantLock taskLock = new ReentrantLock();
     public final static int intervalInMillis = 1000;
@@ -1301,6 +1303,23 @@ public class Gossiper implements 
IFailureDetectionEventListener, GossiperMBean
         return SILENT_SHUTDOWN_STATES.contains(status);
     }
 
+    public boolean isAdministrativelyInactiveState(EndpointState epState)
+    {
+        String status = getGossipStatus(epState);
+        if (status.isEmpty())
+            return false;
+
+        return ADMINISTRATIVELY_INACTIVE_STATES.contains(status);
+    }
+
+    public boolean isAdministrativelyInactiveState(InetAddressAndPort endpoint)
+    {
+        EndpointState epState = getEndpointStateForEndpoint(endpoint);
+        if (epState == null)
+            return true; // if the end point cannot be found, treat as inactive
+        return isAdministrativelyInactiveState(epState);
+    }
+
     private static String getGossipStatus(EndpointState epState)
     {
         if (epState == null)
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/AssassinateAbruptDownedNodeTest.java
 
b/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/AssassinateAbruptDownedNodeTest.java
index 95fa767..d377300 100644
--- 
a/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/AssassinateAbruptDownedNodeTest.java
+++ 
b/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/AssassinateAbruptDownedNodeTest.java
@@ -18,8 +18,12 @@
 
 package org.apache.cassandra.distributed.test.hostreplacement;
 
+import java.net.InetSocketAddress;
+
 import org.apache.cassandra.distributed.Cluster;
 import org.apache.cassandra.distributed.api.IInvokableInstance;
+import org.apache.cassandra.gms.Gossiper;
+import org.apache.cassandra.locator.InetAddressAndPort;
 
 import static org.apache.cassandra.distributed.shared.ClusterUtils.stopAbrupt;
 
@@ -36,4 +40,20 @@ public class AssassinateAbruptDownedNodeTest extends 
BaseAssassinatedCase
     {
         stopAbrupt(cluster, nodeToRemove);
     }
+
+    @Override
+    protected void afterNodeStatusIsLeft(Cluster cluster, IInvokableInstance 
removedNode)
+    {
+        // Check it is possible to alter keyspaces (see CASSANDRA-16422)
+
+        // First, make sure the node is convicted so the gossiper considers it 
unreachable
+        InetSocketAddress socketAddress = 
removedNode.config().broadcastAddress();
+        InetAddressAndPort removedEndpoint = 
InetAddressAndPort.getByAddressOverrideDefaults(socketAddress.getAddress(),
+                                                                               
              socketAddress.getPort());
+        cluster.get(BaseAssassinatedCase.SEED_NUM).runOnInstance(() -> 
Gossiper.instance.convict(removedEndpoint, 1.0));
+
+        // Second, try and alter the keyspace.  Before the bug was fixed, this 
would fail as the check includes
+        // unreachable nodes that could have LEFT status.
+        cluster.schemaChangeIgnoringStoppedInstances(String.format("ALTER 
KEYSPACE %s WITH DURABLE_WRITES = false", KEYSPACE));
+    }
 }
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/BaseAssassinatedCase.java
 
b/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/BaseAssassinatedCase.java
index 4d4768e..353732c 100644
--- 
a/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/BaseAssassinatedCase.java
+++ 
b/test/distributed/org/apache/cassandra/distributed/test/hostreplacement/BaseAssassinatedCase.java
@@ -46,6 +46,10 @@ public abstract class BaseAssassinatedCase extends 
TestBaseImpl
 
     abstract void consume(Cluster cluster, IInvokableInstance nodeToRemove);
 
+    protected void afterNodeStatusIsLeft(Cluster cluster, IInvokableInstance 
nodeToRemove)
+    {
+    }
+
     protected String expectedMessage(IInvokableInstance nodeToRemove)
     {
         return "Cannot replace token " + getTokens(nodeToRemove).get(0) + " 
which does not exist!";
@@ -77,6 +81,9 @@ public abstract class BaseAssassinatedCase extends 
TestBaseImpl
             // wait until the peer sees this assassination
             awaitGossipStatus(seed, nodeToRemove, "LEFT");
 
+            // Any extra checks to run after the node has been as LEFT
+            afterNodeStatusIsLeft(cluster, nodeToRemove);
+
             // allow replacing nodes with the LEFT state, this should fail 
since the token isn't in the ring
             assertThatThrownBy(() ->
                                replaceHostAndStart(cluster, nodeToRemove, 
properties -> {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to