Details
-
Bug
-
Status: Triage Needed
-
Normal
-
Resolution: Unresolved
-
None
-
None
-
All
-
None
Description
What happened
In the Cassandra dtest framework, when newInstanceConfig() is explicitly called in the test code for generating a new instance configuration, it will throw an unexpected index out-of-bound exception when getting tokens from tokenSupplier.
How to reproduce
Put the following test under cassandra/test/distributed/org/apache/cassandra/distributed/upgrade/, and build dtest jars. I'm using version pair [5.0-alpha1, 5.0-alpha2], and this failure happens also in older versions.
package org.apache.cassandra.distributed.upgrade; public class demoUpgradeTest extends UpgradeTestBase @Test public void demoTest() throws Throwable { new TestCase() .nodes(2) .nodesToUpgrade(1) .withConfig(config -> config.with(Feature.GOSSIP, Feature.NETWORK)) .upgradesToCurrentFrom(v3X) .setup((cluster) -> { // do nothing. }) .runAfterNodeUpgrade((cluster, node) -> { IInstanceConfig config = cluster.newInstanceConfig(); }).run(); } }
Run the test with
$ ant test-jvm-dtest-some-Duse.jdk11=true -Dtest.name=org.apache.cassandra.distributed.upgrade.demoUpgradeTest
You will see the following failure:
[junit-timeout] Testcase: demoUpgradeTest(org.apache.cassandra.distributed.upgrade.demoUpgradeTest)-_jdk11: FAILED [junit-timeout] Error in test '5.0-alpha2 -> [5.0-alpha2]' while upgrading to '5.0-alpha2'; successful upgrades [] [junit-timeout] junit.framework.AssertionFailedError: Error in test '5.0-alpha2 -> [5.0-alpha2]' while upgrading to '5.0-alpha2'; successful upgrades [] [junit-timeout] at org.apache.cassandra.distributed.upgrade.UpgradeTestBase$TestCase.run(UpgradeTestBase.java:442) [junit-timeout] at org.apache.cassandra.distributed.upgrade.demoUpgradeTest.demoTest(demoUpgradeTest.java:104) [junit-timeout] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [junit-timeout] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) [junit-timeout] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [junit-timeout] Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2 [junit-timeout] at org.apache.cassandra.distributed.api.TokenSupplier.lambda$evenlyDistributedTokens$0(TokenSupplier.java:59) [junit-timeout] at org.apache.cassandra.distributed.impl.AbstractCluster.createInstanceConfig(AbstractCluster.java:589) [junit-timeout] at org.apache.cassandra.distributed.impl.AbstractCluster.newInstanceConfig(AbstractCluster.java:582) [junit-timeout] at org.apache.cassandra.distributed.upgrade.demoUpgradeTest.lambda$demoTest$2(demoUpgradeTest.java:103) [junit-timeout] at org.apache.cassandra.distributed.upgrade.UpgradeTestBase$TestCase.run(UpgradeTestBase.java:433)
The failure is caused by the code below in AbstractCluster.java
public InstanceConfig newInstanceConfig() { return createInstanceConfig(size() + 1); // here in our example -> 2 + 1 } @VisibleForTesting InstanceConfig createInstanceConfig(int nodeNum) // nodeNum = 3 in the example { INodeProvisionStrategy provisionStrategy = nodeProvisionStrategy.create(subnet, portMap); Collection<String> tokens = tokenSupplier.tokens(nodeNum); // however, here, tokenSupplier only has 2 tokens; we are trying to get the index of 3, which triggers the out-of-bound exception. ... }
The code should carefully check the length of the tokenSupplier before actually fetching elements from it.