Java tutorial
/* * 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; import java.util.*; import javax.management.InstanceNotFoundException; import com.google.common.collect.ArrayListMultimap; import io.airlift.command.Arguments; import io.airlift.command.Command; import io.airlift.command.Option; import org.apache.cassandra.db.ColumnFamilyStoreMBean; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.metrics.CassandraMetricsRegistry; import org.apache.cassandra.tools.NodeProbe; import org.apache.cassandra.tools.NodeTool.NodeToolCmd; import org.apache.cassandra.tools.nodetool.stats.StatsHolder; import org.apache.cassandra.tools.nodetool.stats.StatsKeyspace; import org.apache.cassandra.tools.nodetool.stats.StatsTable; import org.apache.cassandra.tools.nodetool.stats.TableStatsPrinter; @Command(name = "tablestats", description = "Print statistics on tables") public class TableStats extends NodeToolCmd { @Arguments(usage = "[<keyspace.table>...]", description = "List of tables (or keyspace) names") private List<String> tableNames = new ArrayList<>(); @Option(name = "-i", description = "Ignore the list of tables and display the remaining tables") private boolean ignore = false; @Option(title = "human_readable", name = { "-H", "--human-readable" }, description = "Display bytes in human readable form, i.e. KiB, MiB, GiB, TiB") private boolean humanReadable = false; @Option(title = "format", name = { "-F", "--format" }, description = "Output format (json, yaml)") private String outputFormat = ""; @Override public void execute(NodeProbe probe) { if (!outputFormat.isEmpty() && !"json".equals(outputFormat) && !"yaml".equals(outputFormat)) { throw new IllegalArgumentException("arguments for -F are json,yaml only."); } TableStats.OptionFilter filter = new OptionFilter(ignore, tableNames); ArrayListMultimap<String, ColumnFamilyStoreMBean> selectedTableMbeans = ArrayListMultimap.create(); Map<String, StatsKeyspace> keyspaceStats = new HashMap<>(); // get a list of table stores Iterator<Map.Entry<String, ColumnFamilyStoreMBean>> tableMBeans = probe.getColumnFamilyStoreMBeanProxies(); while (tableMBeans.hasNext()) { Map.Entry<String, ColumnFamilyStoreMBean> entry = tableMBeans.next(); String keyspaceName = entry.getKey(); ColumnFamilyStoreMBean tableProxy = entry.getValue(); if (filter.isKeyspaceIncluded(keyspaceName)) { StatsKeyspace stats = keyspaceStats.get(keyspaceName); if (stats == null) { stats = new StatsKeyspace(probe, keyspaceName); keyspaceStats.put(keyspaceName, stats); } stats.add(tableProxy); if (filter.isTableIncluded(keyspaceName, tableProxy.getTableName())) selectedTableMbeans.put(keyspaceName, tableProxy); } } // make sure all specified keyspace and tables exist filter.verifyKeyspaces(probe.getKeyspaces()); filter.verifyTables(); // get metrics of keyspace StatsHolder holder = new StatsHolder(); for (Map.Entry<String, Collection<ColumnFamilyStoreMBean>> entry : selectedTableMbeans.asMap().entrySet()) { String keyspaceName = entry.getKey(); Collection<ColumnFamilyStoreMBean> tables = entry.getValue(); StatsKeyspace statsKeyspace = keyspaceStats.get(keyspaceName); // get metrics of table statistics for this keyspace for (ColumnFamilyStoreMBean table : tables) { String tableName = table.getTableName(); StatsTable statsTable = new StatsTable(); statsTable.name = tableName; statsTable.isIndex = tableName.contains("."); statsTable.sstableCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "LiveSSTableCount"); int[] leveledSStables = table.getSSTableCountPerLevel(); if (leveledSStables != null) { statsTable.isLeveledSstable = true; for (int level = 0; level < leveledSStables.length; level++) { int count = leveledSStables[level]; long maxCount = 4L; // for L0 if (level > 0) maxCount = (long) Math.pow(10, level); // show max threshold for level when exceeded statsTable.sstablesInEachLevel.add(count + ((count > maxCount) ? "/" + maxCount : "")); } } Long memtableOffHeapSize = null; Long bloomFilterOffHeapSize = null; Long indexSummaryOffHeapSize = null; Long compressionMetadataOffHeapSize = null; Long offHeapSize = null; Double percentRepaired = null; try { memtableOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "MemtableOffHeapSize"); bloomFilterOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "BloomFilterOffHeapMemoryUsed"); indexSummaryOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "IndexSummaryOffHeapMemoryUsed"); compressionMetadataOffHeapSize = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "CompressionMetadataOffHeapMemoryUsed"); offHeapSize = memtableOffHeapSize + bloomFilterOffHeapSize + indexSummaryOffHeapSize + compressionMetadataOffHeapSize; percentRepaired = (Double) probe.getColumnFamilyMetric(keyspaceName, tableName, "PercentRepaired"); } catch (RuntimeException e) { // offheap-metrics introduced in 2.1.3 - older versions do not have the appropriate mbeans if (!(e.getCause() instanceof InstanceNotFoundException)) throw e; } statsTable.spaceUsedLive = format( (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "LiveDiskSpaceUsed"), humanReadable); statsTable.spaceUsedTotal = format( (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "TotalDiskSpaceUsed"), humanReadable); statsTable.spaceUsedBySnapshotsTotal = format( (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "SnapshotsSize"), humanReadable); if (offHeapSize != null) { statsTable.offHeapUsed = true; statsTable.offHeapMemoryUsedTotal = format(offHeapSize, humanReadable); } if (percentRepaired != null) { statsTable.percentRepaired = Math.round(100 * percentRepaired) / 100.0; } statsTable.sstableCompressionRatio = probe.getColumnFamilyMetric(keyspaceName, tableName, "CompressionRatio"); Object estimatedPartitionCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "EstimatedPartitionCount"); if (Long.valueOf(-1L).equals(estimatedPartitionCount)) { estimatedPartitionCount = 0L; } statsTable.numberOfKeysEstimate = estimatedPartitionCount; statsTable.memtableCellCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "MemtableColumnsCount"); statsTable.memtableDataSize = format( (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "MemtableLiveDataSize"), humanReadable); if (memtableOffHeapSize != null) { statsTable.memtableOffHeapUsed = true; statsTable.memtableOffHeapMemoryUsed = format(memtableOffHeapSize, humanReadable); } statsTable.memtableSwitchCount = probe.getColumnFamilyMetric(keyspaceName, tableName, "MemtableSwitchCount"); statsTable.localReadCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe .getColumnFamilyMetric(keyspaceName, tableName, "ReadLatency")).getCount(); double localReadLatency = ((CassandraMetricsRegistry.JmxTimerMBean) probe .getColumnFamilyMetric(keyspaceName, tableName, "ReadLatency")).getMean() / 1000; double localRLatency = localReadLatency > 0 ? localReadLatency : Double.NaN; statsTable.localReadLatencyMs = localRLatency; statsTable.localWriteCount = ((CassandraMetricsRegistry.JmxTimerMBean) probe .getColumnFamilyMetric(keyspaceName, tableName, "WriteLatency")).getCount(); double localWriteLatency = ((CassandraMetricsRegistry.JmxTimerMBean) probe .getColumnFamilyMetric(keyspaceName, tableName, "WriteLatency")).getMean() / 1000; double localWLatency = localWriteLatency > 0 ? localWriteLatency : Double.NaN; statsTable.localWriteLatencyMs = localWLatency; statsTable.pendingFlushes = probe.getColumnFamilyMetric(keyspaceName, tableName, "PendingFlushes"); statsTable.bloomFilterFalsePositives = probe.getColumnFamilyMetric(keyspaceName, tableName, "BloomFilterFalsePositives"); statsTable.bloomFilterFalseRatio = probe.getColumnFamilyMetric(keyspaceName, tableName, "RecentBloomFilterFalseRatio"); statsTable.bloomFilterSpaceUsed = format( (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "BloomFilterDiskSpaceUsed"), humanReadable); if (bloomFilterOffHeapSize != null) { statsTable.bloomFilterOffHeapUsed = true; statsTable.bloomFilterOffHeapMemoryUsed = format(bloomFilterOffHeapSize, humanReadable); } if (indexSummaryOffHeapSize != null) { statsTable.indexSummaryOffHeapUsed = true; statsTable.indexSummaryOffHeapMemoryUsed = format(indexSummaryOffHeapSize, humanReadable); } if (compressionMetadataOffHeapSize != null) { statsTable.compressionMetadataOffHeapUsed = true; statsTable.compressionMetadataOffHeapMemoryUsed = format(compressionMetadataOffHeapSize, humanReadable); } statsTable.compactedPartitionMinimumBytes = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "MinPartitionSize"); statsTable.compactedPartitionMaximumBytes = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "MaxPartitionSize"); statsTable.compactedPartitionMeanBytes = (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "MeanPartitionSize"); CassandraMetricsRegistry.JmxHistogramMBean histogram = (CassandraMetricsRegistry.JmxHistogramMBean) probe .getColumnFamilyMetric(keyspaceName, tableName, "LiveScannedHistogram"); statsTable.averageLiveCellsPerSliceLastFiveMinutes = histogram.getMean(); statsTable.maximumLiveCellsPerSliceLastFiveMinutes = histogram.getMax(); histogram = (CassandraMetricsRegistry.JmxHistogramMBean) probe.getColumnFamilyMetric(keyspaceName, tableName, "TombstoneScannedHistogram"); statsTable.averageTombstonesPerSliceLastFiveMinutes = histogram.getMean(); statsTable.maximumTombstonesPerSliceLastFiveMinutes = histogram.getMax(); statsTable.droppedMutations = format( (Long) probe.getColumnFamilyMetric(keyspaceName, tableName, "DroppedMutations"), humanReadable); statsKeyspace.tables.add(statsTable); } holder.keyspaces.add(statsKeyspace); } // print out the keyspace and table statistics TableStatsPrinter printer = TableStatsPrinter.from(outputFormat); printer.print(holder, System.out); } private String format(long bytes, boolean humanReadable) { return humanReadable ? FileUtils.stringifyFileSize(bytes) : Long.toString(bytes); } /** * Used for filtering keyspaces and tables to be displayed using the tablestats command. */ private static class OptionFilter { private final Map<String, List<String>> filter = new HashMap<>(); private final Map<String, List<String>> verifier = new HashMap<>(); // Same as filter initially, but we remove tables every time we've checked them for inclusion // in isTableIncluded() so that we detect if those table requested don't exist (verifyTables()) private final List<String> filterList = new ArrayList<>(); private final boolean ignoreMode; OptionFilter(boolean ignoreMode, List<String> filterList) { this.filterList.addAll(filterList); this.ignoreMode = ignoreMode; for (String s : filterList) { String[] keyValues = s.split("\\.", 2); // build the map that stores the keyspaces and tables to use if (!filter.containsKey(keyValues[0])) { filter.put(keyValues[0], new ArrayList<>()); verifier.put(keyValues[0], new ArrayList<>()); } if (keyValues.length == 2) { filter.get(keyValues[0]).add(keyValues[1]); verifier.get(keyValues[0]).add(keyValues[1]); } } } public boolean isTableIncluded(String keyspace, String table) { // supplying empty params list is treated as wanting to display all keyspaces and tables if (filterList.isEmpty()) return !ignoreMode; List<String> tables = filter.get(keyspace); // no such keyspace is in the map if (tables == null) return ignoreMode; // only a keyspace with no tables was supplied // so ignore or include (based on the flag) every column family in specified keyspace else if (tables.isEmpty()) return !ignoreMode; // keyspace exists, and it contains specific table verifier.get(keyspace).remove(table); return ignoreMode ^ tables.contains(table); } public boolean isKeyspaceIncluded(String keyspace) { // supplying empty params list is treated as wanting to display all keyspaces and tables if (filterList.isEmpty()) return !ignoreMode; // Note that if there is any table for the keyspace, we want to include the keyspace irregarding // of the ignoreMode, since the ignoreMode then apply to the table inside the keyspace but the // keyspace itself is not ignored return filter.get(keyspace) != null || ignoreMode; } public void verifyKeyspaces(List<String> keyspaces) { for (String ks : verifier.keySet()) if (!keyspaces.contains(ks)) throw new IllegalArgumentException("Unknown keyspace: " + ks); } public void verifyTables() { for (String ks : filter.keySet()) if (!verifier.get(ks).isEmpty()) throw new IllegalArgumentException( "Unknown tables: " + verifier.get(ks) + " in keyspace: " + ks); } } }