[ 
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]

Reply via email to