From a04ccf3297839febed68c314704db4d920b64413 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Tue, 17 Nov 2020 14:01:28 -0600 Subject: [PATCH] sstablescrub unit test hardening and docs Patch by Berenguer Blasi, reviewed by brandonwilliams for CASSANDRA-16013 --- doc/source/tools/sstable/sstablescrub.rst | 7 + .../cassandra/tools/StandaloneScrubber.java | 40 ++-- src/java/org/apache/cassandra/tools/Util.java | 2 +- .../cql3/validation/operations/TTLTest.java | 105 ++++++++--- .../org/apache/cassandra/db/ScrubTest.java | 176 +++++++++++++++++- .../tools/StandaloneScrubberTest.java | 6 + 6 files changed, 286 insertions(+), 50 deletions(-) diff --git a/doc/source/tools/sstable/sstablescrub.rst b/doc/source/tools/sstable/sstablescrub.rst index 0bbda9f32f..a8529e32c9 100644 --- a/doc/source/tools/sstable/sstablescrub.rst +++ b/doc/source/tools/sstable/sstablescrub.rst @@ -29,6 +29,13 @@ sstablescrub =================================== ================================================================================ --debug display stack traces +-e,--header-fix Option whether and how to perform a check of the sstable serialization-headers and fix , fixable issues. + Possible argument values: + - validate-only: validate the serialization-headers, but do not fix those. Do not continue with scrub - i.e. only validate the header (dry-run of fix-only). + - validate: (default) validate the serialization-headers, but do not fix those and only continue with scrub if no error were detected. + - fix-only: validate and fix the serialization-headers, don't continue with scrub. + - fix: validate and fix the serialization-headers, do not fix and do not continue with scrub if the serialization-header check encountered errors. + - off: don't perform the serialization-header checks. -h,--help display this help message -m,--manifest-check only check and repair the leveled manifest, without actually scrubbing the sstables -n,--no-validate do not validate columns using column validator diff --git a/src/java/org/apache/cassandra/tools/StandaloneScrubber.java b/src/java/org/apache/cassandra/tools/StandaloneScrubber.java index e2fedadfa0..bd71c64f5a 100644 --- a/src/java/org/apache/cassandra/tools/StandaloneScrubber.java +++ b/src/java/org/apache/cassandra/tools/StandaloneScrubber.java @@ -20,29 +20,41 @@ package org.apache.cassandra.tools; import java.io.File; import java.nio.file.Paths; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeUnit; -import com.google.common.base.Predicate; -import com.google.common.base.Predicates; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; -import org.apache.commons.cli.*; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.CommandLineParser; +import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.ParseException; -import org.apache.cassandra.schema.Schema; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.ColumnFamilyStore; import org.apache.cassandra.db.Directories; import org.apache.cassandra.db.Keyspace; -import org.apache.cassandra.db.compaction.*; +import org.apache.cassandra.db.compaction.AbstractStrategyHolder; +import org.apache.cassandra.db.compaction.CompactionManager; +import org.apache.cassandra.db.compaction.CompactionStrategyManager; +import org.apache.cassandra.db.compaction.LeveledCompactionStrategy; +import org.apache.cassandra.db.compaction.LeveledManifest; +import org.apache.cassandra.db.compaction.OperationType; +import org.apache.cassandra.db.compaction.Scrubber; import org.apache.cassandra.db.lifecycle.LifecycleTransaction; +import org.apache.cassandra.io.sstable.Component; +import org.apache.cassandra.io.sstable.Descriptor; +import org.apache.cassandra.io.sstable.SSTableHeaderFix; import org.apache.cassandra.io.sstable.format.SSTableReader; -import org.apache.cassandra.io.sstable.*; +import org.apache.cassandra.schema.Schema; +import org.apache.cassandra.tools.BulkLoader.CmdLineOptions; import org.apache.cassandra.utils.JVMStabilityInspector; import org.apache.cassandra.utils.OutputHandler; import org.apache.cassandra.utils.Pair; -import static org.apache.cassandra.tools.BulkLoader.CmdLineOptions; - public class StandaloneScrubber { public static final String REINSERT_OVERFLOWED_TTL_OPTION_DESCRIPTION = "Rewrites rows with overflowed expiration date affected by CASSANDRA-14092 with " + @@ -64,7 +76,11 @@ public class StandaloneScrubber public static void main(String args[]) { Options options = Options.parseArgs(args); - Util.initDatabaseDescriptor(); + + if (Boolean.getBoolean(Util.ALLOW_TOOL_REINIT_FOR_TEST)) + DatabaseDescriptor.toolInitialization(false); //Necessary for testing + else + Util.initDatabaseDescriptor(); try { diff --git a/src/java/org/apache/cassandra/tools/Util.java b/src/java/org/apache/cassandra/tools/Util.java index 7411858a64..3757754f35 100644 --- a/src/java/org/apache/cassandra/tools/Util.java +++ b/src/java/org/apache/cassandra/tools/Util.java @@ -54,7 +54,7 @@ import com.google.common.collect.Lists; @SuppressWarnings("serial") public final class Util { - static final String ALLOW_TOOL_REINIT_FOR_TEST = Util.class.getName() + "ALLOW_TOOL_REINIT_FOR_TEST"; // Necessary for testing + public static final String ALLOW_TOOL_REINIT_FOR_TEST = Util.class.getName() + "ALLOW_TOOL_REINIT_FOR_TEST"; // Necessary for testing static final String RESET = "\u001B[0m"; static final String BLUE = "\u001B[34m"; static final String CYAN = "\u001B[36m"; diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/TTLTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/TTLTest.java index 85503d05f6..6f73e83b20 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/TTLTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/TTLTest.java @@ -23,10 +23,12 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.apache.cassandra.OrderedJUnit4ClassRunner; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.Attributes; @@ -37,12 +39,17 @@ import org.apache.cassandra.db.ExpirationDateOverflowHandling; import org.apache.cassandra.db.Keyspace; import org.apache.cassandra.db.rows.AbstractCell; import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.tools.StandaloneScrubber; +import org.apache.cassandra.tools.ToolRunner; +import org.apache.cassandra.tools.ToolRunner.ToolResult; import org.apache.cassandra.utils.FBUtilities; +import org.assertj.core.api.Assertions; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +@RunWith(OrderedJUnit4ClassRunner.class) public class TTLTest extends CQLTester { public static String NEGATIVE_LOCAL_EXPIRATION_TEST_DIR = "test/data/negative-local-expiration-test/%s"; @@ -54,6 +61,7 @@ public class TTLTest extends CQLTester public static final String COMPLEX_NOCLUSTERING = "table3"; public static final String COMPLEX_CLUSTERING = "table4"; private Config.CorruptedTombstoneStrategy corruptTombstoneStrategy; + @Before public void before() { @@ -203,9 +211,20 @@ public class TTLTest extends CQLTester { // this tests writes corrupt tombstones on purpose, disable the strategy: DatabaseDescriptor.setCorruptedTombstoneStrategy(Config.CorruptedTombstoneStrategy.disabled); - baseTestRecoverOverflowedExpiration(false, false); - baseTestRecoverOverflowedExpiration(true, false); - baseTestRecoverOverflowedExpiration(true, true); + baseTestRecoverOverflowedExpiration(false, false, false); + baseTestRecoverOverflowedExpiration(true, false, false); + baseTestRecoverOverflowedExpiration(true, false, true); + // we reset the corrupted ts strategy after each test in @After above + } + + @Test + public void testRecoverOverflowedExpirationWithSSTableScrub() throws Throwable + { + // this tests writes corrupt tombstones on purpose, disable the strategy: + DatabaseDescriptor.setCorruptedTombstoneStrategy(Config.CorruptedTombstoneStrategy.disabled); + baseTestRecoverOverflowedExpiration(false, false, false); + baseTestRecoverOverflowedExpiration(false, true, false); + baseTestRecoverOverflowedExpiration(false, true, true); // we reset the corrupted ts strategy after each test in @After above } @@ -276,16 +295,16 @@ public class TTLTest extends CQLTester } } - public void baseTestRecoverOverflowedExpiration(boolean runScrub, boolean reinsertOverflowedTTL) throws Throwable + public void baseTestRecoverOverflowedExpiration(boolean runScrub, boolean runSStableScrub, boolean reinsertOverflowedTTL) throws Throwable { // simple column, clustering - testRecoverOverflowedExpirationWithScrub(true, true, runScrub, reinsertOverflowedTTL); + testRecoverOverflowedExpirationWithScrub(true, true, runScrub, runSStableScrub, reinsertOverflowedTTL); // simple column, noclustering - testRecoverOverflowedExpirationWithScrub(true, false, runScrub, reinsertOverflowedTTL); + testRecoverOverflowedExpirationWithScrub(true, false, runScrub, runSStableScrub, reinsertOverflowedTTL); // complex column, clustering - testRecoverOverflowedExpirationWithScrub(false, true, runScrub, reinsertOverflowedTTL); + testRecoverOverflowedExpirationWithScrub(false, true, runScrub, runSStableScrub, reinsertOverflowedTTL); // complex column, noclustering - testRecoverOverflowedExpirationWithScrub(false, false, runScrub, reinsertOverflowedTTL); + testRecoverOverflowedExpirationWithScrub(false, false, runScrub, runSStableScrub, reinsertOverflowedTTL); } private void createTable(boolean simple, boolean clustering) @@ -350,11 +369,11 @@ public class TTLTest extends CQLTester return AbstractCell.MAX_DELETION_TIME - nowInSecs; } - public void testRecoverOverflowedExpirationWithScrub(boolean simple, boolean clustering, boolean runScrub, boolean reinsertOverflowedTTL) throws Throwable + public void testRecoverOverflowedExpirationWithScrub(boolean simple, boolean clustering, boolean runScrub, boolean runSStableScrub, boolean reinsertOverflowedTTL) throws Throwable { if (reinsertOverflowedTTL) { - assert runScrub; + assert runScrub || runSStableScrub; } createTable(simple, clustering); @@ -371,25 +390,49 @@ public class TTLTest extends CQLTester if (runScrub) { cfs.scrub(true, false, true, reinsertOverflowedTTL, 1); - } - if (reinsertOverflowedTTL) - { - if (simple) - assertRows(execute("SELECT * from %s"), row(1, 1, 1), row(2, 2, null)); + if (reinsertOverflowedTTL) + { + if (simple) + assertRows(execute("SELECT * from %s"), row(1, 1, 1), row(2, 2, null)); + else + assertRows(execute("SELECT * from %s"), row(1, 1, set("v11", "v12", "v13", "v14")), row(2, 2, set("v21", "v22", "v23", "v24"))); + + cfs.forceMajorCompaction(); + + if (simple) + assertRows(execute("SELECT * from %s"), row(1, 1, 1), row(2, 2, null)); + else + assertRows(execute("SELECT * from %s"), row(1, 1, set("v11", "v12", "v13", "v14")), row(2, 2, set("v21", "v22", "v23", "v24"))); + } else - assertRows(execute("SELECT * from %s"), row(1, 1, set("v11", "v12", "v13", "v14")), row(2, 2, set("v21", "v22", "v23", "v24"))); - - cfs.forceMajorCompaction(); - - if (simple) - assertRows(execute("SELECT * from %s"), row(1, 1, 1), row(2, 2, null)); - else - assertRows(execute("SELECT * from %s"), row(1, 1, set("v11", "v12", "v13", "v14")), row(2, 2, set("v21", "v22", "v23", "v24"))); + { + assertEmpty(execute("SELECT * from %s")); + } } - else + if (runSStableScrub) { - assertEmpty(execute("SELECT * from %s")); + System.setProperty(org.apache.cassandra.tools.Util.ALLOW_TOOL_REINIT_FOR_TEST, "true"); // Necessary for testing + + try + { + ToolResult tool; + if (reinsertOverflowedTTL) + tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-r", KEYSPACE, cfs.name); + else + tool = ToolRunner.invokeClass(StandaloneScrubber.class, KEYSPACE, cfs.name); + + Assertions.assertThat(tool.getStdout()).contains("Pre-scrub sstables snapshotted into"); + if (reinsertOverflowedTTL) + Assertions.assertThat(tool.getStdout()).contains("Fixed 2 rows with overflowed local deletion time."); + else + Assertions.assertThat(tool.getStdout()).contains("Unable to recover 2 rows that were skipped."); + tool.assertOnCleanExit(); + } + finally + { + System.clearProperty(org.apache.cassandra.tools.Util.ALLOW_TOOL_REINIT_FOR_TEST); + } } } diff --git a/test/unit/org/apache/cassandra/db/ScrubTest.java b/test/unit/org/apache/cassandra/db/ScrubTest.java index 533b4909bd..6ecaded6c9 100644 --- a/test/unit/org/apache/cassandra/db/ScrubTest.java +++ b/test/unit/org/apache/cassandra/db/ScrubTest.java @@ -18,21 +18,34 @@ */ package org.apache.cassandra.db; -import java.io.*; +import java.io.File; +import java.io.IOError; +import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.ExecutionException; -import org.apache.cassandra.db.lifecycle.LifecycleNewTracker; import org.apache.commons.lang3.StringUtils; + +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; -import org.apache.cassandra.*; +import org.apache.cassandra.OrderedJUnit4ClassRunner; +import org.apache.cassandra.SchemaLoader; +import org.apache.cassandra.UpdateBuilder; +import org.apache.cassandra.Util; import org.apache.cassandra.cache.ChunkCache; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.cql3.Operator; @@ -41,18 +54,27 @@ import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.db.compaction.CompactionManager; import org.apache.cassandra.db.compaction.OperationType; import org.apache.cassandra.db.compaction.Scrubber; +import org.apache.cassandra.db.lifecycle.LifecycleNewTracker; import org.apache.cassandra.db.lifecycle.LifecycleTransaction; import org.apache.cassandra.db.marshal.LongType; import org.apache.cassandra.db.marshal.UUIDType; import org.apache.cassandra.db.partitions.Partition; import org.apache.cassandra.db.partitions.PartitionUpdate; import org.apache.cassandra.db.rows.EncodingStats; -import org.apache.cassandra.dht.*; +import org.apache.cassandra.dht.ByteOrderedPartitioner; +import org.apache.cassandra.dht.IPartitioner; +import org.apache.cassandra.dht.Murmur3Partitioner; import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.exceptions.RequestExecutionException; import org.apache.cassandra.exceptions.WriteTimeoutException; import org.apache.cassandra.io.compress.CompressionMetadata; -import org.apache.cassandra.io.sstable.*; +import org.apache.cassandra.io.sstable.Component; +import org.apache.cassandra.io.sstable.CorruptSSTableException; +import org.apache.cassandra.io.sstable.Descriptor; +import org.apache.cassandra.io.sstable.SSTableMultiWriter; +import org.apache.cassandra.io.sstable.SSTableRewriter; +import org.apache.cassandra.io.sstable.SSTableTxnWriter; +import org.apache.cassandra.io.sstable.SimpleSSTableMultiWriter; import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.io.sstable.format.SSTableWriter; import org.apache.cassandra.io.sstable.format.big.BigTableWriter; @@ -60,7 +82,11 @@ import org.apache.cassandra.io.sstable.metadata.MetadataCollector; import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.schema.KeyspaceParams; import org.apache.cassandra.schema.TableMetadataRef; +import org.apache.cassandra.tools.StandaloneScrubber; +import org.apache.cassandra.tools.ToolRunner; +import org.apache.cassandra.tools.ToolRunner.ToolResult; import org.apache.cassandra.utils.ByteBufferUtil; +import org.assertj.core.api.Assertions; import static org.apache.cassandra.SchemaLoader.counterCFMD; import static org.apache.cassandra.SchemaLoader.createKeyspace; @@ -79,9 +105,11 @@ public class ScrubTest public static final String INVALID_LEGACY_SSTABLE_ROOT_PROP = "invalid-legacy-sstable-root"; public static final String KEYSPACE = "Keyspace1"; + public static final String KEYSPACE2 = "Keyspace2"; public static final String CF = "Standard1"; public static final String CF2 = "Standard2"; public static final String CF3 = "Standard3"; + public static final String CF4 = "Standard4"; public static final String COUNTER_CF = "Counter1"; public static final String CF_UUID = "UUIDKeys"; public static final String CF_INDEX1 = "Indexed1"; @@ -110,6 +138,12 @@ public class ScrubTest SchemaLoader.compositeIndexCFMD(KEYSPACE, CF_INDEX2_BYTEORDERED, true).partitioner(ByteOrderedPartitioner.instance)); } + @AfterClass + public static void clearClassEnv() + { + System.clearProperty(org.apache.cassandra.tools.Util.ALLOW_TOOL_REINIT_FOR_TEST); + } + @Test public void testScrubOneRow() throws ExecutionException, InterruptedException { @@ -712,4 +746,134 @@ public class ScrubTest rs = QueryProcessor.executeInternal(String.format("SELECT * FROM \"%s\".cf_with_duplicates_3_0", KEYSPACE)); assertEquals(0, rs.size()); } + + @Test + public void testToolTestingEnvSetup() + { + createKeyspace(KEYSPACE2, + KeyspaceParams.simple(1), + standardCFMD(KEYSPACE2, CF), + standardCFMD(KEYSPACE2, CF2), + standardCFMD(KEYSPACE2, CF3), + standardCFMD(KEYSPACE2, CF4), + counterCFMD(KEYSPACE2, COUNTER_CF)); + + System.setProperty(org.apache.cassandra.tools.Util.ALLOW_TOOL_REINIT_FOR_TEST, "true"); // Necessary for testing + } + + @Test + public void testScrubOneRowWithTool() + { + CompactionManager.instance.disableAutoCompaction(); + Keyspace keyspace = Keyspace.open(KEYSPACE2); + ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF); + cfs.clearUnsafe(); + + // insert data and verify we get it back w/ range query + fillCF(cfs, 1); + assertOrderedAll(cfs, 1); + + ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, KEYSPACE2, CF); + Assertions.assertThat(tool.getStdout()).contains("Pre-scrub sstables snapshotted into"); + Assertions.assertThat(tool.getStdout()).contains("1 rows in new sstable and 0 empty"); + tool.assertOnCleanExit(); + + // check data is still there + assertOrderedAll(cfs, 1); + } + + @Test + public void testSkipScrubCorruptedCounterRowWithTool() throws IOException, WriteTimeoutException + { + int numPartitions = 1000; + + CompactionManager.instance.disableAutoCompaction(); + Keyspace keyspace = Keyspace.open(KEYSPACE2); + ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(COUNTER_CF); + cfs.clearUnsafe(); + + fillCounterCF(cfs, numPartitions); + assertOrderedAll(cfs, numPartitions); + assertEquals(1, cfs.getLiveSSTables().size()); + SSTableReader sstable = cfs.getLiveSSTables().iterator().next(); + + overrideWithGarbage(sstable, ByteBufferUtil.bytes("0"), ByteBufferUtil.bytes("1")); + + // with skipCorrupted == false, the scrub is expected to fail + try + { + ToolRunner.invokeClass(StandaloneScrubber.class, KEYSPACE2, COUNTER_CF); + fail("Expected a CorruptSSTableException to be thrown"); + } + catch (IOError err) {} + + // with skipCorrupted == true, the corrupt rows will be skipped + ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-s", KEYSPACE2, COUNTER_CF); + Assertions.assertThat(tool.getStdout()).contains("0 empty"); + Assertions.assertThat(tool.getStdout()).contains("rows that were skipped"); + tool.assertOnCleanExit(); + + assertEquals(1, cfs.getLiveSSTables().size()); + } + + @Test + public void testNoCheckScrubMultiRowWithTool() + { + CompactionManager.instance.disableAutoCompaction(); + Keyspace keyspace = Keyspace.open(KEYSPACE2); + ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF2); + cfs.clearUnsafe(); + + // insert data and verify we get it back w/ range query + fillCF(cfs, 10); + assertOrderedAll(cfs, 10); + + ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-n", KEYSPACE2, CF2); + Assertions.assertThat(tool.getStdout()).contains("Pre-scrub sstables snapshotted into"); + Assertions.assertThat(tool.getStdout()).contains("10 rows in new sstable and 0 empty"); + tool.assertOnCleanExit(); + + // check data is still there + assertOrderedAll(cfs, 10); + } + + @Test + public void testHeaderFixWithTool() + { + CompactionManager.instance.disableAutoCompaction(); + Keyspace keyspace = Keyspace.open(KEYSPACE2); + ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CF3); + cfs.clearUnsafe(); + + fillCF(cfs, 1); + assertOrderedAll(cfs, 1); + + ToolResult tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-e", "validate_only", KEYSPACE2, CF3); + Assertions.assertThat(tool.getStdout()).contains("Not continuing with scrub, since '--header-fix validate-only' was specified."); + tool.assertOnCleanExit(); + assertOrderedAll(cfs, 1); + + tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-e", "validate", KEYSPACE2, CF3); + Assertions.assertThat(tool.getStdout()).contains("Pre-scrub sstables snapshotted into"); + Assertions.assertThat(tool.getStdout()).contains("1 rows in new sstable and 0 empty"); + tool.assertOnCleanExit(); + assertOrderedAll(cfs, 1); + + tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-e", "fix-only", KEYSPACE2, CF3); + Assertions.assertThat(tool.getStdout()).contains("Not continuing with scrub, since '--header-fix fix-only' was specified."); + tool.assertOnCleanExit(); + assertOrderedAll(cfs, 1); + + tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-e", "fix", KEYSPACE2, CF3); + Assertions.assertThat(tool.getStdout()).contains("Pre-scrub sstables snapshotted into"); + Assertions.assertThat(tool.getStdout()).contains("1 rows in new sstable and 0 empty"); + tool.assertOnCleanExit(); + assertOrderedAll(cfs, 1); + + tool = ToolRunner.invokeClass(StandaloneScrubber.class, "-e", "off", KEYSPACE2, CF3); + Assertions.assertThat(tool.getStdout()).contains("Pre-scrub sstables snapshotted into"); + Assertions.assertThat(tool.getStdout()).contains("1 rows in new sstable and 0 empty"); + tool.assertOnCleanExit(); + assertOrderedAll(cfs, 1); + } } diff --git a/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java b/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java index f0b996ff3d..ea1499e886 100644 --- a/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java +++ b/test/unit/org/apache/cassandra/tools/StandaloneScrubberTest.java @@ -34,6 +34,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +/* + * We are testing cmd line params here. + * For sstable scrubbing tests look at {@link ScrubTest} + * For TTL sstable scrubbing tests look at {@link TTLTest} + */ + @RunWith(OrderedJUnit4ClassRunner.class) public class StandaloneScrubberTest extends OfflineToolUtils {