You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ru...@apache.org on 2018/04/11 01:10:32 UTC
[2/2] cassandra git commit: sort and limit output with nodetool
tablestats
sort and limit output with nodetool tablestats
Patch by Patrick Bannister, reviewed by Jon Haddad for CASSANDRA-13889
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/c90b0d62
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/c90b0d62
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/c90b0d62
Branch: refs/heads/trunk
Commit: c90b0d62bc32c91c3a98ab691cb36c8177f12871
Parents: e75c517
Author: Patrick Bannister <pt...@gmail.com>
Authored: Tue Apr 3 23:58:50 2018 +0000
Committer: Jon Haddad <jo...@jonhaddad.com>
Committed: Tue Apr 10 17:44:15 2018 -0700
----------------------------------------------------------------------
CHANGES.txt | 1 +
NEWS.txt | 1 +
.../org/apache/cassandra/io/util/FileUtils.java | 39 ++
.../cassandra/tools/nodetool/TableStats.java | 43 +-
.../tools/nodetool/stats/StatsTable.java | 4 +-
.../nodetool/stats/StatsTableComparator.java | 336 +++++++++++++++
.../tools/nodetool/stats/TableStatsHolder.java | 184 +++++---
.../tools/nodetool/stats/TableStatsPrinter.java | 130 +++---
.../apache/cassandra/io/util/FileUtilsTest.java | 29 ++
.../stats/StatsTableComparatorTest.java | 311 +++++++++++++
.../nodetool/stats/TableStatsPrinterTest.java | 366 ++++++++++++++++
.../nodetool/stats/TableStatsTestBase.java | 432 +++++++++++++++++++
12 files changed, 1755 insertions(+), 121 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index bb8c731..0059ce0 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
4.0
+ * Add options to nodetool tablestats to sort and limit output (CASSANDRA-13889)
* Rename internals to reflect CQL vocabulary
(CASSANDRA-14354)
* Add support for hybrid MIN(), MAX() speculative retry policies
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/NEWS.txt
----------------------------------------------------------------------
diff --git a/NEWS.txt b/NEWS.txt
index f8e3ca6..df4c32a 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -69,6 +69,7 @@ New features
- SSTableDump now supports the -l option to output each partition as it's own json object
See CASSANDRA-13848 for more detail
- Metric for coordinator writes per table has been added. See CASSANDRA-14232
+ - Nodetool cfstats now has options to sort by various metrics as well as limit results.
Upgrading
---------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/src/java/org/apache/cassandra/io/util/FileUtils.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/io/util/FileUtils.java b/src/java/org/apache/cassandra/io/util/FileUtils.java
index b8be84f..a085813 100644
--- a/src/java/org/apache/cassandra/io/util/FileUtils.java
+++ b/src/java/org/apache/cassandra/io/util/FileUtils.java
@@ -400,6 +400,45 @@ public final class FileUtils
ScheduledExecutors.nonPeriodicTasks.execute(runnable);
}
+ public static long parseFileSize(String value)
+ {
+ long result;
+ if (!value.matches("\\d+(\\.\\d+)? (GiB|KiB|MiB|TiB|bytes)"))
+ {
+ throw new IllegalArgumentException(
+ String.format("value %s is not a valid human-readable file size", value));
+ }
+ if (value.endsWith(" TiB"))
+ {
+ result = Math.round(Double.valueOf(value.replace(" TiB", "")) * ONE_TB);
+ return result;
+ }
+ else if (value.endsWith(" GiB"))
+ {
+ result = Math.round(Double.valueOf(value.replace(" GiB", "")) * ONE_GB);
+ return result;
+ }
+ else if (value.endsWith(" KiB"))
+ {
+ result = Math.round(Double.valueOf(value.replace(" KiB", "")) * ONE_KB);
+ return result;
+ }
+ else if (value.endsWith(" MiB"))
+ {
+ result = Math.round(Double.valueOf(value.replace(" MiB", "")) * ONE_MB);
+ return result;
+ }
+ else if (value.endsWith(" bytes"))
+ {
+ result = Math.round(Double.valueOf(value.replace(" bytes", "")));
+ return result;
+ }
+ else
+ {
+ throw new IllegalStateException(String.format("FileUtils.parseFileSize() reached an illegal state parsing %s", value));
+ }
+ }
+
public static String stringifyFileSize(double value)
{
double d;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
index ac23497..5f5651b 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
@@ -18,6 +18,7 @@
package org.apache.cassandra.tools.nodetool;
import java.util.*;
+
import io.airlift.airline.Arguments;
import io.airlift.airline.Command;
import io.airlift.airline.Option;
@@ -45,6 +46,28 @@ public class TableStats extends NodeToolCmd
description = "Output format (json, yaml)")
private String outputFormat = "";
+ @Option(title = "sort_key",
+ name = {"-s", "--sort"},
+ description = "Sort tables by specified sort key (average_live_cells_per_slice_last_five_minutes, "
+ + "average_tombstones_per_slice_last_five_minutes, bloom_filter_false_positives, "
+ + "bloom_filter_false_ratio, bloom_filter_off_heap_memory_used, bloom_filter_space_used, "
+ + "compacted_partition_maximum_bytes, compacted_partition_mean_bytes, "
+ + "compacted_partition_minimum_bytes, compression_metadata_off_heap_memory_used, "
+ + "dropped_mutations, full_name, index_summary_off_heap_memory_used, local_read_count, "
+ + "local_read_latency_ms, local_write_latency_ms, "
+ + "maximum_live_cells_per_slice_last_five_minutes, "
+ + "maximum_tombstones_per_slice_last_five_minutes, memtable_cell_count, memtable_data_size, "
+ + "memtable_off_heap_memory_used, memtable_switch_count, number_of_partitions_estimate, "
+ + "off_heap_memory_used_total, pending_flushes, percent_repaired, read_latency, reads, "
+ + "space_used_by_snapshots_total, space_used_live, space_used_total, "
+ + "sstable_compression_ratio, sstable_count, table_name, write_latency, writes)")
+ private String sortKey = "";
+
+ @Option(title = "top",
+ name = {"-t", "--top"},
+ description = "Show only the top K tables for the sort key (specify the number K of tables to be shown")
+ private int top = 0;
+
@Override
public void execute(NodeProbe probe)
{
@@ -53,9 +76,25 @@ public class TableStats extends NodeToolCmd
throw new IllegalArgumentException("arguments for -F are json,yaml only.");
}
- StatsHolder holder = new TableStatsHolder(probe, humanReadable, ignore, tableNames);
+ if (!sortKey.isEmpty() && !Arrays.asList(StatsTableComparator.supportedSortKeys).contains(sortKey))
+ {
+ throw new IllegalArgumentException(String.format("argument for sort must be one of: %s",
+ String.join(", ", StatsTableComparator.supportedSortKeys)));
+ }
+
+ if (top > 0 && sortKey.isEmpty())
+ {
+ throw new IllegalArgumentException("cannot filter top K tables without specifying a sort key.");
+ }
+
+ if (top < 0)
+ {
+ throw new IllegalArgumentException("argument for top must be a positive integer.");
+ }
+
+ StatsHolder holder = new TableStatsHolder(probe, humanReadable, ignore, tableNames, sortKey, top);
// print out the keyspace and table statistics
- StatsPrinter printer = TableStatsPrinter.from(outputFormat);
+ StatsPrinter printer = TableStatsPrinter.from(outputFormat, !sortKey.isEmpty());
printer.print(holder, System.out);
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
index 642e05f..908c856 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
@@ -23,7 +23,9 @@ import java.util.List;
public class StatsTable
{
- public String name;
+ public String fullName;
+ public String keyspaceName;
+ public String tableName;
public boolean isIndex;
public boolean isLeveledSstable = false;
public Object sstableCount;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java
new file mode 100644
index 0000000..065d89e
--- /dev/null
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTableComparator.java
@@ -0,0 +1,336 @@
+/*
+ * 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.util.Comparator;
+
+import org.apache.cassandra.io.util.FileUtils;
+
+/**
+ * Comparator to sort StatsTables by a named statistic.
+ */
+public class StatsTableComparator implements Comparator
+{
+
+ /**
+ * Name of the stat that will be used as the sort key.
+ */
+ private final String sortKey;
+
+ /**
+ * Whether data size stats are printed human readable.
+ */
+ private final boolean humanReadable;
+
+ /**
+ * Whether sorting should be done ascending.
+ */
+ private final boolean ascending;
+
+ /**
+ * Names of supported sort keys as they should be specified on the command line.
+ */
+ public static final String[] supportedSortKeys = { "average_live_cells_per_slice_last_five_minutes",
+ "average_tombstones_per_slice_last_five_minutes",
+ "bloom_filter_false_positives", "bloom_filter_false_ratio",
+ "bloom_filter_off_heap_memory_used", "bloom_filter_space_used",
+ "compacted_partition_maximum_bytes",
+ "compacted_partition_mean_bytes",
+ "compacted_partition_minimum_bytes",
+ "compression_metadata_off_heap_memory_used", "dropped_mutations",
+ "full_name", "index_summary_off_heap_memory_used",
+ "local_read_count", "local_read_latency_ms",
+ "local_write_latency_ms",
+ "maximum_live_cells_per_slice_last_five_minutes",
+ "maximum_tombstones_per_slice_last_five_minutes",
+ "memtable_cell_count", "memtable_data_size",
+ "memtable_off_heap_memory_used", "memtable_switch_count",
+ "number_of_partitions_estimate", "off_heap_memory_used_total",
+ "pending_flushes", "percent_repaired", "read_latency", "reads",
+ "space_used_by_snapshots_total", "space_used_live",
+ "space_used_total", "sstable_compression_ratio", "sstable_count",
+ "table_name", "write_latency", "writes" };
+
+ public StatsTableComparator(String sortKey, boolean humanReadable)
+ {
+ this(sortKey, humanReadable, false);
+ }
+
+ public StatsTableComparator(String sortKey, boolean humanReadable, boolean ascending)
+ {
+ this.sortKey = sortKey;
+ this.humanReadable = humanReadable;
+ this.ascending = ascending;
+ }
+
+ /**
+ * Compare stats represented as doubles
+ */
+ private int compareDoubles(double x, double y)
+ {
+ int sign = ascending ? 1 : -1;
+ if (Double.isNaN(x) && !Double.isNaN(y))
+ return sign * Double.valueOf(0D).compareTo(Double.valueOf(y));
+ else if (!Double.isNaN(x) && Double.isNaN(y))
+ return sign * Double.valueOf(x).compareTo(Double.valueOf(0D));
+ else if (Double.isNaN(x) && Double.isNaN(y))
+ return 0;
+ else
+ return sign * Double.valueOf(x).compareTo(Double.valueOf(y));
+ }
+
+ /**
+ * Compare file size stats represented as strings
+ */
+ private int compareFileSizes(String x, String y)
+ {
+ int sign = ascending ? 1 : -1;
+ if (null == x && null != y)
+ return sign * -1;
+ else if (null != x && null == y)
+ return sign;
+ else if (null == x && null == y)
+ return 0;
+ long sizeX = humanReadable ? FileUtils.parseFileSize(x) : Long.valueOf(x);
+ long sizeY = humanReadable ? FileUtils.parseFileSize(y) : Long.valueOf(y);
+ return sign * Long.compare(sizeX, sizeY);
+ }
+
+ /**
+ * Compare StatsTable instances based on this instance's sortKey.
+ */
+ public int compare(Object x, Object y)
+ {
+ 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)
+ 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"))
+ {
+ result = compareDoubles(stx.averageLiveCellsPerSliceLastFiveMinutes,
+ sty.averageLiveCellsPerSliceLastFiveMinutes);
+ }
+ else if (sortKey.equals("average_tombstones_per_slice_last_five_minutes"))
+ {
+ result = compareDoubles(stx.averageTombstonesPerSliceLastFiveMinutes,
+ sty.averageTombstonesPerSliceLastFiveMinutes);
+ }
+ else if (sortKey.equals("bloom_filter_false_positives"))
+ {
+ result = sign * ((Long) stx.bloomFilterFalsePositives)
+ .compareTo((Long) sty.bloomFilterFalsePositives);
+ }
+ else if (sortKey.equals("bloom_filter_false_ratio"))
+ {
+ result = compareDoubles((Double) stx.bloomFilterFalseRatio,
+ (Double) sty.bloomFilterFalseRatio);
+ }
+ else if (sortKey.equals("bloom_filter_off_heap_memory_used"))
+ {
+ if (stx.bloomFilterOffHeapUsed && !sty.bloomFilterOffHeapUsed)
+ return sign;
+ else if (!stx.bloomFilterOffHeapUsed && sty.bloomFilterOffHeapUsed)
+ return sign * -1;
+ else if (!stx.bloomFilterOffHeapUsed && !sty.bloomFilterOffHeapUsed)
+ result = 0;
+ else
+ {
+ result = compareFileSizes(stx.bloomFilterOffHeapMemoryUsed,
+ sty.bloomFilterOffHeapMemoryUsed);
+ }
+ }
+ else if (sortKey.equals("bloom_filter_space_used"))
+ {
+ result = compareFileSizes(stx.bloomFilterSpaceUsed,
+ sty.bloomFilterSpaceUsed);
+ }
+ else if (sortKey.equals("compacted_partition_maximum_bytes"))
+ {
+ result = sign * Long.valueOf(stx.compactedPartitionMaximumBytes)
+ .compareTo(Long.valueOf(sty.compactedPartitionMaximumBytes));
+ }
+ else if (sortKey.equals("compacted_partition_mean_bytes"))
+ {
+ result = sign * Long.valueOf(stx.compactedPartitionMeanBytes)
+ .compareTo(Long.valueOf(sty.compactedPartitionMeanBytes));
+ }
+ else if (sortKey.equals("compacted_partition_minimum_bytes"))
+ {
+ result = sign * Long.valueOf(stx.compactedPartitionMinimumBytes)
+ .compareTo(Long.valueOf(sty.compactedPartitionMinimumBytes));
+ }
+ else if (sortKey.equals("compression_metadata_off_heap_memory_used"))
+ {
+ if (stx.compressionMetadataOffHeapUsed && !sty.compressionMetadataOffHeapUsed)
+ return sign;
+ else if (!stx.compressionMetadataOffHeapUsed && sty.compressionMetadataOffHeapUsed)
+ return sign * -1;
+ else if (!stx.compressionMetadataOffHeapUsed && !sty.compressionMetadataOffHeapUsed)
+ result = 0;
+ else
+ {
+ result = compareFileSizes(stx.compressionMetadataOffHeapMemoryUsed,
+ sty.compressionMetadataOffHeapMemoryUsed);
+ }
+ }
+ else if (sortKey.equals("dropped_mutations"))
+ {
+ result = compareFileSizes(stx.droppedMutations, sty.droppedMutations);
+ }
+ else if (sortKey.equals("full_name"))
+ {
+ return sign * stx.fullName.compareTo(sty.fullName);
+ }
+ else if (sortKey.equals("index_summary_off_heap_memory_used"))
+ {
+ if (stx.indexSummaryOffHeapUsed && !sty.indexSummaryOffHeapUsed)
+ return sign;
+ else if (!stx.indexSummaryOffHeapUsed && sty.indexSummaryOffHeapUsed)
+ return sign * -1;
+ else if (!stx.indexSummaryOffHeapUsed && !sty.indexSummaryOffHeapUsed)
+ result = 0;
+ else
+ {
+ result = compareFileSizes(stx.indexSummaryOffHeapMemoryUsed,
+ sty.indexSummaryOffHeapMemoryUsed);
+ }
+ }
+ else if (sortKey.equals("local_read_count") || sortKey.equals("reads"))
+ {
+ result = sign * Long.valueOf(stx.localReadCount)
+ .compareTo(Long.valueOf(sty.localReadCount));
+ }
+ else if (sortKey.equals("local_read_latency_ms") || sortKey.equals("read_latency"))
+ {
+ result = compareDoubles(stx.localReadLatencyMs, sty.localReadLatencyMs);
+ }
+ else if (sortKey.equals("local_write_count") || sortKey.equals("writes"))
+ {
+ result = sign * Long.valueOf(stx.localWriteCount)
+ .compareTo(Long.valueOf(sty.localWriteCount));
+ }
+ else if (sortKey.equals("local_write_latency_ms") || sortKey.equals("write_latency"))
+ {
+ result = compareDoubles(stx.localWriteLatencyMs, sty.localWriteLatencyMs);
+ }
+ else if (sortKey.equals("maximum_live_cells_per_slice_last_five_minutes"))
+ {
+ result = sign * Long.valueOf(stx.maximumLiveCellsPerSliceLastFiveMinutes)
+ .compareTo(Long.valueOf(sty.maximumLiveCellsPerSliceLastFiveMinutes));
+ }
+ else if (sortKey.equals("maximum_tombstones_per_slice_last_five_minutes"))
+ {
+ result = sign * Long.valueOf(stx.maximumTombstonesPerSliceLastFiveMinutes)
+ .compareTo(Long.valueOf(sty.maximumTombstonesPerSliceLastFiveMinutes));
+ }
+ else if (sortKey.equals("memtable_cell_count"))
+ {
+ result = sign * ((Long) stx.memtableCellCount)
+ .compareTo((Long) sty.memtableCellCount);
+ }
+ else if (sortKey.equals("memtable_data_size"))
+ {
+ result = compareFileSizes(stx.memtableDataSize, sty.memtableDataSize);
+ }
+ else if (sortKey.equals("memtable_off_heap_memory_used"))
+ {
+ if (stx.memtableOffHeapUsed && !sty.memtableOffHeapUsed)
+ return sign;
+ else if (!stx.memtableOffHeapUsed && sty.memtableOffHeapUsed)
+ return sign * -1;
+ else if (!stx.memtableOffHeapUsed && !sty.memtableOffHeapUsed)
+ result = 0;
+ else
+ {
+ result = compareFileSizes(stx.memtableOffHeapMemoryUsed,
+ sty.memtableOffHeapMemoryUsed);
+ }
+ }
+ else if (sortKey.equals("memtable_switch_count"))
+ {
+ result = sign * ((Long) stx.memtableSwitchCount)
+ .compareTo((Long) sty.memtableSwitchCount);
+ }
+ else if (sortKey.equals("number_of_partitions_estimate"))
+ {
+ result = sign * ((Long) stx.numberOfPartitionsEstimate)
+ .compareTo((Long) sty.numberOfPartitionsEstimate);
+ }
+ else if (sortKey.equals("off_heap_memory_used_total"))
+ {
+ if (stx.offHeapUsed && !sty.offHeapUsed)
+ return sign;
+ else if (!stx.offHeapUsed && sty.offHeapUsed)
+ return sign * -1;
+ else if (!stx.offHeapUsed && !sty.offHeapUsed)
+ result = 0;
+ else
+ {
+ result = compareFileSizes(stx.offHeapMemoryUsedTotal,
+ sty.offHeapMemoryUsedTotal);
+ }
+ }
+ else if (sortKey.equals("pending_flushes"))
+ {
+ result = sign * ((Long) stx.pendingFlushes)
+ .compareTo((Long) sty.pendingFlushes);
+ }
+ else if (sortKey.equals("percent_repaired"))
+ {
+ result = compareDoubles(stx.percentRepaired, sty.percentRepaired);
+ }
+ else if (sortKey.equals("space_used_by_snapshots_total"))
+ {
+ result = compareFileSizes(stx.spaceUsedBySnapshotsTotal,
+ sty.spaceUsedBySnapshotsTotal);
+ }
+ else if (sortKey.equals("space_used_live"))
+ {
+ result = compareFileSizes(stx.spaceUsedLive, sty.spaceUsedLive);
+ }
+ else if (sortKey.equals("space_used_total"))
+ {
+ result = compareFileSizes(stx.spaceUsedTotal, sty.spaceUsedTotal);
+ }
+ else if (sortKey.equals("sstable_compression_ratio"))
+ {
+ result = compareDoubles((Double) stx.sstableCompressionRatio,
+ (Double) sty.sstableCompressionRatio);
+ }
+ else if (sortKey.equals("sstable_count"))
+ {
+ result = sign * ((Integer) stx.sstableCount)
+ .compareTo((Integer) sty.sstableCount);
+ }
+ else if (sortKey.equals("table_name"))
+ {
+ return sign * stx.tableName.compareTo(sty.tableName);
+ }
+ else
+ {
+ throw new IllegalStateException(String.format("Unsupported sort key: %s", sortKey));
+ }
+ return (result == 0) ? stx.fullName.compareTo(sty.fullName) : result;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
index 929619a..79531c1 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
@@ -20,10 +20,9 @@ package org.apache.cassandra.tools.nodetool.stats;
import java.util.*;
-import javax.management.InstanceNotFoundException;
-
import com.google.common.collect.ArrayListMultimap;
+import javax.management.InstanceNotFoundException;
import org.apache.cassandra.db.*;
import org.apache.cassandra.io.util.*;
import org.apache.cassandra.metrics.*;
@@ -33,17 +32,41 @@ public class TableStatsHolder implements StatsHolder
{
public final List<StatsKeyspace> keyspaces;
public final int numberOfTables;
+ public final boolean humanReadable;
+ public final String sortKey;
+ public final int top;
- public TableStatsHolder(NodeProbe probe, boolean humanReadable, boolean ignore, List<String> tableNames)
+ public TableStatsHolder(NodeProbe probe, boolean humanReadable, boolean ignore, List<String> tableNames, String sortKey, int top)
{
this.keyspaces = new ArrayList<>();
- this.numberOfTables = probe.getNumberOfTables();
- this.initializeKeyspaces(probe, humanReadable, ignore, tableNames);
+ this.humanReadable = humanReadable;
+ this.sortKey = sortKey;
+ this.top = top;
+ if (!this.isTestTableStatsHolder())
+ {
+ this.numberOfTables = probe.getNumberOfTables();
+ this.initializeKeyspaces(probe, ignore, tableNames);
+ }
+ else
+ {
+ this.numberOfTables = 0;
+ }
}
@Override
public Map<String, Object> convert2Map()
{
+ if (sortKey.isEmpty())
+ return convertAllToMap();
+ else
+ return convertSortedFilteredSubsetToMap();
+ }
+
+ /**
+ * @returns Map<String, Object> a nested HashMap of keyspaces, their tables, and the tables' statistics.
+ */
+ private Map<String, Object> convertAllToMap()
+ {
HashMap<String, Object> mpRet = new HashMap<>();
mpRet.put("total_number_of_tables", numberOfTables);
for (StatsKeyspace keyspace : keyspaces)
@@ -62,56 +85,8 @@ public class TableStatsHolder implements StatsHolder
Map<String, Map<String, Object>> mpTables = new HashMap<>();
for (StatsTable table : tables)
{
- Map<String, Object> mpTable = new HashMap<>();
-
- mpTable.put("sstables_in_each_level", table.sstablesInEachLevel);
- mpTable.put("space_used_live", table.spaceUsedLive);
- mpTable.put("space_used_total", table.spaceUsedTotal);
- mpTable.put("space_used_by_snapshots_total", table.spaceUsedBySnapshotsTotal);
- if (table.offHeapUsed)
- mpTable.put("off_heap_memory_used_total", table.offHeapMemoryUsedTotal);
- mpTable.put("sstable_compression_ratio", table.sstableCompressionRatio);
- mpTable.put("number_of_partitions_estimate", table.numberOfPartitionsEstimate);
- mpTable.put("memtable_cell_count", table.memtableCellCount);
- mpTable.put("memtable_data_size", table.memtableDataSize);
- if (table.memtableOffHeapUsed)
- mpTable.put("memtable_off_heap_memory_used", table.memtableOffHeapMemoryUsed);
- mpTable.put("memtable_switch_count", table.memtableSwitchCount);
- mpTable.put("local_read_count", table.localReadCount);
- mpTable.put("local_read_latency_ms", String.format("%01.3f", table.localReadLatencyMs));
- mpTable.put("local_write_count", table.localWriteCount);
- mpTable.put("local_write_latency_ms", String.format("%01.3f", table.localWriteLatencyMs));
- mpTable.put("pending_flushes", table.pendingFlushes);
- mpTable.put("percent_repaired", table.percentRepaired);
-
- mpTable.put("bytes_repaired", table.bytesRepaired);
- mpTable.put("bytes_unrepaired", table.bytesUnrepaired);
- mpTable.put("bytes_pending_repair", table.bytesPendingRepair);
-
- mpTable.put("bloom_filter_false_positives", table.bloomFilterFalsePositives);
- mpTable.put("bloom_filter_false_ratio", String.format("%01.5f", table.bloomFilterFalseRatio));
- mpTable.put("bloom_filter_space_used", table.bloomFilterSpaceUsed);
- if (table.bloomFilterOffHeapUsed)
- mpTable.put("bloom_filter_off_heap_memory_used", table.bloomFilterOffHeapMemoryUsed);
- if (table.indexSummaryOffHeapUsed)
- mpTable.put("index_summary_off_heap_memory_used", table.indexSummaryOffHeapMemoryUsed);
- if (table.compressionMetadataOffHeapUsed)
- mpTable.put("compression_metadata_off_heap_memory_used",
- table.compressionMetadataOffHeapMemoryUsed);
- mpTable.put("compacted_partition_minimum_bytes", table.compactedPartitionMinimumBytes);
- mpTable.put("compacted_partition_maximum_bytes", table.compactedPartitionMaximumBytes);
- mpTable.put("compacted_partition_mean_bytes", table.compactedPartitionMeanBytes);
- mpTable.put("average_live_cells_per_slice_last_five_minutes",
- table.averageLiveCellsPerSliceLastFiveMinutes);
- mpTable.put("maximum_live_cells_per_slice_last_five_minutes",
- table.maximumLiveCellsPerSliceLastFiveMinutes);
- mpTable.put("average_tombstones_per_slice_last_five_minutes",
- table.averageTombstonesPerSliceLastFiveMinutes);
- mpTable.put("maximum_tombstones_per_slice_last_five_minutes",
- table.maximumTombstonesPerSliceLastFiveMinutes);
- mpTable.put("dropped_mutations", table.droppedMutations);
-
- mpTables.put(table.name, mpTable);
+ Map<String, Object> mpTable = convertStatsTableToMap(table);
+ mpTables.put(table.tableName, mpTable);
}
mpKeyspace.put("tables", mpTables);
mpRet.put(keyspace.name, mpKeyspace);
@@ -119,7 +94,71 @@ public class TableStatsHolder implements StatsHolder
return mpRet;
}
- private void initializeKeyspaces(NodeProbe probe, boolean humanReadable, boolean ignore, List<String> tableNames)
+ /**
+ * @returns Map<String, Object> a nested HashMap of the sorted and filtered table names and the HashMaps of their statistics.
+ */
+ private Map<String, Object> convertSortedFilteredSubsetToMap()
+ {
+ HashMap<String, Object> mpRet = new HashMap<>();
+ mpRet.put("total_number_of_tables", numberOfTables);
+ List<StatsTable> sortedFilteredTables = getSortedFilteredTables();
+ for (StatsTable table : sortedFilteredTables)
+ {
+ String tableDisplayName = table.keyspaceName + "." + table.tableName;
+ Map<String, Object> mpTable = convertStatsTableToMap(table);
+ mpRet.put(tableDisplayName, mpTable);
+ }
+ return mpRet;
+ }
+
+ private Map<String, Object> convertStatsTableToMap(StatsTable table)
+ {
+ Map<String, Object> mpTable = new HashMap<>();
+ mpTable.put("sstables_in_each_level", table.sstablesInEachLevel);
+ mpTable.put("space_used_live", table.spaceUsedLive);
+ mpTable.put("space_used_total", table.spaceUsedTotal);
+ mpTable.put("space_used_by_snapshots_total", table.spaceUsedBySnapshotsTotal);
+ if (table.offHeapUsed)
+ mpTable.put("off_heap_memory_used_total", table.offHeapMemoryUsedTotal);
+ mpTable.put("sstable_compression_ratio", table.sstableCompressionRatio);
+ mpTable.put("number_of_partitions_estimate", table.numberOfPartitionsEstimate);
+ mpTable.put("memtable_cell_count", table.memtableCellCount);
+ mpTable.put("memtable_data_size", table.memtableDataSize);
+ if (table.memtableOffHeapUsed)
+ mpTable.put("memtable_off_heap_memory_used", table.memtableOffHeapMemoryUsed);
+ mpTable.put("memtable_switch_count", table.memtableSwitchCount);
+ mpTable.put("local_read_count", table.localReadCount);
+ mpTable.put("local_read_latency_ms", String.format("%01.3f", table.localReadLatencyMs));
+ mpTable.put("local_write_count", table.localWriteCount);
+ mpTable.put("local_write_latency_ms", String.format("%01.3f", table.localWriteLatencyMs));
+ mpTable.put("pending_flushes", table.pendingFlushes);
+ mpTable.put("percent_repaired", table.percentRepaired);
+ mpTable.put("bloom_filter_false_positives", table.bloomFilterFalsePositives);
+ mpTable.put("bloom_filter_false_ratio", String.format("%01.5f", table.bloomFilterFalseRatio));
+ mpTable.put("bloom_filter_space_used", table.bloomFilterSpaceUsed);
+ if (table.bloomFilterOffHeapUsed)
+ mpTable.put("bloom_filter_off_heap_memory_used", table.bloomFilterOffHeapMemoryUsed);
+ if (table.indexSummaryOffHeapUsed)
+ mpTable.put("index_summary_off_heap_memory_used", table.indexSummaryOffHeapMemoryUsed);
+ if (table.compressionMetadataOffHeapUsed)
+ mpTable.put("compression_metadata_off_heap_memory_used",
+ table.compressionMetadataOffHeapMemoryUsed);
+ mpTable.put("compacted_partition_minimum_bytes", table.compactedPartitionMinimumBytes);
+ mpTable.put("compacted_partition_maximum_bytes", table.compactedPartitionMaximumBytes);
+ mpTable.put("compacted_partition_mean_bytes", table.compactedPartitionMeanBytes);
+ mpTable.put("average_live_cells_per_slice_last_five_minutes",
+ table.averageLiveCellsPerSliceLastFiveMinutes);
+ mpTable.put("maximum_live_cells_per_slice_last_five_minutes",
+ table.maximumLiveCellsPerSliceLastFiveMinutes);
+ mpTable.put("average_tombstones_per_slice_last_five_minutes",
+ table.averageTombstonesPerSliceLastFiveMinutes);
+ mpTable.put("maximum_tombstones_per_slice_last_five_minutes",
+ table.maximumTombstonesPerSliceLastFiveMinutes);
+ mpTable.put("dropped_mutations", table.droppedMutations);
+ return mpTable;
+ }
+
+ private void initializeKeyspaces(NodeProbe probe, boolean ignore, List<String> tableNames)
{
OptionFilter filter = new OptionFilter(ignore, tableNames);
ArrayListMultimap<String, ColumnFamilyStoreMBean> selectedTableMbeans = ArrayListMultimap.create();
@@ -165,7 +204,9 @@ public class TableStatsHolder implements StatsHolder
{
String tableName = table.getTableName();
StatsTable statsTable = new StatsTable();
- statsTable.name = tableName;
+ statsTable.fullName = keyspaceName + "." + tableName;
+ statsTable.keyspaceName = keyspaceName;
+ statsTable.tableName = tableName;
statsTable.isIndex = tableName.contains(".");
statsTable.sstableCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "LiveSSTableCount");
int[] leveledSStables = table.getSSTableCountPerLevel();
@@ -190,9 +231,6 @@ public class TableStatsHolder implements StatsHolder
Long compressionMetadataOffHeapSize = null;
Long offHeapSize = null;
Double percentRepaired = null;
- Long bytesRepaired = null;
- Long bytesUnrepaired = null;
- Long bytesPendingRepair = null;
try
{
@@ -202,9 +240,6 @@ public class TableStatsHolder implements StatsHolder
compressionMetadataOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "CompressionMetadataOffHeapMemoryUsed");
offHeapSize = memtableOffHeapSize + bloomFilterOffHeapSize + indexSummaryOffHeapSize + compressionMetadataOffHeapSize;
percentRepaired = (Double) probe.getColumnFamilyMetric(keyspaceName, tableName, "PercentRepaired");
- bytesRepaired = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "BytesRepaired");
- bytesUnrepaired = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "BytesUnrepaired");
- bytesPendingRepair = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "BytesPendingRepair");
}
catch (RuntimeException e)
{
@@ -226,11 +261,6 @@ public class TableStatsHolder implements StatsHolder
{
statsTable.percentRepaired = Math.round(100 * percentRepaired) / 100.0;
}
-
- statsTable.bytesRepaired = bytesRepaired != null ? bytesRepaired : 0;
- statsTable.bytesUnrepaired = bytesUnrepaired != null ? bytesUnrepaired : 0;
- statsTable.bytesPendingRepair = bytesPendingRepair != null ? bytesPendingRepair : 0;
-
statsTable.sstableCompressionRatio = probe.getColumnFamilyMetric(keyspaceName, tableName, "CompressionRatio");
Object estimatedPartitionCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "EstimatedPartitionCount");
if (Long.valueOf(-1L).equals(estimatedPartitionCount))
@@ -303,6 +333,24 @@ public class TableStatsHolder implements StatsHolder
}
/**
+ * Sort and filter this TableStatHolder's tables as specified by its sortKey and top attributes.
+ */
+ public List<StatsTable> getSortedFilteredTables() {
+ List<StatsTable> tables = new ArrayList<>();
+ for (StatsKeyspace keyspace : keyspaces)
+ tables.addAll(keyspace.tables);
+ Collections.sort(tables, new StatsTableComparator(sortKey, humanReadable));
+ int k = (tables.size() >= top) ? top : tables.size();
+ if (k > 0)
+ tables = tables.subList(0, k);
+ return tables;
+ }
+
+ protected boolean isTestTableStatsHolder() {
+ return false;
+ }
+
+ /**
* Used for filtering keyspaces and tables to be displayed using the tablestats command.
*/
private static class OptionFilter
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
index 6138eed..4ea7562 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
@@ -21,11 +21,9 @@ package org.apache.cassandra.tools.nodetool.stats;
import java.io.PrintStream;
import java.util.List;
-import org.apache.cassandra.utils.FBUtilities;
-
public class TableStatsPrinter
{
- public static StatsPrinter from(String format)
+ public static StatsPrinter from(String format, boolean sorted)
{
switch (format)
{
@@ -34,10 +32,16 @@ public class TableStatsPrinter
case "yaml":
return new StatsPrinter.YamlPrinter();
default:
- return new DefaultPrinter();
+ if (sorted)
+ return new SortedDefaultPrinter();
+ else
+ return new DefaultPrinter();
}
}
+ /**
+ * A StatsPrinter to print stats in a keyspace-centric way, nesting stats for each table under their parent keyspaces.
+ */
private static class DefaultPrinter implements StatsPrinter<TableStatsHolder>
{
@Override
@@ -61,60 +65,86 @@ public class TableStatsPrinter
List<StatsTable> tables = keyspace.tables;
for (StatsTable table : tables)
{
- out.println("\t\tTable" + (table.isIndex ? " (index): " + table.name : ": ") + table.name);
- out.println("\t\tSSTable count: " + table.sstableCount);
- if (table.isLeveledSstable)
- out.println("\t\tSSTables in each level: [" + String.join(", ",
- table.sstablesInEachLevel) + "]");
+ printStatsTable(table, table.tableName, "\t\t", out);
+ }
+ out.println("----------------");
+ }
+ }
- out.println("\t\tSpace used (live): " + table.spaceUsedLive);
- out.println("\t\tSpace used (total): " + table.spaceUsedTotal);
- out.println("\t\tSpace used by snapshots (total): " + table.spaceUsedBySnapshotsTotal);
+ protected void printStatsTable(StatsTable table, String tableDisplayName, String indent, PrintStream out)
+ {
+ out.println(indent + "Table" + (table.isIndex ? " (index): " + table.tableName : ": ") + tableDisplayName);
+ out.println(indent + "SSTable count: " + table.sstableCount);
+ if (table.isLeveledSstable)
+ out.println(indent + "SSTables in each level: [" + String.join(", ",
+ table.sstablesInEachLevel) + "]");
- if (table.offHeapUsed)
- out.println("\t\tOff heap memory used (total): " + table.offHeapMemoryUsedTotal);
- out.println("\t\tSSTable Compression Ratio: " + table.sstableCompressionRatio);
- out.println("\t\tNumber of partitions (estimate): " + table.numberOfPartitionsEstimate);
- out.println("\t\tMemtable cell count: " + table.memtableCellCount);
- out.println("\t\tMemtable data size: " + table.memtableDataSize);
+ out.println(indent + "Space used (live): " + table.spaceUsedLive);
+ out.println(indent + "Space used (total): " + table.spaceUsedTotal);
+ out.println(indent + "Space used by snapshots (total): " + table.spaceUsedBySnapshotsTotal);
- if (table.memtableOffHeapUsed)
- out.println("\t\tMemtable off heap memory used: " + table.memtableOffHeapMemoryUsed);
- out.println("\t\tMemtable switch count: " + table.memtableSwitchCount);
- out.println("\t\tLocal read count: " + table.localReadCount);
- out.printf("\t\tLocal read latency: %01.3f ms%n", table.localReadLatencyMs);
- out.println("\t\tLocal write count: " + table.localWriteCount);
- out.printf("\t\tLocal write latency: %01.3f ms%n", table.localWriteLatencyMs);
- out.println("\t\tPending flushes: " + table.pendingFlushes);
- out.println("\t\tPercent repaired: " + table.percentRepaired);
+ if (table.offHeapUsed)
+ out.println(indent + "Off heap memory used (total): " + table.offHeapMemoryUsedTotal);
+ out.println(indent + "SSTable Compression Ratio: " + table.sstableCompressionRatio);
+ out.println(indent + "Number of partitions (estimate): " + table.numberOfPartitionsEstimate);
+ out.println(indent + "Memtable cell count: " + table.memtableCellCount);
+ out.println(indent + "Memtable data size: " + table.memtableDataSize);
- out.println("\t\tBytes repaired: " + FBUtilities.prettyPrintMemory(table.bytesRepaired));
- out.println("\t\tBytes unrepaired: " + FBUtilities.prettyPrintMemory(table.bytesUnrepaired));
- out.println("\t\tBytes pending repair: " + FBUtilities.prettyPrintMemory(table.bytesPendingRepair));
+ if (table.memtableOffHeapUsed)
+ out.println(indent + "Memtable off heap memory used: " + table.memtableOffHeapMemoryUsed);
+ out.println(indent + "Memtable switch count: " + table.memtableSwitchCount);
+ out.println(indent + "Local read count: " + table.localReadCount);
+ out.printf(indent + "Local read latency: %01.3f ms%n", table.localReadLatencyMs);
+ out.println(indent + "Local write count: " + table.localWriteCount);
+ out.printf(indent + "Local write latency: %01.3f ms%n", table.localWriteLatencyMs);
+ out.println(indent + "Pending flushes: " + table.pendingFlushes);
+ out.println(indent + "Percent repaired: " + table.percentRepaired);
- out.println("\t\tBloom filter false positives: " + table.bloomFilterFalsePositives);
- out.printf("\t\tBloom filter false ratio: %01.5f%n", table.bloomFilterFalseRatio);
- out.println("\t\tBloom filter space used: " + table.bloomFilterSpaceUsed);
+ out.println(indent + "Bloom filter false positives: " + table.bloomFilterFalsePositives);
+ out.printf(indent + "Bloom filter false ratio: %01.5f%n", table.bloomFilterFalseRatio);
+ out.println(indent + "Bloom filter space used: " + table.bloomFilterSpaceUsed);
- if (table.bloomFilterOffHeapUsed)
- out.println("\t\tBloom filter off heap memory used: " + table.bloomFilterOffHeapMemoryUsed);
- if (table.indexSummaryOffHeapUsed)
- out.println("\t\tIndex summary off heap memory used: " + table.indexSummaryOffHeapMemoryUsed);
- if (table.compressionMetadataOffHeapUsed)
- out.println("\t\tCompression metadata off heap memory used: " + table.compressionMetadataOffHeapMemoryUsed);
+ if (table.bloomFilterOffHeapUsed)
+ out.println(indent + "Bloom filter off heap memory used: " + table.bloomFilterOffHeapMemoryUsed);
+ if (table.indexSummaryOffHeapUsed)
+ out.println(indent + "Index summary off heap memory used: " + table.indexSummaryOffHeapMemoryUsed);
+ if (table.compressionMetadataOffHeapUsed)
+ out.println(indent + "Compression metadata off heap memory used: " + table.compressionMetadataOffHeapMemoryUsed);
- out.println("\t\tCompacted partition minimum bytes: " + table.compactedPartitionMinimumBytes);
- out.println("\t\tCompacted partition maximum bytes: " + table.compactedPartitionMaximumBytes);
- out.println("\t\tCompacted partition mean bytes: " + table.compactedPartitionMeanBytes);
- out.println("\t\tAverage live cells per slice (last five minutes): " + table.averageLiveCellsPerSliceLastFiveMinutes);
- out.println("\t\tMaximum live cells per slice (last five minutes): " + table.maximumLiveCellsPerSliceLastFiveMinutes);
- out.println("\t\tAverage tombstones per slice (last five minutes): " + table.averageTombstonesPerSliceLastFiveMinutes);
- out.println("\t\tMaximum tombstones per slice (last five minutes): " + table.maximumTombstonesPerSliceLastFiveMinutes);
- out.println("\t\tDropped Mutations: " + table.droppedMutations);
- out.println("");
- }
- out.println("----------------");
+ out.println(indent + "Compacted partition minimum bytes: " + table.compactedPartitionMinimumBytes);
+ out.println(indent + "Compacted partition maximum bytes: " + table.compactedPartitionMaximumBytes);
+ out.println(indent + "Compacted partition mean bytes: " + table.compactedPartitionMeanBytes);
+ out.println(indent + "Average live cells per slice (last five minutes): " + table.averageLiveCellsPerSliceLastFiveMinutes);
+ out.println(indent + "Maximum live cells per slice (last five minutes): " + table.maximumLiveCellsPerSliceLastFiveMinutes);
+ out.println(indent + "Average tombstones per slice (last five minutes): " + table.averageTombstonesPerSliceLastFiveMinutes);
+ out.println(indent + "Maximum tombstones per slice (last five minutes): " + table.maximumTombstonesPerSliceLastFiveMinutes);
+ out.println(indent + "Dropped Mutations: " + table.droppedMutations);
+ out.println("");
+ }
+ }
+
+ /**
+ * A StatsPrinter to print stats in a sorted, table-centric way.
+ */
+ private static class SortedDefaultPrinter extends DefaultPrinter
+ {
+ @Override
+ public void print(TableStatsHolder data, PrintStream out)
+ {
+ List<StatsTable> tables = data.getSortedFilteredTables();
+ String totalTablesSummary = String.format("Total number of tables: %d", data.numberOfTables);
+ if (data.top > 0)
+ {
+ int k = (data.top <= data.numberOfTables) ? data.top : data.numberOfTables;
+ totalTablesSummary += String.format(" (showing top %d by %s)", k, data.sortKey);
}
+ out.println(totalTablesSummary);
+ out.println("----------------");
+ for (StatsTable table : tables)
+ {
+ printStatsTable(table, table.keyspaceName + "." + table.tableName, "\t", out);
+ }
+ out.println("----------------");
}
}
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java b/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java
index 9a5c03a..8688b66 100644
--- a/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java
+++ b/test/unit/org/apache/cassandra/io/util/FileUtilsTest.java
@@ -45,6 +45,35 @@ public class FileUtilsTest
}
@Test
+ public void testParseFileSize() throws Exception
+ {
+ // test straightforward conversions for each unit
+ assertEquals("FileUtils.parseFileSize() failed to parse a whole number of bytes",
+ 256L, FileUtils.parseFileSize("256 bytes"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a whole number of kilobytes",
+ 2048L, FileUtils.parseFileSize("2 KiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a whole number of megabytes",
+ 4194304L, FileUtils.parseFileSize("4 MiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a whole number of gigabytes",
+ 3221225472L, FileUtils.parseFileSize("3 GiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a whole number of terabytes",
+ 5497558138880L, FileUtils.parseFileSize("5 TiB"));
+ // test conversions of fractional units
+ assertEquals("FileUtils.parseFileSize() failed to parse a rational number of kilobytes",
+ 1536L, FileUtils.parseFileSize("1.5 KiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a rational number of kilobytes",
+ 4434L, FileUtils.parseFileSize("4.33 KiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a rational number of megabytes",
+ 2359296L, FileUtils.parseFileSize("2.25 MiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a rational number of megabytes",
+ 3292529L, FileUtils.parseFileSize("3.14 MiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a rational number of gigabytes",
+ 1299227607L, FileUtils.parseFileSize("1.21 GiB"));
+ assertEquals("FileUtils.parseFileSize() failed to parse a rational number of terabytes",
+ 6621259022467L, FileUtils.parseFileSize("6.022 TiB"));
+ }
+
+ @Test
public void testTruncate() throws IOException
{
File file = FileUtils.createDeletableTempFile("testTruncate", "1");
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/test/unit/org/apache/cassandra/tools/nodetool/stats/StatsTableComparatorTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/tools/nodetool/stats/StatsTableComparatorTest.java b/test/unit/org/apache/cassandra/tools/nodetool/stats/StatsTableComparatorTest.java
new file mode 100644
index 0000000..ea18d51
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/nodetool/stats/StatsTableComparatorTest.java
@@ -0,0 +1,311 @@
+/*
+ * 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.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+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
+ */
+ private String buildSortOrderString(List<StatsTable> sorted) {
+ if (sorted == null)
+ return null;
+ if (sorted.size() == 0)
+ return "";
+ String names = sorted.get(0).tableName;
+ for (int i = 1; i < sorted.size(); i++)
+ names += " > " + sorted.get(i).tableName;
+ return names;
+ }
+
+ /**
+ * Reusable runner to test whether StatsTableComparator achieves the expected order for the given parameters.
+ */
+ private void runCompareTest(List<StatsTable> vector, String sortKey, String expectedOrder,
+ boolean humanReadable, boolean ascending)
+ {
+ Collections.sort(vector, 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
+ {
+ boolean humanReadable = false;
+ boolean ascending = false;
+ // average live cells: 1 > 6 > 2 > 5 > 3 > 4
+ runCompareTest(testTables,
+ "average_live_cells_per_slice_last_five_minutes",
+ "table1 > table6 > table2 > table5 > table3 > table4",
+ humanReadable,
+ ascending);
+ // average tombstones: 6 > 1 > 5 > 2 > 3 > 4
+ runCompareTest(testTables,
+ "average_tombstones_per_slice_last_five_minutes",
+ "table6 > table1 > table5 > table2 > table3 > table4",
+ humanReadable,
+ ascending);
+ // read latency: 3 > 2 > 1 > 6 > 4 > 5
+ runCompareTest(testTables,
+ "read_latency",
+ "table3 > table2 > table1 > table6 > table4 > table5",
+ humanReadable,
+ ascending);
+ // write latency: 4 > 5 > 6 > 1 > 2 > 3
+ runCompareTest(testTables,
+ "write_latency",
+ "table4 > table5 > table6 > table1 > table2 > table3",
+ humanReadable,
+ ascending);
+ // percent repaired
+ runCompareTest(testTables,
+ "percent_repaired",
+ "table1 > table2 > table3 > table5 > table4 > table6",
+ humanReadable,
+ ascending);
+ }
+
+ @Test
+ public void testCompareLongs() throws Exception
+ {
+ boolean humanReadable = false;
+ boolean ascending = false;
+ // reads: 6 > 5 > 4 > 3 > 2 > 1
+ runCompareTest(testTables,
+ "reads",
+ "table6 > table5 > table4 > table3 > table2 > table1",
+ humanReadable,
+ ascending);
+ // writes: 1 > 2 > 3 > 4 > 5 > 6
+ runCompareTest(testTables,
+ "writes",
+ "table1 > table2 > table3 > table4 > table5 > table6",
+ humanReadable,
+ ascending);
+ // compacted partition maximum bytes: 1 > 3 > 5 > 2 > 4 = 6
+ runCompareTest(testTables,
+ "compacted_partition_maximum_bytes",
+ "table1 > table3 > table5 > table2 > table4 > table6",
+ humanReadable,
+ ascending);
+ // compacted partition mean bytes: 1 > 3 > 2 = 4 = 5 > 6
+ runCompareTest(testTables,
+ "compacted_partition_mean_bytes",
+ "table1 > table3 > table2 > table4 > table5 > table6",
+ humanReadable,
+ ascending);
+ // compacted partition minimum bytes: 6 > 4 > 2 > 5 > 1 = 3
+ runCompareTest(testTables,
+ "compacted_partition_minimum_bytes",
+ "table6 > table4 > table2 > table5 > table1 > table3",
+ humanReadable,
+ ascending);
+ // maximum live cells last five minutes: 1 > 2 = 3 > 4 = 5 > 6
+ runCompareTest(testTables,
+ "maximum_live_cells_per_slice_last_five_minutes",
+ "table1 > table2 > table3 > table4 > table5 > table6",
+ humanReadable,
+ ascending);
+ // maximum tombstones last five minutes: 6 > 5 > 3 = 4 > 2 > 1
+ runCompareTest(testTables,
+ "maximum_tombstones_per_slice_last_five_minutes",
+ "table6 > table5 > table3 > table4 > table2 > table1",
+ humanReadable,
+ ascending);
+ }
+
+ @Test
+ public void testCompareHumanReadable() throws Exception
+ {
+ boolean humanReadable = true;
+ boolean ascending = false;
+ // human readable space used total: 6 > 5 > 4 > 3 > 2 > 1
+ runCompareTest(humanReadableTables,
+ "space_used_total",
+ "table6 > table5 > table4 > table3 > table2 > table1",
+ humanReadable,
+ ascending);
+ // human readable memtable data size: 1 > 3 > 5 > 2 > 4 > 6
+ runCompareTest(humanReadableTables,
+ "memtable_data_size",
+ "table1 > table3 > table5 > table2 > table4 > table6",
+ humanReadable,
+ ascending);
+ }
+
+ @Test
+ public void testCompareObjects() throws Exception
+ {
+ boolean humanReadable = false;
+ boolean ascending = false;
+ // bloom filter false positives: 2 > 4 > 6 > 1 > 3 > 5
+ runCompareTest(testTables,
+ "bloom_filter_false_positives",
+ "table2 > table4 > table6 > table1 > table3 > table5",
+ humanReadable,
+ ascending);
+ // bloom filter false positive ratio: 5 > 3 > 1 > 6 > 4 > 2
+ runCompareTest(testTables,
+ "bloom_filter_false_ratio",
+ "table5 > table3 > table1 > table6 > table4 > table2",
+ humanReadable,
+ ascending);
+ // memtable cell count: 3 > 5 > 6 > 1 > 2 > 4
+ runCompareTest(testTables,
+ "memtable_cell_count",
+ "table3 > table5 > table6 > table1 > table2 > table4",
+ humanReadable,
+ ascending);
+ // memtable switch count: 4 > 2 > 3 > 6 > 5 > 1
+ runCompareTest(testTables,
+ "memtable_switch_count",
+ "table4 > table2 > table3 > table6 > table5 > table1",
+ humanReadable,
+ ascending);
+ // number of partitions estimate: 1 > 2 > 3 > 4 > 5 > 6
+ runCompareTest(testTables,
+ "number_of_partitions_estimate",
+ "table1 > table2 > table3 > table4 > table5 > table6",
+ humanReadable,
+ ascending);
+ // pending flushes: 2 > 1 > 4 > 3 > 6 > 5
+ runCompareTest(testTables,
+ "pending_flushes",
+ "table2 > table1 > table4 > table3 > table6 > table5",
+ humanReadable,
+ ascending);
+ // sstable compression ratio: 5 > 4 > 1 = 2 = 6 > 3
+ runCompareTest(testTables,
+ "sstable_compression_ratio",
+ "table5 > table4 > table1 > table2 > table6 > table3",
+ humanReadable,
+ ascending);
+ // sstable count: 1 > 3 > 5 > 2 > 4 > 6
+ runCompareTest(testTables,
+ "sstable_count",
+ "table1 > table3 > table5 > table2 > table4 > table6",
+ humanReadable,
+ ascending);
+ }
+
+ @Test
+ public void testCompareOffHeap() throws Exception
+ {
+ boolean humanReadable = false;
+ boolean ascending = false;
+ // offheap memory total: 4 > 2 > 6 > 1 = 3 = 5
+ runCompareTest(testTables,
+ "off_heap_memory_used_total",
+ "table4 > table2 > table6 > table1 > table3 > table5",
+ humanReadable,
+ ascending);
+ // bloom filter offheap: 4 > 6 > 2 > 1 > 3 > 5
+ runCompareTest(testTables,
+ "bloom_filter_off_heap_memory_used",
+ "table4 > table6 > table2 > table1 > table3 > table5",
+ humanReadable,
+ ascending);
+ // compression metadata offheap: 2 > 4 > 6 > 1 = 3 = 5
+ runCompareTest(testTables,
+ "compression_metadata_off_heap_memory_used",
+ "table2 > table4 > table6 > table1 > table3 > table5",
+ humanReadable,
+ ascending);
+ // index summary offheap: 6 > 4 > 2 > 1 = 3 = 5
+ runCompareTest(testTables,
+ "index_summary_off_heap_memory_used",
+ "table6 > table4 > table2 > table1 > table3 > table5",
+ humanReadable,
+ ascending);
+ // memtable offheap: 2 > 6 > 4 > 1 = 3 = 5
+ runCompareTest(testTables,
+ "memtable_off_heap_memory_used",
+ "table2 > table6 > table4 > table1 > table3 > table5",
+ humanReadable,
+ ascending);
+ }
+
+ @Test
+ public void testCompareStrings() throws Exception
+ {
+ boolean humanReadable = false;
+ boolean ascending = false;
+ // full name (keyspace.table) ascending: 1 > 2 > 3 > 4 > 5 > 6
+ runCompareTest(testTables,
+ "full_name",
+ "table1 > table2 > table3 > table4 > table5 > table6",
+ humanReadable,
+ true);
+ // table name ascending: 6 > 5 > 4 > 3 > 2 > 1
+ runCompareTest(testTables,
+ "table_name",
+ "table1 > table2 > table3 > table4 > table5 > table6",
+ humanReadable,
+ true);
+ // bloom filter space used: 2 > 4 > 6 > 1 > 3 > 5
+ runCompareTest(testTables,
+ "bloom_filter_space_used",
+ "table2 > table4 > table6 > table1 > table3 > table5",
+ humanReadable,
+ ascending);
+ // dropped mutations: 6 > 3 > 4 > 2 > 1 = 5
+ runCompareTest(testTables,
+ "dropped_mutations",
+ "table6 > table3 > table4 > table2 > table1 > table5",
+ humanReadable,
+ ascending);
+ // space used by snapshots: 5 > 1 > 2 > 4 > 3 = 6
+ runCompareTest(testTables,
+ "space_used_by_snapshots_total",
+ "table5 > table1 > table2 > table4 > table3 > table6",
+ humanReadable,
+ ascending);
+ // space used live: 6 > 5 > 4 > 2 > 1 = 3
+ runCompareTest(testTables,
+ "space_used_live",
+ "table6 > table5 > table4 > table2 > table1 > table3",
+ humanReadable,
+ ascending);
+ // space used total: 1 > 2 > 3 > 4 > 5 > 6
+ runCompareTest(testTables,
+ "space_used_total",
+ "table1 > table2 > table3 > table4 > table5 > table6",
+ humanReadable,
+ ascending);
+ // memtable data size: 6 > 5 > 4 > 3 > 2 > 1
+ runCompareTest(testTables,
+ "memtable_data_size",
+ "table6 > table5 > table4 > table3 > table2 > table1",
+ humanReadable,
+ ascending);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/c90b0d62/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
new file mode 100644
index 0000000..26b2ff6
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
@@ -0,0 +1,366 @@
+/*
+ * 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.Collections;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TableStatsPrinterTest extends TableStatsTestBase
+{
+
+ public static final String expectedDefaultTable1Output =
+ "\tTable: %s\n" +
+ "\tSSTable count: 60000\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" +
+ "\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" +
+ "\n";
+
+ public static final String expectedDefaultTable2Output =
+ "\tTable: %s\n" +
+ "\tSSTable count: 3000\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" +
+ "\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" +
+ "\n";
+
+ public static final String expectedDefaultTable3Output =
+ "\tTable: %s\n" +
+ "\tSSTable count: 50000\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" +
+ "\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" +
+ "\n";
+
+ public static final String expectedDefaultTable4Output =
+ "\tTable: %s\n" +
+ "\tSSTable count: 2000\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" +
+ "\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" +
+ "\n";
+
+ public static final String expectedDefaultTable5Output =
+ "\tTable: %s\n" +
+ "\tSSTable count: 40000\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" +
+ "\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" +
+ "\n";
+
+ public static final String expectedDefaultTable6Output =
+ "\tTable: %s\n" +
+ "\tSSTable count: 1000\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" +
+ "\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" +
+ "\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(expectedDefaultTable1Output, "table1").replace("\t", "\t\t") +
+ String.format(expectedDefaultTable2Output, "table2").replace("\t", "\t\t") +
+ String.format(expectedDefaultTable3Output, "table3").replace("\t", "\t\t") +
+ "----------------\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(expectedDefaultTable4Output, "table4").replace("\t", "\t\t") +
+ String.format(expectedDefaultTable5Output, "table5").replace("\t", "\t\t") +
+ "----------------\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(expectedDefaultTable6Output, "table6").replace("\t", "\t\t") +
+ "----------------\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";
+
+ @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());
+ }
+
+ @Test
+ public void testSortedDefaultPrinter() throws Exception
+ {
+ // 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());
+ }
+
+ /**
+ * 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);
+ this.keyspaces.clear();
+ this.keyspaces.addAll(testKeyspaces);
+ }
+
+ @Override
+ protected boolean isTestTableStatsHolder()
+ {
+ return true;
+ }
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org