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

ycai pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new d6f1810  Add an option to nodetool tablestats to check sstable 
location correctness
d6f1810 is described below

commit d6f18106f09e7b1a58744c9bd1ee35054a43d68a
Author: Yifan Cai <[email protected]>
AuthorDate: Thu Feb 11 21:53:47 2021 -0800

    Add an option to nodetool tablestats to check sstable location correctness
    
    patch by Yifan Cai; reviewed by Ekaterina Dimitrova, Marcus Eriksson for 
CASSANDRA-16344
---
 CHANGES.txt                                        |   1 +
 .../org/apache/cassandra/db/ColumnFamilyStore.java |  21 +++
 .../cassandra/db/ColumnFamilyStoreMBean.java       |   7 +
 .../org/apache/cassandra/db/DiskBoundaries.java    |   7 +
 .../cassandra/db/compaction/CompactionManager.java |  12 +-
 .../cassandra/tools/nodetool/TableStats.java       |   7 +-
 .../cassandra/tools/nodetool/stats/StatsTable.java |   1 +
 .../tools/nodetool/stats/TableStatsHolder.java     |  11 +-
 .../tools/nodetool/stats/TableStatsPrinter.java    |   2 +
 .../test/MultipleDataDirectoryTest.java            | 172 +++++++++++++++++++++
 .../nodetool/stats/NodetoolTableStatsTest.java     |  58 +++++--
 .../nodetool/stats/TableStatsPrinterTest.java      |   2 +-
 12 files changed, 273 insertions(+), 28 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index 9f6ee1b..7b6a9f1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 4.0-beta5
+ * Add an option to nodetool tablestats to check sstable location correctness 
(CASSANDRA-16344) 
  * Unable to ALTER KEYSPACE while decommissioned/assassinated nodes are in 
gossip (CASSANDRA-16422)
  * Metrics backward compatibility restored after CASSANDRA-15066 
(CASSANDRA-16083)
  * Reduce new reserved keywords introduced since 3.0 (CASSANDRA-16439)
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java 
b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index 64ea6a6..1089e13 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -2880,4 +2880,25 @@ public class ColumnFamilyStore implements 
ColumnFamilyStoreMBean
                 Collections.addAll(collection, localSystemDiskFlushExecutors);
         }
     }
+
+    /*
+     * Check SSTables whether or not they are misplaced.
+     * @return true if any of the SSTables is misplaced.
+     *         If all SSTables are correctly placed or the partitioner does 
not support splitting, it returns false.
+     */
+    @Override
+    public boolean hasMisplacedSSTables()
+    {
+        if (!getPartitioner().splitter().isPresent())
+            return false;
+
+        final DiskBoundaries diskBoundaries = getDiskBoundaries();
+        for (SSTableReader sstable : getSSTables(SSTableSet.CANONICAL))
+        {
+            Directories.DataDirectory dataDirectory = 
getDirectories().getDataDirectoryForFile(sstable.descriptor);
+            if (!diskBoundaries.isInCorrectLocation(sstable, dataDirectory))
+                return true;
+        }
+        return false;
+    }
 }
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java 
b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
index fb0d611..c2cf393 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
@@ -218,4 +218,11 @@ public interface ColumnFamilyStoreMBean
     public void setNeverPurgeTombstones(boolean value);
 
     public boolean getNeverPurgeTombstones();
+
+    /**
+     * Check SSTables whether or not they are misplaced.
+     * @return true if any of the SSTables is misplaced.
+     *         If all SSTables are correctly placed or the partitioner does 
not support splitting, it returns false.
+     */
+    public boolean hasMisplacedSSTables();
 }
diff --git a/src/java/org/apache/cassandra/db/DiskBoundaries.java 
b/src/java/org/apache/cassandra/db/DiskBoundaries.java
index 5b377e2..f33b43e 100644
--- a/src/java/org/apache/cassandra/db/DiskBoundaries.java
+++ b/src/java/org/apache/cassandra/db/DiskBoundaries.java
@@ -141,6 +141,13 @@ public class DiskBoundaries
         return directories.get(getDiskIndex(key));
     }
 
+    public boolean isInCorrectLocation(SSTableReader sstable, 
Directories.DataDirectory currentLocation)
+    {
+        int diskIndex = getDiskIndex(sstable);
+        PartitionPosition diskLast = positions.get(diskIndex);
+        return directories.get(diskIndex).equals(currentLocation) && 
sstable.last.compareTo(diskLast) <= 0;
+    }
+
     private int getDiskIndex(DecoratedKey key)
     {
         int pos = Collections.binarySearch(positions, key);
diff --git a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java 
b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
index 7df56fc..acb21b2 100644
--- a/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
@@ -660,13 +660,9 @@ public class CompactionManager implements 
CompactionManagerMBean
                 if (!cfs.getPartitioner().splitter().isPresent())
                     return true;
 
-                int diskIndex = diskBoundaries.getDiskIndex(sstable);
-                PartitionPosition diskLast = 
diskBoundaries.positions.get(diskIndex);
-
-                // the location we get from directoryIndex is based on the 
first key in the sstable
-                // now we need to make sure the last key is less than the 
boundary as well:
-                Directories.DataDirectory dataDirectory = 
cfs.getDirectories().getDataDirectoryForFile(sstable.descriptor);
-                return 
diskBoundaries.directories.get(diskIndex).equals(dataDirectory) && 
sstable.last.compareTo(diskLast) <= 0;
+                // Compare the expected data directory for the sstable with 
its current data directory
+                Directories.DataDirectory currentDirectory = 
cfs.getDirectories().getDataDirectoryForFile(sstable.descriptor);
+                return diskBoundaries.isInCorrectLocation(sstable, 
currentDirectory);
             }
 
             @Override
@@ -1476,7 +1472,7 @@ public class CompactionManager implements 
CompactionManagerMBean
      * @param txn a transaction over the repaired sstables to anticompact
      * @param ranges full and transient ranges to be placed into one of the 
new sstables. The repaired table will be tracked via
      *   the {@link 
org.apache.cassandra.io.sstable.metadata.StatsMetadata#pendingRepair} field.
-     * @param sessionID the repair session we're anti-compacting for
+     * @param pendingRepair the repair session we're anti-compacting for
      * @param isCancelled function that indicates if active anti-compaction 
should be canceled
      */
     private void doAntiCompaction(ColumnFamilyStore cfs,
diff --git a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java 
b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
index 47ca132..4a33c64 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/TableStats.java
@@ -68,6 +68,11 @@ public class TableStats extends NodeToolCmd
             description = "Show only the top K tables for the sort key 
(specify the number K of tables to be shown")
     private int top = 0;
 
+    @Option(title = "sstable_location_check",
+            name = {"-l", "--sstable-location-check"},
+            description = "Check whether or not the SSTables are in the 
correct location.")
+    private boolean locationCheck = false;
+
     @Override
     public void execute(NodeProbe probe)
     {
@@ -92,7 +97,7 @@ public class TableStats extends NodeToolCmd
             throw new IllegalArgumentException("argument for top must be a 
positive integer.");
         }
 
-        StatsHolder holder = new TableStatsHolder(probe, humanReadable, 
ignore, tableNames, sortKey, top);
+        StatsHolder holder = new TableStatsHolder(probe, humanReadable, 
ignore, tableNames, sortKey, top, locationCheck);
         // print out the keyspace and table statistics
         StatsPrinter printer = TableStatsPrinter.from(outputFormat, 
!sortKey.isEmpty());
         printer.print(holder, probe.output().out);
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 01d2164..d897eab 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
@@ -69,4 +69,5 @@ public class StatsTable
     public long maximumTombstonesPerSliceLastFiveMinutes;
     public String droppedMutations;
     public List<String> sstablesInEachLevel = new ArrayList<>();
+    public Boolean isInCorrectLocation = null; // null: option not active
 }
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 624484f..8b9b722 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
@@ -35,13 +35,16 @@ public class TableStatsHolder implements StatsHolder
     public final boolean humanReadable;
     public final String sortKey;
     public final int top;
+    public final boolean locationCheck;
 
-    public TableStatsHolder(NodeProbe probe, boolean humanReadable, boolean 
ignore, List<String> tableNames, String sortKey, int top)
+    public TableStatsHolder(NodeProbe probe, boolean humanReadable, boolean 
ignore, List<String> tableNames, String sortKey, int top, boolean locationCheck)
     {
         this.keyspaces = new ArrayList<>();
         this.humanReadable = humanReadable;
         this.sortKey = sortKey;
         this.top = top;
+        this.locationCheck = locationCheck;
+
         if (!this.isTestTableStatsHolder())
         {
             this.numberOfTables = probe.getNumberOfTables();
@@ -155,6 +158,8 @@ public class TableStatsHolder implements StatsHolder
         mpTable.put("maximum_tombstones_per_slice_last_five_minutes",
                     table.maximumTombstonesPerSliceLastFiveMinutes);
         mpTable.put("dropped_mutations", table.droppedMutations);
+        if (locationCheck)
+            mpTable.put("sstables_in_correct_location", 
table.isInCorrectLocation);
         return mpTable;
     }
 
@@ -210,6 +215,7 @@ public class TableStatsHolder implements StatsHolder
                 statsTable.isIndex = tableName.contains(".");
                 statsTable.sstableCount = 
probe.getColumnFamilyMetric(keyspaceName, tableName, "LiveSSTableCount");
                 statsTable.oldSSTableCount = 
probe.getColumnFamilyMetric(keyspaceName, tableName, "OldVersionSSTableCount");
+
                 int[] leveledSStables = table.getSSTableCountPerLevel();
                 if (leveledSStables != null)
                 {
@@ -226,6 +232,9 @@ public class TableStatsHolder implements StatsHolder
                     }
                 }
 
+                if (locationCheck)
+                    statsTable.isInCorrectLocation = 
!table.hasMisplacedSSTables();
+
                 Long memtableOffHeapSize = null;
                 Long bloomFilterOffHeapSize = null;
                 Long indexSummaryOffHeapSize = null;
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 2291f45..8c27e1e 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
@@ -120,6 +120,8 @@ public class TableStatsPrinter<T extends StatsHolder>
             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);
+            if (table.isInCorrectLocation != null)
+                out.println(indent + "SSTables in correct location: " + 
table.isInCorrectLocation);
             out.println("");
         }
     }
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/MultipleDataDirectoryTest.java
 
b/test/distributed/org/apache/cassandra/distributed/test/MultipleDataDirectoryTest.java
new file mode 100644
index 0000000..a2f4aab
--- /dev/null
+++ 
b/test/distributed/org/apache/cassandra/distributed/test/MultipleDataDirectoryTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.distributed.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.Iterator;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.distributed.Cluster;
+import org.apache.cassandra.distributed.api.IInvokableInstance;
+import org.apache.cassandra.io.sstable.Component;
+import org.apache.cassandra.io.sstable.Descriptor;
+import org.apache.cassandra.io.sstable.format.SSTableReader;
+
+public class MultipleDataDirectoryTest extends TestBaseImpl
+{
+    private static Cluster CLUSTER;
+    private static IInvokableInstance NODE;
+
+    @BeforeClass
+    public static void before() throws IOException
+    {
+        CLUSTER = 
init(Cluster.build().withNodes(1).withDataDirCount(3).start());
+        NODE = CLUSTER.get(1);
+        CLUSTER.schemaChange(withKeyspace("CREATE TABLE %s.cf (k text, c1 
text, c2 text, PRIMARY KEY (k)) WITH compaction = {'class': 
'LeveledCompactionStrategy', 'enabled': 'false'}"));
+        Assert.assertEquals(3, NODE.callsOnInstance(() -> 
DatabaseDescriptor.getAllDataFileLocations().length).call().intValue());
+    }
+
+    @AfterClass
+    public static void after()
+    {
+        if (CLUSTER != null)
+            CLUSTER.close();
+    }
+
+    @Before
+    public void populateData()
+    {
+        final int rowsPerFile = 500;
+        final int files = 5;
+        for (int k = 0; k < files; k++)
+        {
+            for (int i = k * rowsPerFile; i < k * rowsPerFile + rowsPerFile; 
++i)
+                NODE.executeInternal(withKeyspace("INSERT INTO %s.cf (k, c1, 
c2) VALUES (?, 'value1', 'value2');"), Integer.toString(i));
+            NODE.nodetool("flush");
+        }
+    }
+
+    @After
+    public void cleanupData()
+    {
+        NODE.runOnInstance(() -> {
+            
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf").truncateBlocking();
+        });
+    }
+
+    @Test
+    public void testSSTablesAreInCorrectLocation()
+    {
+        NODE.runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            Assert.assertFalse("All SSTables should be in the correct 
location",
+                               cfs.hasMisplacedSSTables());
+        });
+    }
+
+    @Test
+    public void testDetectSSTableMisplaced()
+    {
+        setupMisplacedSSTables();
+        NODE.runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            Assert.assertTrue("Some SSTable should be misplaced",
+                               cfs.hasMisplacedSSTables());
+        });
+    }
+
+    @Test
+    public void testNodeToolRelocateSSTablesFindNoFilesToMove()
+    {
+        long logStartLoc = NODE.logs().mark();
+        NODE.nodetoolResult("relocatesstables", KEYSPACE, "cf")
+            .asserts()
+            .success();
+        String expectedLog = String.format("No sstables to RELOCATE for 
%s.%s", KEYSPACE, "cf");
+        Assert.assertEquals("relocatesstables should find no sstables to move",
+                            1, NODE.logs().grep(logStartLoc, 
expectedLog).getResult().size());
+    }
+
+    @Test
+    public void testNodeToolRelocateSSTables()
+    {
+        setupMisplacedSSTables();
+        long logStartLoc = NODE.logs().mark();
+        NODE.nodetoolResult("relocatesstables", KEYSPACE, "cf")
+            .asserts()
+            .success();
+        String expectedLog = String.format("Finished Relocate sstables to 
correct disk for %s.%s successfully", KEYSPACE, "cf");
+        Assert.assertEquals("relocatesstables should find sstables to move",
+                            1, NODE.logs().grep(logStartLoc, 
expectedLog).getResult().size());
+        NODE.runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            Assert.assertFalse("All SSTables should be in the correct 
location",
+                              cfs.hasMisplacedSSTables());
+        });
+    }
+
+    // by moving all sstables from the first data directory to the second.
+    private void setupMisplacedSSTables()
+    {
+        NODE.runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            Assert.assertNotEquals(0, cfs.getLiveSSTables().size());
+            Iterator<SSTableReader> sstables = 
cfs.getLiveSSTables().iterator();
+            // finding 2 descriptors that live in different data directory
+            Descriptor first = sstables.next().descriptor;
+            Descriptor second = null;
+            while (sstables.hasNext() && second == null) {
+                second = sstables.next().descriptor;
+                if (first.directory.equals(second.directory))
+                    second = null;
+            }
+            Assert.assertNotNull("There should be SSTables in multiple data 
directories", second);
+            // getting a new file index in order to move SSTable between 
directories.
+            second = cfs.newSSTableDescriptor(second.directory);
+            // now we just move all sstables from first to second
+            for (Component component : SSTableReader.componentsFor(first))
+            {
+                File file = new File(first.filenameFor(component));
+                if (file.exists())
+                {
+                    try
+                    {
+                        Files.copy(file.toPath(), new 
File(second.filenameFor(component)).toPath());
+                    }
+                    catch (IOException e)
+                    {
+                        throw new RuntimeException("Something wrong with 
copying sstables", e);
+                    }
+                }
+            }
+            ColumnFamilyStore.loadNewSSTables(KEYSPACE, "cf");
+        });
+    }
+}
diff --git 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java
 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java
index 528a6ca..a459f0f 100644
--- 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java
+++ 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/NodetoolTableStatsTest.java
@@ -62,16 +62,16 @@ public class NodetoolTableStatsTest extends CQLTester
                         "        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" + 
+                        "        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" +
+                        "                [(-l | --sstable-location-check)] 
[(-s <sort_key> | --sort <sort_key>)]\n" +
+                        "                [(-t <top> | --top <top>)] [--] 
[<keyspace.table>...]\n" +
                         "\n" + 
-                        "OPTIONS\n" + 
-                        "        -F <format>, --format <format>\n" + 
+                        "OPTIONS\n" +
+                        "        -F <format>, --format <format>\n" +
                         "            Output format (json, yaml)\n" + 
                         "\n" + 
                         "        -h <host>, --host <host>\n" + 
@@ -81,7 +81,10 @@ public class NodetoolTableStatsTest extends CQLTester
                         "            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" + 
+                        "            Ignore the list of tables and display the 
remaining tables\n" +
+                        "\n" +
+                        "        -l, --sstable-location-check\n" +
+                        "            Check whether or not the SSTables are in 
the correct location.\n" +
                         "\n" + 
                         "        -p <port>, --port <port>\n" + 
                         "            Remote jmx agent port number\n" + 
@@ -167,7 +170,7 @@ public class NodetoolTableStatsTest extends CQLTester
     {
         Arrays.asList("-H", "--human-readable").forEach(arg -> {
             ToolResult tool = ToolRunner.invokeNodetool("tablestats", arg);
-            assertThat("Arg: [" + arg + "]", tool.getStdout(), 
CoreMatchers.containsString(" KiB"));
+            assertThat(argFormat(arg), tool.getStdout(), 
CoreMatchers.containsString(" KiB"));
             assertTrue(String.format("Expected empty stderr for option [%s] 
but found: %s",
                                      arg,
                                      tool.getCleanedStderr()),
@@ -196,11 +199,11 @@ public class NodetoolTableStatsTest extends CQLTester
             while (m.find())
                 sorted.add(m.group(1));
 
-            assertNotEquals("Arg: [" + arg + "]", orig, sorted);
+            assertNotEquals(argFormat(arg), orig, sorted);
             Collections.sort(orig);
             Collections.sort(sorted);
-            assertEquals("Arg: [" + arg + "]", orig, sorted);
-            assertTrue("Arg: [" + arg + "]", 
tool.getCleanedStderr().isEmpty());
+            assertEquals(argFormat(arg), orig, sorted);
+            assertTrue(argFormat(arg), tool.getCleanedStderr().isEmpty());
             assertEquals(0, tool.getExitCode());
         });
 
@@ -215,9 +218,9 @@ public class NodetoolTableStatsTest extends CQLTester
     {
         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());
+            assertEquals(argFormat(arg), 
StringUtils.countMatches(tool.getStdout(), "Table:"), 1);
+            assertTrue(argFormat(arg), tool.getCleanedStderr().isEmpty());
+            assertEquals(argFormat(arg), 0, tool.getExitCode());
         });
 
         ToolResult tool = ToolRunner.invokeNodetool("tablestats", "-s", 
"table_name", "-t", "-1");
@@ -225,4 +228,25 @@ public class NodetoolTableStatsTest extends CQLTester
         tool.assertCleanStdErr();
         assertEquals(1, tool.getExitCode());
     }
+
+    @Test
+    public void testSSTableLocationCheckArg()
+    {
+        Arrays.asList("-l", "--sstable-location-check").forEach(arg -> {
+            ToolResult tool = ToolRunner.invokeNodetool("tablestats", arg, 
"system.local");
+            assertEquals(argFormat(arg), 
StringUtils.countMatches(tool.getStdout(), "SSTables in correct location: "), 
1);
+            assertTrue(argFormat(arg), tool.getCleanedStderr().isEmpty());
+            assertEquals(argFormat(arg), 0, tool.getExitCode());
+        });
+
+        ToolResult tool = ToolRunner.invokeNodetool("tablestats", 
"system.local");
+        assertThat(tool.getStdout(), 
CoreMatchers.not(CoreMatchers.containsString("SSTables in correct location: 
")));
+        tool.assertCleanStdErr();
+        assertEquals(0, tool.getExitCode());
+    }
+
+    private String argFormat(String arg)
+    {
+        return "Arg: [" + arg + ']';
+    }
 }
diff --git 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
index 92c29f5..cc2caa3 100644
--- 
a/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
+++ 
b/test/unit/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinterTest.java
@@ -365,7 +365,7 @@ public class TableStatsPrinterTest extends 
TableStatsTestBase
 
         public TestTableStatsHolder(List<StatsKeyspace> testKeyspaces, String 
sortKey, int top)
         {
-            super(null, false, false, new ArrayList<>(), sortKey, top);
+            super(null, false, false, new ArrayList<>(), sortKey, top, false);
             this.keyspaces.clear();
             this.keyspaces.addAll(testKeyspaces);
         }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to