[
https://issues.apache.org/jira/browse/CASSANDRA-21097?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
ConfX updated CASSANDRA-21097:
------------------------------
Description:
The `ClusterUtils.parseGossipInfo` method in the distributed test framework
parses the gossip info output from `nodetool gossipinfo` by checking the line
starts with "/". It expects endpoint addresses to start with "/" correctly.
But when there is a node restart, the actual format from Java's
`InetAddress.toString()` is changed to "hostname/ipaddress" (e.g.,
`localhost/127.0.0.1`).
{code:java}
private static Map<String, Map<String, String>> parseGossipInfo(String str)
{
Map<String, Map<String, String>> map = new HashMap<>();
String[] lines = str.split("\n");
String currentInstance = null;
for (String line : lines)
{
if (line.startsWith("/")) // BUG: This condition is incorrect
{
// start of new instance
currentInstance = line;
continue;
}
Objects.requireNonNull(currentInstance); // NPE thrown here
String[] kv = line.trim().split(":", 2);
assert kv.length == 2 : "When splitting line '" + line + "' expected 2
parts but not true";
Map<String, String> state = map.computeIfAbsent(currentInstance, ignore
-> new HashMap<>());
state.put(kv[0], kv[1]);
}
return map;
} {code}
The method checks `if (line.startsWith("/"))` to identify endpoint address
lines. However, the actual gossip info output format after a node restart looks
like:
{code:java}
localhost/127.0.0.1 <-- Does NOT start with "/"
127.0.0.2/127.0.0.2 <-- Does NOT start with "/"{code}
The Java documentation for `InetAddress.toString()` states:
{code:java}
> "The string is of the form: hostname / literal IP address"{code}
h2. Actual Gossip Info Output after node restart
{code:java}
localhost/127.0.0.1
generation:1767679555
heartbeat:74
STATUS:23:NORMAL,-1
LOAD:27:144545.0
SCHEMA:17:d03783d7-b468-3c1a-82f1-8e30b2edde8b
DC:13:datacenter0
RACK:15:rack0
...
127.0.0.2/127.0.0.2
generation:1767679591
heartbeat:33
... {code}
The first line `localhost/127.0.0.1` does NOT start with "/" so it's not
recognized as an endpoint. When the method tries to process the next line `
generation:1767679555`, `currentInstance` is still null, causing the NPE.
h2. Stacktrace
{code:java}
java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:209)
at
org.apache.cassandra.distributed.shared.ClusterUtils.parseGossipInfo(ClusterUtils.java:691)
at
org.apache.cassandra.distributed.shared.ClusterUtils.gossipInfo(ClusterUtils.java:655)
at
org.apache.cassandra.distributed.shared.ClusterUtils.awaitGossip(ClusterUtils.java:554)
at
org.apache.cassandra.distributed.shared.ClusterUtils.awaitGossipSchemaMatch(ClusterUtils.java:600)
{code}
I'm happy to send a PR for this.
was:
The `ClusterUtils.parseGossipInfo` method in the distributed test framework
incorrectly parses the gossip info output from `nodetool gossipinfo`. It
expects endpoint addresses to start with "/" but the actual format from Java's
`InetAddress.toString()` is "hostname/ipaddress" (e.g., `localhost/127.0.0.1`).
{code:java}
private static Map<String, Map<String, String>> parseGossipInfo(String str)
{
Map<String, Map<String, String>> map = new HashMap<>();
String[] lines = str.split("\n");
String currentInstance = null;
for (String line : lines)
{
if (line.startsWith("/")) // BUG: This condition is incorrect
{
// start of new instance
currentInstance = line;
continue;
}
Objects.requireNonNull(currentInstance); // NPE thrown here
String[] kv = line.trim().split(":", 2);
assert kv.length == 2 : "When splitting line '" + line + "' expected 2
parts but not true";
Map<String, String> state = map.computeIfAbsent(currentInstance, ignore
-> new HashMap<>());
state.put(kv[0], kv[1]);
}
return map;
} {code}
The method checks `if (line.startsWith("/"))` to identify endpoint address
lines. However, the actual gossip info output format uses Java's
`InetAddress.toString()` which produces:
hostname/ipaddress, for example:
{code:java}
localhost/127.0.0.1 <-- Does NOT start with "/"
127.0.0.2/127.0.0.2 <-- Does NOT start with "/"{code}
The Java documentation for `InetAddress.toString()` states:
{code:java}
> "The string is of the form: hostname / literal IP address"{code}
h2. Actual Gossip Info Output
{code:java}
localhost/127.0.0.1
generation:1767679555
heartbeat:74
STATUS:23:NORMAL,-1
LOAD:27:144545.0
SCHEMA:17:d03783d7-b468-3c1a-82f1-8e30b2edde8b
DC:13:datacenter0
RACK:15:rack0
...
127.0.0.2/127.0.0.2
generation:1767679591
heartbeat:33
... {code}
The first line `localhost/127.0.0.1` does NOT start with "/" so it's not
recognized as an endpoint. When the method tries to process the next line `
generation:1767679555`, `currentInstance` is still null, causing the NPE.
h2. Stacktrace
{code:java}
java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:209)
at
org.apache.cassandra.distributed.shared.ClusterUtils.parseGossipInfo(ClusterUtils.java:691)
at
org.apache.cassandra.distributed.shared.ClusterUtils.gossipInfo(ClusterUtils.java:655)
at
org.apache.cassandra.distributed.shared.ClusterUtils.awaitGossip(ClusterUtils.java:554)
at
org.apache.cassandra.distributed.shared.ClusterUtils.awaitGossipSchemaMatch(ClusterUtils.java:600)
{code}
h2. Fix
Change the endpoint detection logic to recognize lines that contain "/" but
don't start with whitespace (key-value lines are indented with spaces):
{code:java}
private static Map<String, Map<String, String>> parseGossipInfo(String str)
{
Map<String, Map<String, String>> map = new HashMap<>();
String[] lines = str.split("\n");
String currentInstance = null;
for (String line : lines)
{
// Endpoint lines don't start with whitespace and contain "/"
// Key-value lines are indented with spaces and contain ":"
if (!line.isEmpty() && !Character.isWhitespace(line.charAt(0)) &&
line.contains("/"))
{
// start of new instance
currentInstance = line;
continue;
}
if (currentInstance == null || line.trim().isEmpty())
continue;
String[] kv = line.trim().split(":", 2);
if (kv.length != 2)
continue;
Map<String, String> state = map.computeIfAbsent(currentInstance, ignore
-> new HashMap<>());
state.put(kv[0], kv[1]);
}
return map;
} {code}
I'm happy to send a PR for this.
> ClusterUtils.parseGossipInfo Fails to Parse Gossip Output Format if there is
> a node restart
> -------------------------------------------------------------------------------------------
>
> Key: CASSANDRA-21097
> URL: https://issues.apache.org/jira/browse/CASSANDRA-21097
> Project: Apache Cassandra
> Issue Type: Bug
> Components: Test/dtest/java
> Reporter: ConfX
> Priority: Normal
>
> The `ClusterUtils.parseGossipInfo` method in the distributed test framework
> parses the gossip info output from `nodetool gossipinfo` by checking the line
> starts with "/". It expects endpoint addresses to start with "/" correctly.
> But when there is a node restart, the actual format from Java's
> `InetAddress.toString()` is changed to "hostname/ipaddress" (e.g.,
> `localhost/127.0.0.1`).
> {code:java}
> private static Map<String, Map<String, String>> parseGossipInfo(String str)
> {
> Map<String, Map<String, String>> map = new HashMap<>();
> String[] lines = str.split("\n");
> String currentInstance = null;
> for (String line : lines)
> {
> if (line.startsWith("/")) // BUG: This condition is incorrect
> {
> // start of new instance
> currentInstance = line;
> continue;
> }
> Objects.requireNonNull(currentInstance); // NPE thrown here
> String[] kv = line.trim().split(":", 2);
> assert kv.length == 2 : "When splitting line '" + line + "' expected
> 2 parts but not true";
> Map<String, String> state = map.computeIfAbsent(currentInstance,
> ignore -> new HashMap<>());
> state.put(kv[0], kv[1]);
> }
> return map;
> } {code}
> The method checks `if (line.startsWith("/"))` to identify endpoint address
> lines. However, the actual gossip info output format after a node restart
> looks like:
> {code:java}
> localhost/127.0.0.1 <-- Does NOT start with "/"
> 127.0.0.2/127.0.0.2 <-- Does NOT start with "/"{code}
> The Java documentation for `InetAddress.toString()` states:
> {code:java}
> > "The string is of the form: hostname / literal IP address"{code}
> h2. Actual Gossip Info Output after node restart
>
> {code:java}
> localhost/127.0.0.1
> generation:1767679555
> heartbeat:74
> STATUS:23:NORMAL,-1
> LOAD:27:144545.0
> SCHEMA:17:d03783d7-b468-3c1a-82f1-8e30b2edde8b
> DC:13:datacenter0
> RACK:15:rack0
> ...
> 127.0.0.2/127.0.0.2
> generation:1767679591
> heartbeat:33
> ... {code}
> The first line `localhost/127.0.0.1` does NOT start with "/" so it's not
> recognized as an endpoint. When the method tries to process the next line `
> generation:1767679555`, `currentInstance` is still null, causing the NPE.
>
> h2. Stacktrace
> {code:java}
> java.lang.NullPointerException
> at java.base/java.util.Objects.requireNonNull(Objects.java:209)
> at
> org.apache.cassandra.distributed.shared.ClusterUtils.parseGossipInfo(ClusterUtils.java:691)
> at
> org.apache.cassandra.distributed.shared.ClusterUtils.gossipInfo(ClusterUtils.java:655)
> at
> org.apache.cassandra.distributed.shared.ClusterUtils.awaitGossip(ClusterUtils.java:554)
> at
> org.apache.cassandra.distributed.shared.ClusterUtils.awaitGossipSchemaMatch(ClusterUtils.java:600)
> {code}
> I'm happy to send a PR for this.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]