This is an automated email from the ASF dual-hosted git repository. samt pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit 62bd9ab1ae206a3582eebbff9dbfa8855f479333 Author: Sam Tunnicliffe <[email protected]> AuthorDate: Fri Nov 7 17:29:21 2025 +0000 Gossip entries for hibernating non-members don't block truncate Patch by Sam Tunnicliffe; reviewed by Marcus Eriksson for CASSANDRA-21003 --- CHANGES.txt | 1 + src/java/org/apache/cassandra/gms/Gossiper.java | 5 ++ .../test/HibernatingGossipOnlyMembersTest.java | 67 ++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index bf947bc7e2..0f7cf787f4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.1 + * Gossip entries for hibernating non-members don't block truncate (CASSANDRA-21003) * Retry without time limit calculates wait time incorrectly (CASSANDRA-21002) * Don't submit AlterSchemaStatements which produce no effect locally to the CMS (CASSANDRA-21001) * Avoid iterating all prepared statements when getting PreparedStatementsCacheSize metric (CASSANDRA-21038) diff --git a/src/java/org/apache/cassandra/gms/Gossiper.java b/src/java/org/apache/cassandra/gms/Gossiper.java index d6aebf1ba8..386bb0e13a 100644 --- a/src/java/org/apache/cassandra/gms/Gossiper.java +++ b/src/java/org/apache/cassandra/gms/Gossiper.java @@ -402,6 +402,11 @@ public class Gossiper implements IFailureDetectionEventListener, GossiperMBean, for (InetAddressAndPort endpoint : unreachableEndpoints.keySet()) { NodeId nodeId = metadata.directory.peerId(endpoint); + // If the endpoint is not known to cluster metadata, the peer is probably a leftover of upgrading as + // long-gone hibernating peers may persist indefinitely. By definition though, such a peer cannot be + // a token owner, so it is safe to exclude it. + if (nodeId == null) + continue; NodeState state = metadata.directory.peerState(nodeId); switch (state) { diff --git a/test/distributed/org/apache/cassandra/distributed/test/HibernatingGossipOnlyMembersTest.java b/test/distributed/org/apache/cassandra/distributed/test/HibernatingGossipOnlyMembersTest.java new file mode 100644 index 0000000000..4a0c1dd039 --- /dev/null +++ b/test/distributed/org/apache/cassandra/distributed/test/HibernatingGossipOnlyMembersTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.cassandra.distributed.test; + +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import org.apache.cassandra.distributed.Cluster; +import org.apache.cassandra.distributed.api.ConsistencyLevel; +import org.apache.cassandra.distributed.api.IIsolatedExecutor; +import org.apache.cassandra.gms.ApplicationState; +import org.apache.cassandra.gms.EndpointState; +import org.apache.cassandra.gms.Gossiper; +import org.apache.cassandra.gms.HeartBeatState; +import org.apache.cassandra.gms.VersionedValue; +import org.apache.cassandra.locator.InetAddressAndPort; + +public class HibernatingGossipOnlyMembersTest extends TestBaseImpl +{ + @Test + public void testTruncationWithHibernatingGossipOnlyNodes() throws Exception + { + try (Cluster cluster = init(Cluster.build(2) + .start())) + { + cluster.schemaChange("create table " + KEYSPACE + ".tbl (id int primary key, t int)"); + cluster.get(1).runOnInstance(injectPeerIntoGossip("127.0.0.99")); + // Even though the hibernating peer is considered unreachable, it won't prevent the truncation + cluster.coordinator(1).execute("TRUNCATE " + KEYSPACE + ".tbl", ConsistencyLevel.ALL); + } + } + + /** + * Add a hibernating peer to gossip which is not a member of the cluster. This can be a legacy + * of upgrades, where previously this kind of non-member were never purged from gossip. + */ + private IIsolatedExecutor.SerializableRunnable injectPeerIntoGossip(String address) + { + return () -> { + Map<ApplicationState, VersionedValue> appState = new EnumMap<>(ApplicationState.class); + appState.put(ApplicationState.STATUS_WITH_PORT, VersionedValue.unsafeMakeVersionedValue("hibernate,true", 1)); + EndpointState epState = new EndpointState(HeartBeatState.empty(), appState); + Map<InetAddressAndPort, EndpointState> hibernating = new HashMap<>(); + hibernating.put(InetAddressAndPort.getByNameUnchecked(address), epState); + Gossiper.runInGossipStageBlocking(() -> Gossiper.instance.applyStateLocally(hibernating)); + }; + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
