This is an automated email from the ASF dual-hosted git repository. bdeggleston pushed a commit to branch cassandra-3.0 in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-3.0 by this push: new fee7a10 Prevent invoking enable/disable gossip when not in NORMAL fee7a10 is described below commit fee7a10823da1e29bd0e6504fea9679389180c9e Author: yifan-c <yc25c...@gmail.com> AuthorDate: Mon Sep 28 13:11:05 2020 -0700 Prevent invoking enable/disable gossip when not in NORMAL Patch by Yifan Cai; Reviewed by Brandon Williams and Blake Eggleston for CASSANDRA-16146 --- CHANGES.txt | 1 + .../apache/cassandra/service/StorageService.java | 18 +++- .../cassandra/distributed/test/GossipTest.java | 114 ++++++++++++++++++++- 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0f016d0..bfc0a81 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 3.0.23: + * Prevent invoking enable/disable gossip when not in NORMAL (CASSANDRA-16146) * Fix OOM when terminating repair session (CASSANDRA-15902) * Avoid marking shutting down nodes as up after receiving gossip shutdown message (CASSANDRA-16094) * Check SSTables for latest version before dropping compact storage (CASSANDRA-16063) diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index 1e544b2..7645091 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -323,6 +323,9 @@ public class StorageService extends NotificationBroadcasterSupport implements IE { if (initialized) { + if (!isNormal()) + throw new IllegalStateException("Unable to stop gossip because the node is not in the normal state. Try to stop the node instead."); + logger.warn("Stopping gossip by operator request"); if (isNativeTransportRunning()) @@ -1221,6 +1224,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE DatabaseDescriptor.setIncrementalBackupsEnabled(value); } + @VisibleForTesting // only used by test + public void setMovingModeUnsafe() { + setMode(Mode.MOVING, true); + } + private void setMode(Mode m, boolean log) { setMode(m, null, log); @@ -3690,7 +3698,7 @@ public class StorageService extends NotificationBroadcasterSupport implements IE throw new UnsupportedOperationException("local node is not a member of the token ring yet"); if (tokenMetadata.cloneAfterAllLeft().sortedTokens().size() < 2) throw new UnsupportedOperationException("no other normal nodes in the ring; decommission would be pointless"); - if (operationMode != Mode.NORMAL) + if (!isNormal()) throw new UnsupportedOperationException("Node in " + operationMode + " state; wait for status to become normal or restart"); PendingRangeCalculatorService.instance.blockUntilFinished(); @@ -4209,6 +4217,11 @@ public class StorageService extends NotificationBroadcasterSupport implements IE return operationMode == Mode.DRAINING; } + public boolean isNormal() + { + return operationMode == Mode.NORMAL; + } + public String getDrainProgress() { return String.format("Drained %s/%s ColumnFamilies", remainingCFs, totalCFs); @@ -4371,6 +4384,9 @@ public class StorageService extends NotificationBroadcasterSupport implements IE if (isShutdown()) // do not rely on operationMode in case it gets changed to decomissioned or other throw new IllegalStateException(String.format("Unable to start %s because the node was drained.", service)); + + if (!isNormal()) + throw new IllegalStateException(String.format("Unable to start %s because the node is not in the normal state.", service)); } // Never ever do this at home. Used by tests. diff --git a/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java b/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java index 83c62c8..32ecb95 100644 --- a/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java +++ b/test/distributed/org/apache/cassandra/distributed/test/GossipTest.java @@ -18,16 +18,25 @@ package org.apache.cassandra.distributed.test; +import java.io.Closeable; import java.net.InetAddress; import java.util.Collection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; import java.util.stream.Collectors; import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.Uninterruptibles; import org.junit.Assert; import org.junit.Test; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; +import net.bytebuddy.implementation.MethodDelegation; import org.apache.cassandra.dht.Token; import org.apache.cassandra.distributed.Cluster; import org.apache.cassandra.gms.ApplicationState; @@ -36,6 +45,8 @@ import org.apache.cassandra.gms.Gossiper; import org.apache.cassandra.service.StorageService; import org.apache.cassandra.utils.FBUtilities; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import static org.apache.cassandra.distributed.api.Feature.GOSSIP; import static org.apache.cassandra.distributed.api.Feature.NETWORK; @@ -111,5 +122,106 @@ public class GossipTest extends TestBaseImpl Assert.assertEquals(expectTokens, tokens); } } - + + public static class BBBootstrapInterceptor + { + final static CountDownLatch bootstrapReady = new CountDownLatch(1); + final static CountDownLatch bootstrapStart = new CountDownLatch(1); + static void install(ClassLoader cl, int nodeNumber) + { + if (nodeNumber != 2) + return; + new ByteBuddy().rebase(StorageService.class) + .method(named("bootstrap").and(takesArguments(1))) + .intercept(MethodDelegation.to(BBBootstrapInterceptor.class)) + .make() + .load(cl, ClassLoadingStrategy.Default.INJECTION); + } + + public static boolean bootstrap(Collection<Token> tokens) throws Exception + { + bootstrapStart.countDown(); + Uninterruptibles.awaitUninterruptibly(bootstrapReady); + return false; // bootstrap fails + } + } + + @Test + public void testPreventStoppingGossipDuringBootstrap() throws Exception + { + ExecutorService es = Executors.newFixedThreadPool(1); + try (Cluster cluster = builder().withNodes(2) + .withConfig(config -> config.with(GOSSIP) + .with(NETWORK) + .set("auto_bootstrap", true)) + .withInstanceInitializer(BBBootstrapInterceptor::install) + .createWithoutStarting(); + Closeable ignored = es::shutdown) + { + Runnable test = () -> + { + try + { + cluster.get(2).runOnInstance(() -> { + Uninterruptibles.awaitUninterruptibly(BBBootstrapInterceptor.bootstrapStart); + BBBootstrapInterceptor.bootstrapReady.countDown(); + try + { + StorageService.instance.stopGossiping(); + + Assert.fail("stopGossiping did not fail!"); + } + catch (Exception ex) + { + Assert.assertSame(ex.getClass(), IllegalStateException.class); + Assert.assertEquals(ex.getMessage(), "Unable to stop gossip because the node is not in the normal state. Try to stop the node instead."); + } + }); + } + finally + { + // shut down the node2 to speed up cluster startup. + cluster.get(2).shutdown(); + } + }; + Future<?> testResult = es.submit(test); + try + { + cluster.startup(); + } + catch (Exception ex) { + // ignore exceptions from startup process. More interested in the test result. + } + testResult.get(); + } + + es.awaitTermination(5, TimeUnit.SECONDS); + } + + @Test + public void testPreventEnablingGossipDuringMove() throws Exception + { + try (Cluster cluster = builder().withNodes(2) + .withConfig(config -> config.with(GOSSIP) + .with(NETWORK)) + .start()) + { + cluster.get(1).runOnInstance(() -> { + StorageService.instance.stopGossiping(); + StorageService.instance.setMovingModeUnsafe(); + try + { + StorageService.instance.startGossiping(); + + Assert.fail("startGossiping did not fail!"); + } + catch (Exception ex) + { + Assert.assertSame(ex.getClass(), IllegalStateException.class); + Assert.assertEquals(ex.getMessage(), "Unable to start gossip because the node is not in the normal state."); + } + }); + } + } + } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org