| /* |
| * 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.PrintStream; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Pattern; |
| |
| import org.junit.Test; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| public class TableStatsPrinterTest extends TableStatsTestBase |
| { |
| |
| public static final String expectedDefaultTable1Output = |
| "\tTable: %s\n" + |
| "\tSSTable count: 60000\n" + |
| "\tOld SSTable count: 0\n" + |
| "\tSpace used (live): 0\n" + |
| "\tSpace used (total): 9001\n" + |
| "\tSpace used by snapshots (total): 1111\n" + |
| "\tSSTable Compression Ratio: 0.68\n" + |
| "\tNumber of partitions (estimate): 111111\n" + |
| "\tMemtable cell count: 111\n" + |
| "\tMemtable data size: 0\n" + |
| "\tMemtable switch count: 1\n" + |
| "\tLocal read count: 0\n" + |
| "\tLocal read latency: 2.000 ms\n" + |
| "\tLocal write count: 5\n" + |
| "\tLocal write latency: 0.050 ms\n" + |
| "\tPending flushes: 11111\n" + |
| "\tPercent repaired: 100.0\n" + |
| "\tBytes repaired: 0.000KiB\n" + |
| "\tBytes unrepaired: 0.000KiB\n" + |
| "\tBytes pending repair: 0.000KiB\n" + |
| "\tBloom filter false positives: 30\n" + |
| "\tBloom filter false ratio: 0.40000\n" + |
| "\tBloom filter space used: 789\n" + |
| "\tCompacted partition minimum bytes: 2\n" + |
| "\tCompacted partition maximum bytes: 60\n" + |
| "\tCompacted partition mean bytes: 6\n" + |
| "\tAverage live cells per slice (last five minutes): 6.0\n" + |
| "\tMaximum live cells per slice (last five minutes): 6\n" + |
| "\tAverage tombstones per slice (last five minutes): 5.0\n" + |
| "\tMaximum tombstones per slice (last five minutes): 1\n" + |
| "\tDropped Mutations: 0\n" + |
| "\tDroppable tombstone ratio: 0.00000\n" + |
| "\n"; |
| |
| public static final String expectedDefaultTable2Output = |
| "\tTable: %s\n" + |
| "\tSSTable count: 3000\n" + |
| "\tOld SSTable count: 0\n" + |
| "\tSpace used (live): 22\n" + |
| "\tSpace used (total): 1024\n" + |
| "\tSpace used by snapshots (total): 222\n" + |
| "\tOff heap memory used (total): 314159367\n" + |
| "\tSSTable Compression Ratio: 0.68\n" + |
| "\tNumber of partitions (estimate): 22222\n" + |
| "\tMemtable cell count: 22\n" + |
| "\tMemtable data size: 900\n" + |
| "\tMemtable off heap memory used: 314159265\n" + |
| "\tMemtable switch count: 22222\n" + |
| "\tLocal read count: 1\n" + |
| "\tLocal read latency: 3.000 ms\n" + |
| "\tLocal write count: 4\n" + |
| "\tLocal write latency: 0.000 ms\n" + |
| "\tPending flushes: 222222\n" + |
| "\tPercent repaired: 99.9\n" + |
| "\tBytes repaired: 0.000KiB\n" + |
| "\tBytes unrepaired: 0.000KiB\n" + |
| "\tBytes pending repair: 0.000KiB\n" + |
| "\tBloom filter false positives: 600\n" + |
| "\tBloom filter false ratio: 0.01000\n" + |
| "\tBloom filter space used: 161718\n" + |
| "\tBloom filter off heap memory used: 98\n" + |
| "\tIndex summary off heap memory used: 1\n" + |
| "\tCompression metadata off heap memory used: 3\n" + |
| "\tCompacted partition minimum bytes: 4\n" + |
| "\tCompacted partition maximum bytes: 30\n" + |
| "\tCompacted partition mean bytes: 4\n" + |
| "\tAverage live cells per slice (last five minutes): 4.01\n" + |
| "\tMaximum live cells per slice (last five minutes): 5\n" + |
| "\tAverage tombstones per slice (last five minutes): 4.001\n" + |
| "\tMaximum tombstones per slice (last five minutes): 2\n" + |
| "\tDropped Mutations: 222\n" + |
| "\tDroppable tombstone ratio: 0.22222\n" + |
| "\n"; |
| |
| public static final String expectedDefaultTable3Output = |
| "\tTable: %s\n" + |
| "\tSSTable count: 50000\n" + |
| "\tOld SSTable count: 0\n" + |
| "\tSpace used (live): 0\n" + |
| "\tSpace used (total): 512\n" + |
| "\tSpace used by snapshots (total): 0\n" + |
| "\tSSTable Compression Ratio: 0.32\n" + |
| "\tNumber of partitions (estimate): 3333\n" + |
| "\tMemtable cell count: 333333\n" + |
| "\tMemtable data size: 1999\n" + |
| "\tMemtable switch count: 3333\n" + |
| "\tLocal read count: 2\n" + |
| "\tLocal read latency: 4.000 ms\n" + |
| "\tLocal write count: 3\n" + |
| "\tLocal write latency: NaN ms\n" + |
| "\tPending flushes: 333\n" + |
| "\tPercent repaired: 99.8\n" + |
| "\tBytes repaired: 0.000KiB\n" + |
| "\tBytes unrepaired: 0.000KiB\n" + |
| "\tBytes pending repair: 0.000KiB\n" + |
| "\tBloom filter false positives: 20\n" + |
| "\tBloom filter false ratio: 0.50000\n" + |
| "\tBloom filter space used: 456\n" + |
| "\tCompacted partition minimum bytes: 2\n" + |
| "\tCompacted partition maximum bytes: 50\n" + |
| "\tCompacted partition mean bytes: 5\n" + |
| "\tAverage live cells per slice (last five minutes): 0.0\n" + |
| "\tMaximum live cells per slice (last five minutes): 5\n" + |
| "\tAverage tombstones per slice (last five minutes): NaN\n" + |
| "\tMaximum tombstones per slice (last five minutes): 3\n" + |
| "\tDropped Mutations: 33333\n" + |
| "\tDroppable tombstone ratio: 0.33333\n" + |
| "\n"; |
| |
| public static final String expectedDefaultTable4Output = |
| "\tTable: %s\n" + |
| "\tSSTable count: 2000\n" + |
| "\tOld SSTable count: 0\n" + |
| "\tSpace used (live): 4444\n" + |
| "\tSpace used (total): 256\n" + |
| "\tSpace used by snapshots (total): 44\n" + |
| "\tOff heap memory used (total): 441213818\n" + |
| "\tSSTable Compression Ratio: 0.95\n" + |
| "\tNumber of partitions (estimate): 444\n" + |
| "\tMemtable cell count: 4\n" + |
| "\tMemtable data size: 3000\n" + |
| "\tMemtable off heap memory used: 141421356\n" + |
| "\tMemtable switch count: 444444\n" + |
| "\tLocal read count: 3\n" + |
| "\tLocal read latency: NaN ms\n" + |
| "\tLocal write count: 2\n" + |
| "\tLocal write latency: 2.000 ms\n" + |
| "\tPending flushes: 4444\n" + |
| "\tPercent repaired: 50.0\n" + |
| "\tBytes repaired: 0.000KiB\n" + |
| "\tBytes unrepaired: 0.000KiB\n" + |
| "\tBytes pending repair: 0.000KiB\n" + |
| "\tBloom filter false positives: 500\n" + |
| "\tBloom filter false ratio: 0.02000\n" + |
| "\tBloom filter space used: 131415\n" + |
| "\tBloom filter off heap memory used: 299792458\n" + |
| "\tIndex summary off heap memory used: 2\n" + |
| "\tCompression metadata off heap memory used: 2\n" + |
| "\tCompacted partition minimum bytes: 5\n" + |
| "\tCompacted partition maximum bytes: 20\n" + |
| "\tCompacted partition mean bytes: 4\n" + |
| "\tAverage live cells per slice (last five minutes): NaN\n" + |
| "\tMaximum live cells per slice (last five minutes): 3\n" + |
| "\tAverage tombstones per slice (last five minutes): 0.0\n" + |
| "\tMaximum tombstones per slice (last five minutes): 3\n" + |
| "\tDropped Mutations: 4444\n" + |
| "\tDroppable tombstone ratio: 0.44444\n" + |
| "\n"; |
| |
| public static final String expectedDefaultTable5Output = |
| "\tTable: %s\n" + |
| "\tSSTable count: 40000\n" + |
| "\tOld SSTable count: 0\n" + |
| "\tSpace used (live): 55555\n" + |
| "\tSpace used (total): 64\n" + |
| "\tSpace used by snapshots (total): 55555\n" + |
| "\tSSTable Compression Ratio: 0.99\n" + |
| "\tNumber of partitions (estimate): 55\n" + |
| "\tMemtable cell count: 55555\n" + |
| "\tMemtable data size: 20000\n" + |
| "\tMemtable switch count: 5\n" + |
| "\tLocal read count: 4\n" + |
| "\tLocal read latency: 0.000 ms\n" + |
| "\tLocal write count: 1\n" + |
| "\tLocal write latency: 1.000 ms\n" + |
| "\tPending flushes: 5\n" + |
| "\tPercent repaired: 93.0\n" + |
| "\tBytes repaired: 0.000KiB\n" + |
| "\tBytes unrepaired: 0.000KiB\n" + |
| "\tBytes pending repair: 0.000KiB\n" + |
| "\tBloom filter false positives: 10\n" + |
| "\tBloom filter false ratio: 0.60000\n" + |
| "\tBloom filter space used: 123\n" + |
| "\tCompacted partition minimum bytes: 3\n" + |
| "\tCompacted partition maximum bytes: 40\n" + |
| "\tCompacted partition mean bytes: 4\n" + |
| "\tAverage live cells per slice (last five minutes): 4.0\n" + |
| "\tMaximum live cells per slice (last five minutes): 3\n" + |
| "\tAverage tombstones per slice (last five minutes): 4.01\n" + |
| "\tMaximum tombstones per slice (last five minutes): 5\n" + |
| "\tDropped Mutations: 0\n" + |
| "\tDroppable tombstone ratio: 0.55556\n" + |
| "\n"; |
| |
| public static final String expectedDefaultTable6Output = |
| "\tTable: %s\n" + |
| "\tSSTable count: 1000\n" + |
| "\tOld SSTable count: 0\n" + |
| "\tSpace used (live): 666666\n" + |
| "\tSpace used (total): 0\n" + |
| "\tSpace used by snapshots (total): 0\n" + |
| "\tOff heap memory used (total): 162470810\n" + |
| "\tSSTable Compression Ratio: 0.68\n" + |
| "\tNumber of partitions (estimate): 6\n" + |
| "\tMemtable cell count: 6666\n" + |
| "\tMemtable data size: 1000000\n" + |
| "\tMemtable off heap memory used: 161803398\n" + |
| "\tMemtable switch count: 6\n" + |
| "\tLocal read count: 5\n" + |
| "\tLocal read latency: 1.000 ms\n" + |
| "\tLocal write count: 0\n" + |
| "\tLocal write latency: 0.500 ms\n" + |
| "\tPending flushes: 66\n" + |
| "\tPercent repaired: 0.0\n" + |
| "\tBytes repaired: 0.000KiB\n" + |
| "\tBytes unrepaired: 0.000KiB\n" + |
| "\tBytes pending repair: 0.000KiB\n" + |
| "\tBloom filter false positives: 400\n" + |
| "\tBloom filter false ratio: 0.03000\n" + |
| "\tBloom filter space used: 101112\n" + |
| "\tBloom filter off heap memory used: 667408\n" + |
| "\tIndex summary off heap memory used: 3\n" + |
| "\tCompression metadata off heap memory used: 1\n" + |
| "\tCompacted partition minimum bytes: 6\n" + |
| "\tCompacted partition maximum bytes: 20\n" + |
| "\tCompacted partition mean bytes: 3\n" + |
| "\tAverage live cells per slice (last five minutes): 5.0\n" + |
| "\tMaximum live cells per slice (last five minutes): 2\n" + |
| "\tAverage tombstones per slice (last five minutes): 6.0\n" + |
| "\tMaximum tombstones per slice (last five minutes): 6\n" + |
| "\tDropped Mutations: 666666\n" + |
| "\tDroppable tombstone ratio: 0.66667\n" + |
| "\n"; |
| |
| /** |
| * Expected output of TableStatsPrinter DefaultPrinter for this dataset. |
| * Total number of tables is zero because it's non-trivial to simulate that metric |
| * without leaking test implementation into the TableStatsHolder implementation. |
| */ |
| public static final String expectedDefaultPrinterOutput = |
| "Total number of tables: 0\n" + |
| "----------------\n" + |
| "Keyspace : keyspace1\n" + |
| "\tRead Count: 3\n" + |
| "\tRead Latency: 0.0 ms\n" + |
| "\tWrite Count: 12\n" + |
| "\tWrite Latency: 0.0 ms\n" + |
| "\tPending Flushes: 233666\n" + |
| String.format(duplicateTabs(expectedDefaultTable1Output), "table1") + |
| String.format(duplicateTabs(expectedDefaultTable2Output), "table2") + |
| String.format(duplicateTabs(expectedDefaultTable3Output), "table3") + |
| "----------------\n" + |
| "Keyspace : keyspace2\n" + |
| "\tRead Count: 7\n" + |
| "\tRead Latency: 0.0 ms\n" + |
| "\tWrite Count: 3\n" + |
| "\tWrite Latency: 0.0 ms\n" + |
| "\tPending Flushes: 4449\n" + |
| String.format(duplicateTabs(expectedDefaultTable4Output), "table4") + |
| String.format(duplicateTabs(expectedDefaultTable5Output), "table5") + |
| "----------------\n" + |
| "Keyspace : keyspace3\n" + |
| "\tRead Count: 5\n" + |
| "\tRead Latency: 0.0 ms\n" + |
| "\tWrite Count: 0\n" + |
| "\tWrite Latency: NaN ms\n" + |
| "\tPending Flushes: 66\n" + |
| String.format(duplicateTabs(expectedDefaultTable6Output), "table6") + |
| "----------------\n"; |
| |
| /** |
| * Expected output from SortedDefaultPrinter for data sorted by reads in this test. |
| */ |
| private static final String expectedSortedDefaultPrinterOutput = |
| "Total number of tables: 0\n" + |
| "----------------\n" + |
| String.format(expectedDefaultTable6Output, "keyspace3.table6") + |
| String.format(expectedDefaultTable5Output, "keyspace2.table5") + |
| String.format(expectedDefaultTable4Output, "keyspace2.table4") + |
| String.format(expectedDefaultTable3Output, "keyspace1.table3") + |
| String.format(expectedDefaultTable2Output, "keyspace1.table2") + |
| String.format(expectedDefaultTable1Output, "keyspace1.table1") + |
| "----------------\n"; |
| |
| /** |
| * Expected output from SortedDefaultPrinter for data sorted by reads and limited to the top 4 tables. |
| */ |
| private static final String expectedSortedDefaultPrinterTopOutput = |
| "Total number of tables: 0 (showing top 0 by %s)\n" + |
| "----------------\n" + |
| String.format(expectedDefaultTable6Output, "keyspace3.table6") + |
| String.format(expectedDefaultTable5Output, "keyspace2.table5") + |
| String.format(expectedDefaultTable4Output, "keyspace2.table4") + |
| String.format(expectedDefaultTable3Output, "keyspace1.table3") + |
| "----------------\n"; |
| |
| /** |
| * Expected output from SortedDefaultPrinter for data sorted by reads and limited to the top 10 tables. |
| */ |
| private static final String expectedSortedDefaultPrinterLargeTopOutput = |
| "Total number of tables: 0 (showing top 0 by %s)\n" + |
| "----------------\n" + |
| String.format(expectedDefaultTable6Output, "keyspace3.table6") + |
| String.format(expectedDefaultTable5Output, "keyspace2.table5") + |
| String.format(expectedDefaultTable4Output, "keyspace2.table4") + |
| String.format(expectedDefaultTable3Output, "keyspace1.table3") + |
| String.format(expectedDefaultTable2Output, "keyspace1.table2") + |
| String.format(expectedDefaultTable1Output, "keyspace1.table1") + |
| "----------------\n"; |
| |
| private static String duplicateTabs(String s) |
| { |
| return Pattern.compile("\t").matcher(s).replaceAll("\t\t"); |
| } |
| |
| @Test |
| public void testDefaultPrinter() throws Exception |
| { |
| StatsHolder holder = new TestTableStatsHolder(testKeyspaces, "", 0); |
| StatsPrinter<StatsHolder> printer = TableStatsPrinter.from("", false); |
| try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) |
| { |
| printer.print(holder, new PrintStream(byteStream)); |
| assertEquals("StatsTablePrinter.DefaultPrinter does not print test vector as expected", expectedDefaultPrinterOutput, byteStream.toString()); |
| } |
| } |
| |
| @Test |
| public void testSortedDefaultPrinter() throws Exception |
| { |
| // test sorting |
| StatsHolder holder = new TestTableStatsHolder(testKeyspaces, "reads", 0); |
| StatsPrinter<StatsHolder> printer = TableStatsPrinter.from("reads", true); |
| try (ByteArrayOutputStream byteStream = new ByteArrayOutputStream()) |
| { |
| printer.print(holder, new PrintStream(byteStream)); |
| assertEquals("StatsTablePrinter.SortedDefaultPrinter does not print sorted tables as expected", |
| expectedSortedDefaultPrinterOutput, byteStream.toString()); |
| byteStream.reset(); |
| // test sorting and filtering top k, where k < total number of tables |
| String sortKey = "reads"; |
| int top = 4; |
| holder = new TestTableStatsHolder(testKeyspaces, sortKey, top); |
| printer = TableStatsPrinter.from(sortKey, true); |
| printer.print(holder, new PrintStream(byteStream)); |
| assertEquals("StatsTablePrinter.SortedDefaultPrinter does not print top K sorted tables as expected", |
| String.format(expectedSortedDefaultPrinterTopOutput, sortKey), byteStream.toString()); |
| byteStream.reset(); |
| // test sorting and filtering top k, where k >= total number of tables |
| sortKey = "reads"; |
| top = 10; |
| holder = new TestTableStatsHolder(testKeyspaces, sortKey, top); |
| printer = TableStatsPrinter.from(sortKey, true); |
| printer.print(holder, new PrintStream(byteStream)); |
| assertEquals("StatsTablePrinter.SortedDefaultPrinter does not print top K sorted tables as expected for large values of K", |
| String.format(expectedSortedDefaultPrinterLargeTopOutput, sortKey), byteStream.toString()); |
| } |
| } |
| |
| /** |
| * A test version of TableStatsHolder to hold a test vector instead of gathering stats from a live cluster. |
| */ |
| private static class TestTableStatsHolder extends TableStatsHolder |
| { |
| |
| public TestTableStatsHolder(List<StatsKeyspace> testKeyspaces, String sortKey, int top) |
| { |
| super(null, false, false, new ArrayList<>(), sortKey, top, false); |
| this.keyspaces.clear(); |
| this.keyspaces.addAll(testKeyspaces); |
| } |
| |
| @Override |
| protected boolean isTestTableStatsHolder() |
| { |
| return true; |
| } |
| } |
| |
| } |