diff --git a/CHANGES.txt b/CHANGES.txt index 66144ce1e6..ec0e7c60d7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -8,6 +8,7 @@ * Allow empty keystore_password in encryption_options (CASSANDRA-18778) * Skip ColumnFamilyStore#topPartitions initialization when client or tool mode (CASSANDRA-18697) Merged from 4.0: + * Fix Gossiper::hasMajorVersion3Nodes to return false during minor upgrade (CASSANDRA-18999) * Revert unnecessary read lock acquisition when reading ring version in TokenMetadata introduced in CASSANDRA-16286 (CASSANDRA-19107) * Support max SSTable size in sorted CQLSSTableWriter (CASSANDRA-18941) * Fix nodetool repair_admin summarize-pending command to not throw exception (CASSANDRA-19014) diff --git a/src/java/org/apache/cassandra/gms/Gossiper.java b/src/java/org/apache/cassandra/gms/Gossiper.java index 0d5db5f81c..018e20542d 100644 --- a/src/java/org/apache/cassandra/gms/Gossiper.java +++ b/src/java/org/apache/cassandra/gms/Gossiper.java @@ -187,6 +187,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * This property and anything that checks it should be removed in 5.0 */ private volatile boolean upgradeInProgressPossible = true; + private volatile boolean hasNodeWithUnknownVersion = false; public void clearUnsafe() { @@ -223,14 +224,14 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean } // Check the release version of all the peers it heard of. Not necessary the peer that it has/had contacted with. - boolean allHostsHaveKnownVersion = true; + hasNodeWithUnknownVersion = false; for (InetAddressAndPort host : endpointStateMap.keySet()) { CassandraVersion version = getReleaseVersion(host); //Raced with changes to gossip state, wait until next iteration if (version == null) - allHostsHaveKnownVersion = false; + hasNodeWithUnknownVersion = true; else if (version.compareTo(minVersion) < 0) minVersion = version; } @@ -238,7 +239,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean if (minVersion.compareTo(SystemKeyspace.CURRENT_VERSION) < 0) return new ExpiringMemoizingSupplier.Memoized<>(minVersion); - if (!allHostsHaveKnownVersion) + if (hasNodeWithUnknownVersion) return new ExpiringMemoizingSupplier.NotMemoized<>(minVersion); upgradeInProgressPossible = false; @@ -1526,7 +1527,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean EndpointState localEpStatePtr = endpointStateMap.get(ep); EndpointState remoteState = entry.getValue(); - if (!hasMajorVersion3Nodes()) + if (!hasMajorVersion3OrUnknownNodes()) remoteState.removeMajorVersion3LegacyApplicationStates(); /* @@ -1614,7 +1615,7 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean localState.addApplicationStates(updatedStates); // get rid of legacy fields once the cluster is not in mixed mode - if (!hasMajorVersion3Nodes()) + if (!hasMajorVersion3OrUnknownNodes()) localState.removeMajorVersion3LegacyApplicationStates(); // need to run STATUS or STATUS_WITH_PORT first to handle BOOT_REPLACE correctly (else won't be a member, so TOKENS won't be processed) @@ -2352,12 +2353,12 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean * Returns {@code false} only if the information about the version of each node in the cluster is available and * ALL the nodes are on 4.0+ (regardless of the patch version). */ - public boolean hasMajorVersion3Nodes() + public boolean hasMajorVersion3OrUnknownNodes() { return isUpgradingFromVersionLowerThan(CassandraVersion.CASSANDRA_4_0) || // this is quite obvious // however if we discovered only nodes at current version so far (in particular only this node), // but still there are nodes with unknown version, we also want to report that the cluster may have nodes at 3.x - upgradeInProgressPossible && !isUpgradingFromVersionLowerThan(SystemKeyspace.CURRENT_VERSION.familyLowerBound.get()); + hasNodeWithUnknownVersion; } /** diff --git a/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java b/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java index dc40093d4d..d63bbace79 100644 --- a/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java +++ b/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java @@ -227,7 +227,7 @@ public final class SystemDistributedKeyspace { // Don't record repair history if an upgrade is in progress as version 3 nodes generates errors // due to schema differences - boolean includeNewColumns = !Gossiper.instance.hasMajorVersion3Nodes(); + boolean includeNewColumns = !Gossiper.instance.hasMajorVersion3OrUnknownNodes(); InetAddressAndPort coordinator = FBUtilities.getBroadcastAddressAndPort(); Set participants = Sets.newHashSet(); diff --git a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java index c6ca1f28a0..78ccc3273a 100644 --- a/src/java/org/apache/cassandra/tracing/TraceKeyspace.java +++ b/src/java/org/apache/cassandra/tracing/TraceKeyspace.java @@ -122,7 +122,7 @@ public final class TraceKeyspace rb.ttl(ttl) .add("client", client) .add("coordinator", FBUtilities.getBroadcastAddressAndPort().getAddress()); - if (!Gossiper.instance.hasMajorVersion3Nodes()) + if (!Gossiper.instance.hasMajorVersion3OrUnknownNodes()) rb.add("coordinator_port", FBUtilities.getBroadcastAddressAndPort().getPort()); rb.add("request", request) .add("started_at", new Date(startedAt)) @@ -149,7 +149,7 @@ public final class TraceKeyspace rowBuilder.add("activity", message) .add("source", FBUtilities.getBroadcastAddressAndPort().getAddress()); - if (!Gossiper.instance.hasMajorVersion3Nodes()) + if (!Gossiper.instance.hasMajorVersion3OrUnknownNodes()) rowBuilder.add("source_port", FBUtilities.getBroadcastAddressAndPort().getPort()); rowBuilder.add("thread", threadName); diff --git a/test/unit/org/apache/cassandra/Util.java b/test/unit/org/apache/cassandra/Util.java index 0459cb3b3b..6ebd804c65 100644 --- a/test/unit/org/apache/cassandra/Util.java +++ b/test/unit/org/apache/cassandra/Util.java @@ -882,7 +882,7 @@ public class Util /** * Setups Gossiper to mimic the upgrade behaviour when {@link Gossiper#isUpgradingFromVersionLowerThan(CassandraVersion)} - * or {@link Gossiper#hasMajorVersion3Nodes()} is called. + * or {@link Gossiper#hasMajorVersion3OrUnknownNodes()} is called. */ public static void setUpgradeFromVersion(String version) { diff --git a/test/unit/org/apache/cassandra/gms/GossiperTest.java b/test/unit/org/apache/cassandra/gms/GossiperTest.java index 68841ea30a..f6c32067c6 100644 --- a/test/unit/org/apache/cassandra/gms/GossiperTest.java +++ b/test/unit/org/apache/cassandra/gms/GossiperTest.java @@ -136,7 +136,7 @@ public class GossiperTest assertFalse(Gossiper.instance.upgradeFromVersionSupplier.get().value().compareTo(new CassandraVersion("3.0")) < 0); assertTrue(Gossiper.instance.upgradeFromVersionSupplier.get().value().compareTo(new CassandraVersion("3.1")) < 0); - assertTrue(Gossiper.instance.hasMajorVersion3Nodes()); + assertTrue(Gossiper.instance.hasMajorVersion3OrUnknownNodes()); Gossiper.instance.endpointStateMap.remove(InetAddressAndPort.getByName("127.0.0.3")); Gossiper.instance.liveEndpoints.remove(InetAddressAndPort.getByName("127.0.0.3")); @@ -144,7 +144,7 @@ public class GossiperTest assertFalse(Gossiper.instance.upgradeFromVersionSupplier.get().value().compareTo(new CassandraVersion("3.0")) < 0); assertFalse(Gossiper.instance.upgradeFromVersionSupplier.get().value().compareTo(new CassandraVersion("3.1")) < 0); assertTrue(Gossiper.instance.upgradeFromVersionSupplier.get().value().compareTo(new CassandraVersion("3.12")) < 0); - assertTrue(Gossiper.instance.hasMajorVersion3Nodes()); + assertTrue(Gossiper.instance.hasMajorVersion3OrUnknownNodes()); Gossiper.instance.endpointStateMap.remove(InetAddressAndPort.getByName("127.0.0.2")); Gossiper.instance.liveEndpoints.remove(InetAddressAndPort.getByName("127.0.0.2")); @@ -152,6 +152,52 @@ public class GossiperTest assertEquals(SystemKeyspace.CURRENT_VERSION, Gossiper.instance.upgradeFromVersionSupplier.get().value()); } + @Test + public void testHasVersion3NodesShouldReturnFalseWhenNoVersion3NodesDetectedAndCassandra4UpgradeInProgress() throws Exception + { + Gossiper.instance.start(0); + Gossiper.instance.expireUpgradeFromVersion(); + + VersionedValue.VersionedValueFactory factory = new VersionedValue.VersionedValueFactory(null); + EndpointState es = new EndpointState((HeartBeatState) null); + es.addApplicationState(ApplicationState.RELEASE_VERSION, factory.releaseVersion(CURRENT_VERSION.toString())); + Gossiper.instance.endpointStateMap.put(InetAddressAndPort.getByName("127.0.0.1"), es); + Gossiper.instance.liveEndpoints.add(InetAddressAndPort.getByName("127.0.0.1")); + + es = new EndpointState((HeartBeatState) null); + String previousPatchVersion = String.valueOf(CURRENT_VERSION.major) + '.' + (CURRENT_VERSION.minor) + '.' + Math.max(CURRENT_VERSION.patch - 1, 0); + es.addApplicationState(ApplicationState.RELEASE_VERSION, factory.releaseVersion(previousPatchVersion)); + Gossiper.instance.endpointStateMap.put(InetAddressAndPort.getByName("127.0.0.2"), es); + Gossiper.instance.liveEndpoints.add(InetAddressAndPort.getByName("127.0.0.2")); + assertFalse(Gossiper.instance.hasMajorVersion3OrUnknownNodes()); + + Gossiper.instance.endpointStateMap.remove(InetAddressAndPort.getByName("127.0.0.2")); + Gossiper.instance.liveEndpoints.remove(InetAddressAndPort.getByName("127.0.0.2")); + } + + @Test + public void testHasVersion3NodesShouldReturnTrueWhenNoVersion3NodesDetectedButNotAllVersionsKnown() throws Exception + { + Gossiper.instance.start(0); + Gossiper.instance.expireUpgradeFromVersion(); + + VersionedValue.VersionedValueFactory factory = new VersionedValue.VersionedValueFactory(null); + EndpointState es = new EndpointState((HeartBeatState) null); + es.addApplicationState(ApplicationState.RELEASE_VERSION, null); + Gossiper.instance.endpointStateMap.put(InetAddressAndPort.getByName("127.0.0.3"), es); + Gossiper.instance.liveEndpoints.add(InetAddressAndPort.getByName("127.0.0.3")); + + es = new EndpointState((HeartBeatState) null); + String previousPatchVersion = String.valueOf(CURRENT_VERSION.major) + '.' + (CURRENT_VERSION.minor) + '.' + Math.max(CURRENT_VERSION.patch - 1, 0); + es.addApplicationState(ApplicationState.RELEASE_VERSION, factory.releaseVersion(previousPatchVersion)); + Gossiper.instance.endpointStateMap.put(InetAddressAndPort.getByName("127.0.0.2"), es); + Gossiper.instance.liveEndpoints.add(InetAddressAndPort.getByName("127.0.0.2")); + assertTrue(Gossiper.instance.hasMajorVersion3OrUnknownNodes()); + + Gossiper.instance.endpointStateMap.remove(InetAddressAndPort.getByName("127.0.0.2")); + Gossiper.instance.liveEndpoints.remove(InetAddressAndPort.getByName("127.0.0.2")); + } + @Test public void testLargeGenerationJump() throws UnknownHostException, InterruptedException {