Add netstats and tablestats unit tests

patch by Berenguer Blasi; reviewed by Andrés de la Peña for CASSANDRA-16227
This commit is contained in:
Bereng 2020-11-03 18:06:17 +00:00 committed by Andrés de la Peña
parent 980c6ea6ab
commit 93c2d763eb
8 changed files with 583 additions and 181 deletions

View File

@ -23,6 +23,8 @@ import io.airlift.airline.Option;
import java.io.PrintStream;
import java.util.Set;
import com.google.common.annotations.VisibleForTesting;
import org.apache.cassandra.io.util.FileUtils;
import org.apache.cassandra.net.MessagingServiceMBean;
import org.apache.cassandra.streaming.ProgressInfo;
@ -61,63 +63,11 @@ public class NetStats extends NodeToolCmd
out.printf("%n");
if (!info.receivingSummaries.isEmpty())
{
long totalFilesToReceive = info.getTotalFilesToReceive();
long totalBytesToReceive = info.getTotalSizeToReceive();
long totalFilesReceived = info.getTotalFilesReceived();
long totalSizeReceived = info.getTotalSizeReceived();
double percentageFilesReceived = ((double) totalFilesReceived / totalFilesToReceive) * 100;
double percentageSizesReceived = ((double) totalSizeReceived / totalBytesToReceive) * 100;
if (humanReadable)
out.printf(" Receiving %d files, %s total. Already received %d files (%.2f%%), %s total (%.2f%%)%n",
totalFilesToReceive,
FileUtils.stringifyFileSize(totalBytesToReceive),
totalFilesReceived,
percentageFilesReceived,
FileUtils.stringifyFileSize(totalSizeReceived),
percentageSizesReceived);
else
out.printf(" Receiving %d files, %d bytes total. Already received %d files (%.2f%%), %d bytes total (%.2f%%)%n",
totalFilesToReceive,
totalBytesToReceive,
totalFilesReceived,
percentageFilesReceived,
totalSizeReceived,
percentageSizesReceived);
for (ProgressInfo progress : info.getReceivingFiles())
{
out.printf(" %s%n", progress.toString(printPort));
}
printReceivingSummaries(out, info, humanReadable);
}
if (!info.sendingSummaries.isEmpty())
{
long totalFilesToSend = info.getTotalFilesToSend();
long totalSizeToSend = info.getTotalSizeToSend();
long totalFilesSent = info.getTotalFilesSent();
long totalSizeSent = info.getTotalSizeSent();
double percentageFilesSent = ((double) totalFilesSent / totalFilesToSend) * 100;
double percentageSizeSent = ((double) totalSizeSent / totalSizeToSend) * 100;
if (humanReadable)
out.printf(" Sending %d files, %s total. Already sent %d files (%.2f%%), %s total (%.2f%%)%n",
totalFilesToSend,
FileUtils.stringifyFileSize(totalSizeToSend),
totalFilesSent,
percentageFilesSent,
FileUtils.stringifyFileSize(totalSizeSent),
percentageSizeSent);
else
out.printf(" Sending %d files, %d bytes total. Already sent %d files (%.2f%%), %d bytes total (%.2f%%) %n",
totalFilesToSend,
totalSizeToSend,
totalFilesSent,
percentageFilesSent,
totalSizeSent,
percentageSizeSent);
for (ProgressInfo progress : info.getSendingFiles())
{
out.printf(" %s%n", progress.toString(printPort));
}
printSendingSummaries(out, info, humanReadable);
}
}
}
@ -171,4 +121,52 @@ public class NetStats extends NodeToolCmd
out.printf("%-25s%10s%10s%15s%10s%n", "Gossip messages", "n/a", pending, completed, dropped);
}
}
@VisibleForTesting
public void printReceivingSummaries(PrintStream out, SessionInfo info, boolean printHumanReadable)
{
long totalFilesToReceive = info.getTotalFilesToReceive();
long totalBytesToReceive = info.getTotalSizeToReceive();
long totalFilesReceived = info.getTotalFilesReceived();
long totalSizeReceived = info.getTotalSizeReceived();
double percentageFilesReceived = ((double) totalFilesReceived / totalFilesToReceive) * 100;
double percentageSizesReceived = ((double) totalSizeReceived / totalBytesToReceive) * 100;
out.printf(" Receiving %d files, %s total. Already received %d files (%.2f%%), %s total (%.2f%%)%n",
totalFilesToReceive,
printHumanReadable ? FileUtils.stringifyFileSize(totalBytesToReceive) : Long.toString(totalBytesToReceive) + " bytes",
totalFilesReceived,
percentageFilesReceived,
printHumanReadable ? FileUtils.stringifyFileSize(totalSizeReceived) : Long.toString(totalSizeReceived) + " bytes",
percentageSizesReceived);
for (ProgressInfo progress : info.getReceivingFiles())
{
out.printf(" %s%n", progress.toString(printPort));
}
}
@VisibleForTesting
public void printSendingSummaries(PrintStream out, SessionInfo info, boolean printHumanReadable)
{
long totalFilesToSend = info.getTotalFilesToSend();
long totalSizeToSend = info.getTotalSizeToSend();
long totalFilesSent = info.getTotalFilesSent();
long totalSizeSent = info.getTotalSizeSent();
double percentageFilesSent = ((double) totalFilesSent / totalFilesToSend) * 100;
double percentageSizeSent = ((double) totalSizeSent / totalSizeToSend) * 100;
out.printf(" Sending %d files, %s total. Already sent %d files (%.2f%%), %s total (%.2f%%)%n",
totalFilesToSend,
printHumanReadable ? FileUtils.stringifyFileSize(totalSizeToSend) : Long.toString(totalSizeToSend) + " bytes",
totalFilesSent,
percentageFilesSent,
printHumanReadable ? FileUtils.stringifyFileSize(totalSizeSent) : Long.toString(totalSizeSent) + " bytes",
percentageSizeSent);
for (ProgressInfo progress : info.getSendingFiles())
{
out.printf(" %s%n", progress.toString(printPort));
}
}
}

View File

@ -25,7 +25,7 @@ import org.apache.cassandra.io.util.FileUtils;
/**
* Comparator to sort StatsTables by a named statistic.
*/
public class StatsTableComparator implements Comparator
public class StatsTableComparator implements Comparator<StatsTable>
{
/**
@ -115,15 +115,10 @@ public class StatsTableComparator implements Comparator
/**
* Compare StatsTable instances based on this instance's sortKey.
*/
public int compare(Object x, Object y)
public int compare(StatsTable stx, StatsTable sty)
{
if (!(x instanceof StatsTable && y instanceof StatsTable))
throw new ClassCastException(String.format("StatsTableComparator cannot compare %s and %s",
x.getClass().toString(), y.getClass().toString()));
if (x == null || y == null)
if (stx == null || sty == null)
throw new NullPointerException("StatsTableComparator cannot compare null objects");
StatsTable stx = (StatsTable) x;
StatsTable sty = (StatsTable) y;
int sign = ascending ? 1 : -1;
int result = 0;
if (sortKey.equals("average_live_cells_per_slice_last_five_minutes"))

View File

@ -21,21 +21,21 @@ package org.apache.cassandra.tools.nodetool.stats;
import java.io.PrintStream;
import java.util.List;
public class TableStatsPrinter
public class TableStatsPrinter<T extends StatsHolder>
{
public static StatsPrinter from(String format, boolean sorted)
public static <T extends StatsHolder> StatsPrinter<T> from(String format, boolean sorted)
{
switch (format)
{
case "json":
return new StatsPrinter.JsonPrinter();
return new StatsPrinter.JsonPrinter<T>();
case "yaml":
return new StatsPrinter.YamlPrinter();
return new StatsPrinter.YamlPrinter<T>();
default:
if (sorted)
return new SortedDefaultPrinter();
return (StatsPrinter<T>) new SortedDefaultPrinter();
else
return new DefaultPrinter();
return (StatsPrinter<T>) new DefaultPrinter();
}
}

View File

@ -0,0 +1,168 @@
/*
* 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;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.net.NoPayload;
import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.streaming.SessionInfo;
import org.apache.cassandra.streaming.StreamSession.State;
import org.apache.cassandra.streaming.StreamSummary;
import org.apache.cassandra.tools.ToolRunner.ToolResult;
import org.apache.cassandra.tools.nodetool.NetStats;
import org.apache.cassandra.utils.FBUtilities;
import org.assertj.core.api.Assertions;
import org.hamcrest.CoreMatchers;
import static org.apache.cassandra.net.Verb.ECHO_REQ;
import static org.junit.Assert.assertThat;
@RunWith(OrderedJUnit4ClassRunner.class)
public class NodetoolNetStatsTest extends CQLTester
{
private static NodeProbe probe;
@BeforeClass
public static void setup() throws Exception
{
StorageService.instance.initServer();
startJMXServer();
probe = new NodeProbe(jmxHost, jmxPort);
}
@AfterClass
public static void teardown() throws IOException
{
probe.close();
}
@Test
public void testMaybeChangeDocs()
{
// If you added, modified options or help, please update docs if necessary
ToolResult tool = ToolRunner.invokeNodetool("help", "netstats");
String help = "NAME\n" +
" nodetool netstats - Print network information on provided host\n" +
" (connecting node by default)\n" +
"\n" +
"SYNOPSIS\n" +
" nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]\n" +
" [(-pp | --print-port)] [(-pw <password> | --password <password>)]\n" +
" [(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]\n" +
" [(-u <username> | --username <username>)] netstats\n" +
" [(-H | --human-readable)]\n" +
"\n" +
"OPTIONS\n" +
" -h <host>, --host <host>\n" +
" Node hostname or ip address\n" +
"\n" +
" -H, --human-readable\n" +
" Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB\n" +
"\n" +
" -p <port>, --port <port>\n" +
" Remote jmx agent port number\n" +
"\n" +
" -pp, --print-port\n" +
" Operate in 4.0 mode with hosts disambiguated by port number\n" +
"\n" +
" -pw <password>, --password <password>\n" +
" Remote jmx agent password\n" +
"\n" +
" -pwf <passwordFilePath>, --password-file <passwordFilePath>\n" +
" Path to the JMX password file\n" +
"\n" +
" -u <username>, --username <username>\n" +
" Remote jmx agent username\n" +
"\n" +
"\n";
Assertions.assertThat(tool.getStdout()).isEqualTo(help);
tool.assertOnCleanExit();
}
@Test
public void testNetStats()
{
Message<NoPayload> echoMessageOut = Message.out(ECHO_REQ, NoPayload.noPayload);
MessagingService.instance().send(echoMessageOut, FBUtilities.getBroadcastAddressAndPort());
ToolResult tool = ToolRunner.invokeNodetool("netstats");
assertThat(tool.getStdout(), CoreMatchers.containsString("Gossip messages n/a 0 2 0"));
tool.assertOnCleanExit();
}
@Test
public void testHumanReadable() throws IOException
{
List<StreamSummary> streamSummaries = Collections.singletonList(new StreamSummary(TableId.generate(), 1, 1024));
SessionInfo info = new SessionInfo(InetAddressAndPort.getLocalHost(),
1,
InetAddressAndPort.getLocalHost(),
streamSummaries,
streamSummaries,
State.COMPLETE);
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream out = new PrintStream(baos))
{
NetStats nstats = new NetStats();
nstats.printReceivingSummaries(out, info, false);
String stdout = getSummariesStdout(baos, out);
Assertions.assertThat(stdout).doesNotContain("Kib");
baos.reset();
nstats.printSendingSummaries(out, info, false);
stdout = getSummariesStdout(baos, out);
Assertions.assertThat(stdout).doesNotContain("KiB");
baos.reset();
nstats.printReceivingSummaries(out, info, true);
stdout = getSummariesStdout(baos, out);
Assertions.assertThat(stdout).contains("KiB");
baos.reset();
nstats.printSendingSummaries(out, info, true);
stdout = getSummariesStdout(baos, out);
Assertions.assertThat(stdout).contains("KiB");
}
}
private String getSummariesStdout(ByteArrayOutputStream baos, PrintStream ps) throws IOException
{
baos.flush();
ps.flush();
return baos.toString(StandardCharsets.UTF_8.toString());
}
}

View File

@ -0,0 +1,240 @@
/*
* 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.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.cassandra.OrderedJUnit4ClassRunner;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.tools.NodeProbe;
import org.apache.cassandra.tools.ToolRunner;
import org.apache.cassandra.tools.ToolRunner.ToolResult;
import org.assertj.core.api.Assertions;
import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(OrderedJUnit4ClassRunner.class)
public class NodetoolTableStatsTest extends CQLTester
{
private static NodeProbe probe;
@BeforeClass
public static void setup() throws Exception
{
StorageService.instance.initServer();
startJMXServer();
probe = new NodeProbe(jmxHost, jmxPort);
}
@AfterClass
public static void teardown() throws IOException
{
probe.close();
}
@Test
public void testMaybeChangeDocs()
{
// If you added, modified options or help, please update docs if necessary
ToolResult tool = ToolRunner.invokeNodetool("help", "tablestats");
String help = "NAME\n" +
" nodetool tablestats - Print statistics on tables\n" +
"\n" +
"SYNOPSIS\n" +
" nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]\n" +
" [(-pp | --print-port)] [(-pw <password> | --password <password>)]\n" +
" [(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]\n" +
" [(-u <username> | --username <username>)] tablestats\n" +
" [(-F <format> | --format <format>)] [(-H | --human-readable)] [-i]\n" +
" [(-s <sort_key> | --sort <sort_key>)] [(-t <top> | --top <top>)] [--]\n" +
" [<keyspace.table>...]\n" +
"\n" +
"OPTIONS\n" +
" -F <format>, --format <format>\n" +
" Output format (json, yaml)\n" +
"\n" +
" -h <host>, --host <host>\n" +
" Node hostname or ip address\n" +
"\n" +
" -H, --human-readable\n" +
" Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB\n" +
"\n" +
" -i\n" +
" Ignore the list of tables and display the remaining tables\n" +
"\n" +
" -p <port>, --port <port>\n" +
" Remote jmx agent port number\n" +
"\n" +
" -pp, --print-port\n" +
" Operate in 4.0 mode with hosts disambiguated by port number\n" +
"\n" +
" -pw <password>, --password <password>\n" +
" Remote jmx agent password\n" +
"\n" +
" -pwf <passwordFilePath>, --password-file <passwordFilePath>\n" +
" Path to the JMX password file\n" +
"\n" +
" -s <sort_key>, --sort <sort_key>\n" +
" Sort tables by specified sort key\n" +
" (average_live_cells_per_slice_last_five_minutes,\n" +
" average_tombstones_per_slice_last_five_minutes,\n" +
" bloom_filter_false_positives, bloom_filter_false_ratio,\n" +
" bloom_filter_off_heap_memory_used, bloom_filter_space_used,\n" +
" compacted_partition_maximum_bytes, compacted_partition_mean_bytes,\n" +
" compacted_partition_minimum_bytes,\n" +
" compression_metadata_off_heap_memory_used, dropped_mutations,\n" +
" full_name, index_summary_off_heap_memory_used, local_read_count,\n" +
" local_read_latency_ms, local_write_latency_ms,\n" +
" maximum_live_cells_per_slice_last_five_minutes,\n" +
" maximum_tombstones_per_slice_last_five_minutes, memtable_cell_count,\n" +
" memtable_data_size, memtable_off_heap_memory_used,\n" +
" memtable_switch_count, number_of_partitions_estimate,\n" +
" off_heap_memory_used_total, pending_flushes, percent_repaired,\n" +
" read_latency, reads, space_used_by_snapshots_total, space_used_live,\n" +
" space_used_total, sstable_compression_ratio, sstable_count,\n" +
" table_name, write_latency, writes)\n" +
"\n" +
" -t <top>, --top <top>\n" +
" Show only the top K tables for the sort key (specify the number K of\n" +
" tables to be shown\n" +
"\n" +
" -u <username>, --username <username>\n" +
" Remote jmx agent username\n" +
"\n" +
" --\n" +
" This option can be used to separate command-line options from the\n" +
" list of argument, (useful when arguments might be mistaken for\n" +
" command-line options\n" +
"\n" +
" [<keyspace.table>...]\n" +
" List of tables (or keyspace) names\n" +
"\n" +
"\n";
Assertions.assertThat(tool.getStdout()).isEqualTo(help);
tool.assertOnCleanExit();
}
@Test
public void testTableStats()
{
ToolResult tool = ToolRunner.invokeNodetool("tablestats");
assertThat(tool.getStdout(), CoreMatchers.containsString("Keyspace : system_schema"));
assertTrue(StringUtils.countMatches(tool.getStdout(), "Table:") > 1);
tool.assertOnCleanExit();
tool = ToolRunner.invokeNodetool("tablestats", "system_distributed");
assertThat(tool.getStdout(), CoreMatchers.containsString("Keyspace : system_distributed"));
assertThat(tool.getStdout(), CoreMatchers.not(CoreMatchers.containsString("Keyspace : system_schema")));
assertTrue(StringUtils.countMatches(tool.getStdout(), "Table:") > 1);
tool.assertOnCleanExit();
}
@Test
public void testTableIgnoreArg()
{
ToolResult tool = ToolRunner.invokeNodetool("tablestats", "-i", "system_schema.aggregates");
assertThat(tool.getStdout(), CoreMatchers.containsString("Keyspace : system_schema"));
assertThat(tool.getStdout(), CoreMatchers.not(CoreMatchers.containsString("Table: system_schema.aggregates")));
assertTrue(StringUtils.countMatches(tool.getStdout(), "Table:") > 1);
tool.assertOnCleanExit();
}
@Test
public void testHumanReadableArg()
{
Arrays.asList("-H", "--human-readable").forEach(arg -> {
ToolResult tool = ToolRunner.invokeNodetool("tablestats", arg);
assertThat("Arg: [" + arg + "]", tool.getStdout(), CoreMatchers.containsString(" KiB"));
assertTrue(String.format("Expected empty stderr for option [%s] but found: %s",
arg,
tool.getCleanedStderr()),
tool.getCleanedStderr().isEmpty());
assertEquals(String.format("Expected exit code 0 for option [%s] but found: %s", arg, tool.getExitCode()),
0,
tool.getExitCode());
});
}
@Test
public void testSortArg()
{
Pattern regExp = Pattern.compile("((?m)Table: .*$)");
Arrays.asList("-s", "--sort").forEach(arg -> {
ToolResult tool = ToolRunner.invokeNodetool("tablestats", arg, "table_name");
Matcher m = regExp.matcher(tool.getStdout());
ArrayList<String> orig = new ArrayList<>();
while (m.find())
orig.add(m.group(1));
tool = ToolRunner.invokeNodetool("tablestats", arg, "sstable_count");
m = regExp.matcher(tool.getStdout());
ArrayList<String> sorted = new ArrayList<>();
while (m.find())
sorted.add(m.group(1));
assertNotEquals("Arg: [" + arg + "]", orig, sorted);
Collections.sort(orig);
Collections.sort(sorted);
assertEquals("Arg: [" + arg + "]", orig, sorted);
assertTrue("Arg: [" + arg + "]", tool.getCleanedStderr().isEmpty());
assertEquals(0, tool.getExitCode());
});
ToolResult tool = ToolRunner.invokeNodetool("tablestats", "-s", "wrongSort");
assertThat(tool.getStdout(), CoreMatchers.containsString("argument for sort must be one of"));
tool.assertCleanStdErr();
assertEquals(1, tool.getExitCode());
}
@Test
public void testTopArg()
{
Arrays.asList("-t", "--top").forEach(arg -> {
ToolResult tool = ToolRunner.invokeNodetool("tablestats", "-s", "table_name", arg, "1");
assertEquals("Arg: [" + arg + "]", StringUtils.countMatches(tool.getStdout(), "Table:"), 1);
assertTrue("Arg: [" + arg + "]", tool.getCleanedStderr().isEmpty());
assertEquals("Arg: [" + arg + "]", 0, tool.getExitCode());
});
ToolResult tool = ToolRunner.invokeNodetool("tablestats", "-s", "table_name", "-t", "-1");
assertThat(tool.getStdout(), CoreMatchers.containsString("argument for top must be a positive integer"));
tool.assertCleanStdErr();
assertEquals(1, tool.getExitCode());
}
}

View File

@ -18,31 +18,29 @@
package org.apache.cassandra.tools.nodetool.stats;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class StatsTableComparatorTest extends TableStatsTestBase
{
/**
* Builds a string of the format "table1 > table2 > ... > tableN-1 > tableN"
* to show the order of StatsTables in a sorted list.
* @returns String a string showing the relative position in the list of its StatsTables
* @return String a string showing the relative position in the list of its StatsTables
*/
private String buildSortOrderString(List<StatsTable> sorted) {
if (sorted == null)
return null;
if (sorted.size() == 0)
return "";
String names = sorted.get(0).tableName;
StringBuilder names = new StringBuilder(sorted.get(0).tableName);
for (int i = 1; i < sorted.size(); i++)
names += " > " + sorted.get(i).tableName;
return names;
names.append(" > ").append(sorted.get(i).tableName);
return names.toString();
}
/**
@ -51,13 +49,13 @@ public class StatsTableComparatorTest extends TableStatsTestBase
private void runCompareTest(List<StatsTable> vector, String sortKey, String expectedOrder,
boolean humanReadable, boolean ascending)
{
Collections.sort(vector, new StatsTableComparator(sortKey, humanReadable, ascending));
vector.sort(new StatsTableComparator(sortKey, humanReadable, ascending));
String failureMessage = String.format("StatsTableComparator failed to sort by %s", sortKey);
assertEquals(failureMessage, expectedOrder, buildSortOrderString(vector));
}
@Test
public void testCompareDoubles() throws Exception
public void testCompareDoubles()
{
boolean humanReadable = false;
boolean ascending = false;
@ -94,7 +92,7 @@ public class StatsTableComparatorTest extends TableStatsTestBase
}
@Test
public void testCompareLongs() throws Exception
public void testCompareLongs()
{
boolean humanReadable = false;
boolean ascending = false;
@ -143,7 +141,7 @@ public class StatsTableComparatorTest extends TableStatsTestBase
}
@Test
public void testCompareHumanReadable() throws Exception
public void testCompareHumanReadable()
{
boolean humanReadable = true;
boolean ascending = false;
@ -162,7 +160,7 @@ public class StatsTableComparatorTest extends TableStatsTestBase
}
@Test
public void testCompareObjects() throws Exception
public void testCompareObjects()
{
boolean humanReadable = false;
boolean ascending = false;
@ -217,7 +215,7 @@ public class StatsTableComparatorTest extends TableStatsTestBase
}
@Test
public void testCompareOffHeap() throws Exception
public void testCompareOffHeap()
{
boolean humanReadable = false;
boolean ascending = false;
@ -254,7 +252,7 @@ public class StatsTableComparatorTest extends TableStatsTestBase
}
@Test
public void testCompareStrings() throws Exception
public void testCompareStrings()
{
boolean humanReadable = false;
boolean ascending = false;

View File

@ -21,13 +21,12 @@ package org.apache.cassandra.tools.nodetool.stats;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
public class TableStatsPrinterTest extends TableStatsTestBase
{
@ -247,9 +246,9 @@ public class TableStatsPrinterTest extends TableStatsTestBase
"\tWrite Count: 12\n" +
"\tWrite Latency: 0.0 ms\n" +
"\tPending Flushes: 233666\n" +
String.format(expectedDefaultTable1Output, "table1").replace("\t", "\t\t") +
String.format(expectedDefaultTable2Output, "table2").replace("\t", "\t\t") +
String.format(expectedDefaultTable3Output, "table3").replace("\t", "\t\t") +
String.format(duplicateTabs(expectedDefaultTable1Output), "table1") +
String.format(duplicateTabs(expectedDefaultTable2Output), "table2") +
String.format(duplicateTabs(expectedDefaultTable3Output), "table3") +
"----------------\n" +
"Keyspace : keyspace2\n" +
"\tRead Count: 7\n" +
@ -257,8 +256,8 @@ public class TableStatsPrinterTest extends TableStatsTestBase
"\tWrite Count: 3\n" +
"\tWrite Latency: 0.0 ms\n" +
"\tPending Flushes: 4449\n" +
String.format(expectedDefaultTable4Output, "table4").replace("\t", "\t\t") +
String.format(expectedDefaultTable5Output, "table5").replace("\t", "\t\t") +
String.format(duplicateTabs(expectedDefaultTable4Output), "table4") +
String.format(duplicateTabs(expectedDefaultTable5Output), "table5") +
"----------------\n" +
"Keyspace : keyspace3\n" +
"\tRead Count: 5\n" +
@ -266,7 +265,7 @@ public class TableStatsPrinterTest extends TableStatsTestBase
"\tWrite Count: 0\n" +
"\tWrite Latency: NaN ms\n" +
"\tPending Flushes: 66\n" +
String.format(expectedDefaultTable6Output, "table6").replace("\t", "\t\t") +
String.format(duplicateTabs(expectedDefaultTable6Output), "table6") +
"----------------\n";
/**
@ -309,14 +308,21 @@ public class TableStatsPrinterTest extends TableStatsTestBase
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 printer = TableStatsPrinter.from("", false);
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
printer.print(holder, new PrintStream(byteStream));
assertEquals("StatsTablePrinter.DefaultPrinter does not print test vector as expected", expectedDefaultPrinterOutput, byteStream.toString());
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
@ -324,29 +330,31 @@ public class TableStatsPrinterTest extends TableStatsTestBase
{
// test sorting
StatsHolder holder = new TestTableStatsHolder(testKeyspaces, "reads", 0);
StatsPrinter printer = TableStatsPrinter.from("reads", true);
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());
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());
}
}
/**

View File

@ -21,9 +21,7 @@ package org.apache.cassandra.tools.nodetool.stats;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* Create a test vector for unit testing of TableStats features.
@ -52,7 +50,7 @@ public class TableStatsTestBase
protected static List<StatsTable> testTables;
/**
* @returns StatsKeyspace an instance of StatsKeyspace preset with values for use in a test vector
* @return StatsKeyspace an instance of StatsKeyspace preset with values for use in a test vector
*/
private static StatsKeyspace createStatsKeyspaceTemplate(String keyspaceName)
{
@ -60,14 +58,14 @@ public class TableStatsTestBase
}
/**
* @returns StatsTable an instance of StatsTable preset with values for use in a test vector
* @return StatsTable an instance of StatsTable preset with values for use in a test vector
*/
private static StatsTable createStatsTableTemplate(String keyspaceName, String tableName)
{
StatsTable template = new StatsTable();
template.fullName = keyspaceName + "." + tableName;
template.keyspaceName = new String(keyspaceName);
template.tableName = new String(tableName);
template.keyspaceName = keyspaceName;
template.tableName = tableName;
template.isIndex = false;
template.sstableCount = 0L;
template.oldSSTableCount = 0L;
@ -96,9 +94,6 @@ public class TableStatsTestBase
template.compactedPartitionMinimumBytes = 0L;
template.compactedPartitionMaximumBytes = 0L;
template.compactedPartitionMeanBytes = 0L;
template.bytesRepaired = 0L;
template.bytesUnrepaired = 0L;
template.bytesPendingRepair = 0L;
template.averageLiveCellsPerSliceLastFiveMinutes = Double.NaN;
template.maximumLiveCellsPerSliceLastFiveMinutes = 0L;
template.averageTombstonesPerSliceLastFiveMinutes = Double.NaN;
@ -132,19 +127,19 @@ public class TableStatsTestBase
table5.averageTombstonesPerSliceLastFiveMinutes = 4.01D;
table6.averageTombstonesPerSliceLastFiveMinutes = 6D;
// bloom filter false positives: 2 > 4 > 6 > 1 > 3 > 5
table1.bloomFilterFalsePositives = (Object) 30L;
table2.bloomFilterFalsePositives = (Object) 600L;
table3.bloomFilterFalsePositives = (Object) 20L;
table4.bloomFilterFalsePositives = (Object) 500L;
table5.bloomFilterFalsePositives = (Object) 10L;
table6.bloomFilterFalsePositives = (Object) 400L;
table1.bloomFilterFalsePositives = 30L;
table2.bloomFilterFalsePositives = 600L;
table3.bloomFilterFalsePositives = 20L;
table4.bloomFilterFalsePositives = 500L;
table5.bloomFilterFalsePositives = 10L;
table6.bloomFilterFalsePositives = 400L;
// bloom filter false positive ratio: 5 > 3 > 1 > 6 > 4 > 2
table1.bloomFilterFalseRatio = (Object) 0.40D;
table2.bloomFilterFalseRatio = (Object) 0.01D;
table3.bloomFilterFalseRatio = (Object) 0.50D;
table4.bloomFilterFalseRatio = (Object) 0.02D;
table5.bloomFilterFalseRatio = (Object) 0.60D;
table6.bloomFilterFalseRatio = (Object) 0.03D;
table1.bloomFilterFalseRatio = 0.40D;
table2.bloomFilterFalseRatio = 0.01D;
table3.bloomFilterFalseRatio = 0.50D;
table4.bloomFilterFalseRatio = 0.02D;
table5.bloomFilterFalseRatio = 0.60D;
table6.bloomFilterFalseRatio = 0.03D;
// bloom filter space used: 2 > 4 > 6 > 1 > 3 > 5
table1.bloomFilterSpaceUsed = "789";
table2.bloomFilterSpaceUsed = "161718";
@ -223,12 +218,12 @@ public class TableStatsTestBase
table5.maximumTombstonesPerSliceLastFiveMinutes = 5L;
table6.maximumTombstonesPerSliceLastFiveMinutes = 6L;
// memtable cell count: 3 > 5 > 6 > 1 > 2 > 4
table1.memtableCellCount = (Object) 111L;
table2.memtableCellCount = (Object) 22L;
table3.memtableCellCount = (Object) 333333L;
table4.memtableCellCount = (Object) 4L;
table5.memtableCellCount = (Object) 55555L;
table6.memtableCellCount = (Object) 6666L;
table1.memtableCellCount = 111L;
table2.memtableCellCount = 22L;
table3.memtableCellCount = 333333L;
table4.memtableCellCount = 4L;
table5.memtableCellCount = 55555L;
table6.memtableCellCount = 6666L;
// memtable data size: 6 > 5 > 4 > 3 > 2 > 1
table1.memtableDataSize = "0";
table2.memtableDataSize = "900";
@ -237,26 +232,26 @@ public class TableStatsTestBase
table5.memtableDataSize = "20000";
table6.memtableDataSize = "1000000";
// memtable switch count: 4 > 2 > 3 > 6 > 5 > 1
table1.memtableSwitchCount = (Object) 1L;
table2.memtableSwitchCount = (Object) 22222L;
table3.memtableSwitchCount = (Object) 3333L;
table4.memtableSwitchCount = (Object) 444444L;
table5.memtableSwitchCount = (Object) 5L;
table6.memtableSwitchCount = (Object) 6L;
table1.memtableSwitchCount = 1L;
table2.memtableSwitchCount = 22222L;
table3.memtableSwitchCount = 3333L;
table4.memtableSwitchCount = 444444L;
table5.memtableSwitchCount = 5L;
table6.memtableSwitchCount = 6L;
// number of partitions estimate: 1 > 2 > 3 > 4 > 5 > 6
table1.numberOfPartitionsEstimate = (Object) 111111L;
table2.numberOfPartitionsEstimate = (Object) 22222L;
table3.numberOfPartitionsEstimate = (Object) 3333L;
table4.numberOfPartitionsEstimate = (Object) 444L;
table5.numberOfPartitionsEstimate = (Object) 55L;
table6.numberOfPartitionsEstimate = (Object) 6L;
table1.numberOfPartitionsEstimate = 111111L;
table2.numberOfPartitionsEstimate = 22222L;
table3.numberOfPartitionsEstimate = 3333L;
table4.numberOfPartitionsEstimate = 444L;
table5.numberOfPartitionsEstimate = 55L;
table6.numberOfPartitionsEstimate = 6L;
// pending flushes: 2 > 1 > 4 > 3 > 6 > 5
table1.pendingFlushes = (Object) 11111L;
table2.pendingFlushes = (Object) 222222L;
table3.pendingFlushes = (Object) 333L;
table4.pendingFlushes = (Object) 4444L;
table5.pendingFlushes = (Object) 5L;
table6.pendingFlushes = (Object) 66L;
table1.pendingFlushes = 11111L;
table2.pendingFlushes = 222222L;
table3.pendingFlushes = 333L;
table4.pendingFlushes = 4444L;
table5.pendingFlushes = 5L;
table6.pendingFlushes = 66L;
// percent repaired: 1 > 2 > 3 > 5 > 4 > 6
table1.percentRepaired = 100.0D;
table2.percentRepaired = 99.9D;
@ -286,19 +281,19 @@ public class TableStatsTestBase
table5.spaceUsedTotal = "64";
table6.spaceUsedTotal = "0";
// sstable compression ratio: 5 > 4 > 1 = 2 = 6 > 3
table1.sstableCompressionRatio = (Object) 0.68D;
table2.sstableCompressionRatio = (Object) 0.68D;
table3.sstableCompressionRatio = (Object) 0.32D;
table4.sstableCompressionRatio = (Object) 0.95D;
table5.sstableCompressionRatio = (Object) 0.99D;
table6.sstableCompressionRatio = (Object) 0.68D;
table1.sstableCompressionRatio = 0.68D;
table2.sstableCompressionRatio = 0.68D;
table3.sstableCompressionRatio = 0.32D;
table4.sstableCompressionRatio = 0.95D;
table5.sstableCompressionRatio = 0.99D;
table6.sstableCompressionRatio = 0.68D;
// sstable count: 1 > 3 > 5 > 2 > 4 > 6
table1.sstableCount = (Object) 60000;
table2.sstableCount = (Object) 3000;
table3.sstableCount = (Object) 50000;
table4.sstableCount = (Object) 2000;
table5.sstableCount = (Object) 40000;
table6.sstableCount = (Object) 1000;
table1.sstableCount = 60000;
table2.sstableCount = 3000;
table3.sstableCount = 50000;
table4.sstableCount = 2000;
table5.sstableCount = 40000;
table6.sstableCount = 1000;
// set even numbered tables to have some offheap usage
table2.offHeapUsed = true;
table4.offHeapUsed = true;
@ -336,7 +331,7 @@ public class TableStatsTestBase
table4.memtableOffHeapMemoryUsed = "141421356";
table6.memtableOffHeapMemoryUsed = "161803398";
// create test keyspaces from templates
testKeyspaces = new ArrayList<StatsKeyspace>();
testKeyspaces = new ArrayList<>();
StatsKeyspace keyspace1 = createStatsKeyspaceTemplate("keyspace1");
StatsKeyspace keyspace2 = createStatsKeyspaceTemplate("keyspace2");
StatsKeyspace keyspace3 = createStatsKeyspaceTemplate("keyspace3");
@ -364,7 +359,7 @@ public class TableStatsTestBase
testKeyspaces.set(i, ks);
}
// populate testTables test vector
testTables = new ArrayList<StatsTable>();
testTables = new ArrayList<>();
testTables.add(table1);
testTables.add(table2);
testTables.add(table3);
@ -394,7 +389,7 @@ public class TableStatsTestBase
humanReadableTable5.memtableDataSize = "3.14 MiB";
humanReadableTable6.memtableDataSize = "0 bytes";
// create human readable keyspaces from template
humanReadableKeyspaces = new ArrayList<StatsKeyspace>();
humanReadableKeyspaces = new ArrayList<>();
StatsKeyspace humanReadableKeyspace1 = createStatsKeyspaceTemplate("keyspace1");
StatsKeyspace humanReadableKeyspace2 = createStatsKeyspaceTemplate("keyspace2");
StatsKeyspace humanReadableKeyspace3 = createStatsKeyspaceTemplate("keyspace3");
@ -422,7 +417,7 @@ public class TableStatsTestBase
humanReadableKeyspaces.set(i, ks);
}
// populate human readable tables test vector
humanReadableTables = new ArrayList<StatsTable>();
humanReadableTables = new ArrayList<>();
humanReadableTables.add(humanReadableTable1);
humanReadableTables.add(humanReadableTable2);
humanReadableTables.add(humanReadableTable3);