You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ad...@apache.org on 2021/09/10 12:29:43 UTC

[cassandra] branch cassandra-4.0 updated: Fix missed wait latencies in the output of `nodetool tpstats -F`

This is an automated email from the ASF dual-hosted git repository.

adelapena pushed a commit to branch cassandra-4.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/cassandra-4.0 by this push:
     new 636ab42  Fix missed wait latencies in the output of `nodetool tpstats -F`
636ab42 is described below

commit 636ab42bd5b2d2e55d37ce653daf23955f869a38
Author: Andrés de la Peña <a....@gmail.com>
AuthorDate: Fri Sep 10 13:20:46 2021 +0100

    Fix missed wait latencies in the output of `nodetool tpstats -F`
    
    patch by Dimitar Dimitrov and Andrés de la Peña; reviewed by Ekaterina Dimitrova for CASSANDRA-16938
    
    Co-authored-by: Andrés de la Peña <a....@gmail.com>
    Co-authored-by: Dimitar Dimitrov <dm...@gmail.com>
---
 CHANGES.txt                                        |   1 +
 .../tools/nodetool/stats/StatsPrinter.java         |  17 +-
 .../tools/nodetool/stats/TpStatsHolder.java        |  11 +-
 .../cassandra/tools/NodeToolTPStatsTest.java       |  12 +-
 .../tools/nodetool/stats/StatsPrinterTest.java     | 186 +++++++++++++++++++++
 5 files changed, 213 insertions(+), 14 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 1a487c4..b332be6 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0.2
+ * Fix missed wait latencies in the output of `nodetool tpstats -F` (CASSANDRA-16938)
  * Remove all the state pollution between tests in SSTableReaderTest (CASSANDRA-16888)
  * Delay auth setup until after gossip has settled to avoid unavailables on startup (CASSANDRA-16783)
  * Fix clustering order logic in CREATE MATERIALIZED VIEW (CASSANDRA-16898)
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java
index e67f33a..389efba 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsPrinter.java
@@ -18,9 +18,10 @@
 
 package org.apache.cassandra.tools.nodetool.stats;
 
+import java.io.IOException;
 import java.io.PrintStream;
 
-import org.json.simple.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.yaml.snakeyaml.DumperOptions;
 import org.yaml.snakeyaml.Yaml;
 
@@ -39,9 +40,17 @@ public interface StatsPrinter<T extends StatsHolder>
         @Override
         public void print(T data, PrintStream out)
         {
-            JSONObject json = new JSONObject();
-            json.putAll(data.convert2Map());
-            out.println(json.toString());
+            ObjectMapper mapper = new ObjectMapper();
+            try
+            {
+                String json = mapper.writerWithDefaultPrettyPrinter()
+                                    .writeValueAsString(data.convert2Map());
+                out.println(json);
+            }
+            catch (IOException e)
+            {
+                out.println(e);
+            }
         }
     }
 
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/TpStatsHolder.java b/src/java/org/apache/cassandra/tools/nodetool/stats/TpStatsHolder.java
index 38011ba..db9c298 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TpStatsHolder.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TpStatsHolder.java
@@ -18,7 +18,6 @@
 
 package org.apache.cassandra.tools.nodetool.stats;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -39,7 +38,7 @@ public class TpStatsHolder implements StatsHolder
         HashMap<String, Object> result = new HashMap<>();
         HashMap<String, Map<String, Object>> threadPools = new HashMap<>();
         HashMap<String, Object> droppedMessage = new HashMap<>();
-        HashMap<String, String[]> waitLatencies = new HashMap<>();
+        HashMap<String, Double[]> waitLatencies = new HashMap<>();
 
         for (Map.Entry<String, String> tp : probe.getThreadPools().entries())
         {
@@ -55,13 +54,11 @@ public class TpStatsHolder implements StatsHolder
 
         for (Map.Entry<String, Integer> entry : probe.getDroppedMessages().entrySet())
         {
-            droppedMessage.put(entry.getKey(), entry.getValue());
+            String key = entry.getKey();
+            droppedMessage.put(key, entry.getValue());
             try
             {
-                String[] strValues = (String[]) Arrays.stream(probe.metricPercentilesAsArray(probe.getMessagingQueueWaitMetrics(entry.getKey())))
-                                                      .map(D -> D.toString())
-                                                      .toArray();
-                waitLatencies.put(entry.getKey(), strValues);
+                waitLatencies.put(key, probe.metricPercentilesAsArray(probe.getMessagingQueueWaitMetrics(key)));
             }
             catch (RuntimeException e)
             {
diff --git a/test/unit/org/apache/cassandra/tools/NodeToolTPStatsTest.java b/test/unit/org/apache/cassandra/tools/NodeToolTPStatsTest.java
index 31423a4..b507bcd 100644
--- a/test/unit/org/apache/cassandra/tools/NodeToolTPStatsTest.java
+++ b/test/unit/org/apache/cassandra/tools/NodeToolTPStatsTest.java
@@ -44,6 +44,7 @@ import org.assertj.core.api.Assertions;
 import org.yaml.snakeyaml.Yaml;
 
 import static org.apache.cassandra.net.Verb.ECHO_REQ;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
@@ -68,6 +69,7 @@ public class NodeToolTPStatsTest extends CQLTester
     }
 
     @Test
+    @SuppressWarnings("SingleCharacterStringConcatenation")
     public void testMaybeChangeDocs()
     {
         // If you added, modified options or help, please update docs if necessary
@@ -151,18 +153,22 @@ public class NodeToolTPStatsTest extends CQLTester
     }
 
     @Test
-    public void testFromatArg() throws Throwable
+    public void testFormatArg()
     {
         Arrays.asList(Pair.of("-F", "json"), Pair.of("--format", "json")).forEach(arg -> {
             ToolResult tool = ToolRunner.invokeNodetool("tpstats", arg.getLeft(), arg.getRight());
-            assertTrue(isJSONString(tool.getStdout()));
+            String json = tool.getStdout();
+            assertThat(isJSONString(json)).isTrue();
+            assertThat(json).containsPattern("\"WaitLatencies\"\\s*:\\s*\\{\\s*\"");
             assertTrue(tool.getCleanedStderr().isEmpty());
             assertEquals(0, tool.getExitCode());
         });
 
         Arrays.asList( Pair.of("-F", "yaml"), Pair.of("--format", "yaml")).forEach(arg -> {
             ToolResult tool = ToolRunner.invokeNodetool("tpstats", arg.getLeft(), arg.getRight());
-            assertTrue(isYAMLString(tool.getStdout()));
+            String yaml = tool.getStdout();
+            assertThat(isYAMLString(yaml)).isTrue();
+            assertThat(yaml).containsPattern("WaitLatencies:\\s*[A-Z|_]+:\\s+-\\s");
             assertTrue(tool.getCleanedStderr().isEmpty());
             assertEquals(0, tool.getExitCode());
         });
diff --git a/test/unit/org/apache/cassandra/tools/nodetool/stats/StatsPrinterTest.java b/test/unit/org/apache/cassandra/tools/nodetool/stats/StatsPrinterTest.java
new file mode 100644
index 0000000..0cf2ea7
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/nodetool/stats/StatsPrinterTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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.tools.nodetool.stats;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests for {@link StatsPrinter}.
+ */
+public class StatsPrinterTest
+{
+    @Test
+    public void testPrintEmpty() throws Throwable
+    {
+        TestCase.create()
+                .validateJson("{}")
+                .validateYaml("{}");
+    }
+
+    @Test
+    public void testPrintSimpleTypes() throws Throwable
+    {
+        TestCase.create("null", null)
+                .validateJson("{\"null\":null}")
+                .validateYaml("'null': null");
+        TestCase.create("string", "")
+                .validateJson("{\"string\":\"\"}")
+                .validateYaml("string: ''");
+        TestCase.create("string", "test")
+                .validateJson("{\"string\":\"test\"}")
+                .validateYaml("string: test");
+        TestCase.create("bool", true)
+                .validateJson("{\"bool\":true}")
+                .validateYaml("bool: true");
+        TestCase.create("bool", false)
+                .validateJson("{\"bool\":false}")
+                .validateYaml("bool: false");
+        TestCase.create("int", 1)
+                .validateJson("{\"int\":1}")
+                .validateYaml("int: 1");
+        TestCase.create("long", 1L)
+                .validateJson("{\"long\":1}")
+                .validateYaml("long: 1");
+        TestCase.create("float", 1.1f)
+                .validateJson("{\"float\":1.1}")
+                .validateYaml("float: 1.1");
+        TestCase.create("double", 1.1d)
+                .validateJson("{\"double\":1.1}")
+                .validateYaml("double: 1.1");
+    }
+
+    @Test
+    public void testPrintArrays() throws Throwable
+    {
+        TestCase.create("ints", new int[]{ -1, 0, 1 })
+                .validateJson("{\"ints\":[-1, 0, 1]}")
+                .validateYaml("ints:\n- -1\n- 0\n- 1");
+        TestCase.create("longs", new long[]{ -1L, 0L, 1L })
+                .validateJson("{\"longs\":[-1, 0, 1]}")
+                .validateYaml("longs:\n- -1\n- 0\n- 1");
+        TestCase.create("floats", new float[]{ -1.1f, 0.1f, 1.1f })
+                .validateJson("{\"floats\":[-1.1, 0.1, 1.1]}")
+                .validateYaml("floats:\n- -1.1\n- 0.1\n- 1.1");
+        TestCase.create("doubles", new double[]{ -1.1d, 0.1d, 1.1d })
+                .validateJson("{\"doubles\":[-1.1, 0.1, 1.1]}")
+                .validateYaml("doubles:\n- -1.1\n- 0.1\n- 1.1");
+    }
+
+    @Test
+    public void testPrintLists() throws Throwable
+    {
+        TestCase.create("ints", Arrays.asList(-1, 0, 1))
+                .validateJson("{\"ints\":[-1,0,1]}")
+                .validateYaml("ints:\n- -1\n- 0\n- 1");
+        TestCase.create("longs", Arrays.asList(-1L, 0L, 1L))
+                .validateJson("{\"longs\":[-1,0,1]}")
+                .validateYaml("longs:\n- -1\n- 0\n- 1");
+        TestCase.create("floats", Arrays.asList(-1.1f, 0.1f, 1.1f))
+                .validateJson("{\"floats\":[-1.1,0.1,1.1]}")
+                .validateYaml("floats:\n- -1.1\n- 0.1\n- 1.1");
+        TestCase.create("doubles", Arrays.asList(-1.1d, 0.1d, 1.1d))
+                .validateJson("{\"doubles\":[-1.1,0.1,1.1]}")
+                .validateYaml("doubles:\n- -1.1\n- 0.1\n- 1.1");
+    }
+
+    @Test
+    public void testPrintMultiple() throws Throwable
+    {
+        TestCase.create()
+                .put("string", "test")
+                .put("array", new int[]{ -1, 0, 1 })
+                .put("list", Arrays.asList(-1, 0, 1))
+                .validateJson("{\"array\":[-1,0,1],\"list\":[-1,0,1],\"string\":\"test\"}")
+                .validateYaml("array:\n- -1\n- 0\n- 1\nlist:\n- -1\n- 0\n- 1\nstring: test");
+    }
+
+    private static class TestCase implements StatsHolder
+    {
+        private static final StatsPrinter<TestCase> jsonPrinter = new StatsPrinter.JsonPrinter<>();
+        private static final StatsPrinter<TestCase> yamlPrinter = new StatsPrinter.YamlPrinter<>();
+
+        private final Map<String, Object> map;
+
+        private TestCase()
+        {
+            this.map = new TreeMap<>();
+        }
+
+        public static TestCase create()
+        {
+            return new TestCase();
+        }
+
+        public static TestCase create(String key, Object value)
+        {
+            return create().put(key, value);
+        }
+
+        public TestCase put(String key, Object value)
+        {
+            map.put(key, value);
+            return this;
+        }
+
+        @Override
+        public Map<String, Object> convert2Map()
+        {
+            return map;
+        }
+
+        private String print(StatsPrinter<TestCase> printer) throws IOException
+        {
+            try (ByteArrayOutputStream stream = new ByteArrayOutputStream())
+            {
+                printer.print(this, new PrintStream(stream));
+                return stream.toString();
+            }
+        }
+
+        private static String cleanJson(String json)
+        {
+            return Pattern.compile("\n|\\s*").matcher(json).replaceAll("");
+        }
+
+        TestCase validateJson(String expectedJson) throws IOException
+        {
+            String expected = cleanJson(expectedJson);
+            String actual = cleanJson(print(jsonPrinter));
+            assertEquals(expected, actual);
+            return this;
+        }
+
+        @SuppressWarnings("UnusedReturnValue")
+        TestCase validateYaml(String expectedYaml) throws IOException
+        {
+            assertEquals(expectedYaml + "\n\n", print(yamlPrinter));
+            return this;
+        }
+    }
+}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org