This is an automated email from the ASF dual-hosted git repository.
bereng pushed a commit to branch cassandra-4.0
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/cassandra-4.0 by this push:
new 95257c0 Sstableverify unit test operate on SSTables
95257c0 is described below
commit 95257c0805eeeb7209a421148b59796c3fb426ed
Author: Bereng <[email protected]>
AuthorDate: Tue Jan 18 07:45:27 2022 +0100
Sstableverify unit test operate on SSTables
patch by Brian Houser; reviewed by Andres de la Peña, Berenguer Blasi for
CASSANDRA-17009
Co-authored-by: Brian Houser <[email protected]>
Co-authored-by: Berenguer Blasi <[email protected]>
---
.gitignore | 1 +
.../apache/cassandra/tools/StandaloneVerifier.java | 40 +++-
test/unit/org/apache/cassandra/db/VerifyTest.java | 42 ++--
.../apache/cassandra/tools/OfflineToolUtils.java | 3 -
.../tools/StandaloneVerifierOnSSTablesTest.java | 222 +++++++++++++++++++++
.../cassandra/tools/StandaloneVerifierTest.java | 30 ++-
6 files changed, 304 insertions(+), 34 deletions(-)
diff --git a/.gitignore b/.gitignore
index 7d5e24d..3b01673 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ src/gen-java/
src/resources/org/apache/cassandra/config/
logs/
data/
+!test/data
conf/hotspot_compiler
doc/cql3/CQL.html
doc/build/
diff --git a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
index 4e4a80a..df276bd 100644
--- a/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneVerifier.java
@@ -18,26 +18,37 @@
*/
package org.apache.cassandra.tools;
-import org.apache.cassandra.schema.Schema;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+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.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.CompactionManager;
+import org.apache.cassandra.db.compaction.Verifier;
import org.apache.cassandra.dht.Murmur3Partitioner;
import org.apache.cassandra.dht.Range;
import org.apache.cassandra.dht.Token;
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.format.SSTableReader;
+import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.utils.JVMStabilityInspector;
import org.apache.cassandra.utils.OutputHandler;
-import org.apache.commons.cli.*;
-
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static org.apache.cassandra.tools.BulkLoader.CmdLineOptions;
@@ -56,7 +67,8 @@ public class StandaloneVerifier
public static void main(String args[])
{
Options options = Options.parseArgs(args);
- Util.initDatabaseDescriptor();
+ initDatabaseDescriptorForTool();
+
System.out.println("sstableverify using the following options: " +
options);
try
@@ -98,6 +110,7 @@ public class StandaloneVerifier
System.err.println(String.format("Error Loading %s: %s",
entry.getKey(), e.getMessage()));
if (options.debug)
e.printStackTrace(System.err);
+ System.exit(1);
}
}
Verifier.Options verifyOptions =
Verifier.options().invokeDiskFailurePolicy(false)
@@ -134,6 +147,13 @@ public class StandaloneVerifier
}
}
+ private static void initDatabaseDescriptorForTool() {
+ if (Boolean.getBoolean(Util.ALLOW_TOOL_REINIT_FOR_TEST))
+ DatabaseDescriptor.toolInitialization(false); //Necessary for
testing
+ else
+ Util.initDatabaseDescriptor();
+ }
+
private static class Options
{
public final String keyspaceName;
diff --git a/test/unit/org/apache/cassandra/db/VerifyTest.java
b/test/unit/org/apache/cassandra/db/VerifyTest.java
index 8e3fbe4..b2f4344 100644
--- a/test/unit/org/apache/cassandra/db/VerifyTest.java
+++ b/test/unit/org/apache/cassandra/db/VerifyTest.java
@@ -18,13 +18,31 @@
*/
package org.apache.cassandra.db;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+import java.util.zip.CRC32;
+import java.util.zip.CheckedInputStream;
+
import com.google.common.base.Charsets;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.apache.cassandra.UpdateBuilder;
import org.apache.cassandra.Util;
import org.apache.cassandra.batchlog.Batch;
import org.apache.cassandra.batchlog.BatchlogManager;
import org.apache.cassandra.cache.ChunkCache;
-import org.apache.cassandra.UpdateBuilder;
import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.db.compaction.Verifier;
import org.apache.cassandra.db.marshal.UUIDType;
@@ -45,20 +63,6 @@ import org.apache.cassandra.schema.CompressionParams;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.commons.lang3.StringUtils;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.io.*;
-import java.net.UnknownHostException;
-import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
-import java.util.concurrent.ExecutionException;
-import java.util.zip.CRC32;
-import java.util.zip.CheckedInputStream;
import static org.apache.cassandra.SchemaLoader.counterCFMD;
import static org.apache.cassandra.SchemaLoader.createKeyspace;
@@ -69,6 +73,14 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+/**
+ * Test for {@link Verifier}.
+ *
+ * Note: the complete coverage is composed of:
+ * - {@link org.apache.cassandra.tools.StandaloneVerifierOnSSTablesTest}
+ * - {@link org.apache.cassandra.tools.StandaloneVerifierTest}
+ * - {@link VerifyTest}
+ */
public class VerifyTest
{
public static final String KEYSPACE = "Keyspace1";
diff --git a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
index c1678d7..7bd1143 100644
--- a/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
+++ b/test/unit/org/apache/cassandra/tools/OfflineToolUtils.java
@@ -33,10 +33,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.io.FileUtils;
-
-import org.junit.AfterClass;
import org.junit.BeforeClass;
-
import org.slf4j.LoggerFactory;
import static
org.apache.cassandra.utils.FBUtilities.preventIllegalAccessWarnings;
diff --git
a/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
b/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
new file mode 100644
index 0000000..7df28d2
--- /dev/null
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierOnSSTablesTest.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.tools;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.SchemaLoader;
+import org.apache.cassandra.UpdateBuilder;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.db.PartitionPosition;
+import org.apache.cassandra.db.compaction.CompactionManager;
+import org.apache.cassandra.io.sstable.Component;
+import org.apache.cassandra.io.sstable.format.SSTableReader;
+import org.apache.cassandra.schema.KeyspaceParams;
+import org.apache.cassandra.service.StorageService;
+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.standardCFMD;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Class that tests tables for {@link StandaloneVerifier} by updating using
{@link SchemaLoader}
+ * Similar in vein to other {@link SchemaLoader} type tests, as well as {@link
StandaloneUpgraderOnSStablesTest}.
+ * Since the tool mainly exercises the {@link
org.apache.cassandra.db.compaction.Verifier}, we elect to
+ * not run every conceivable option as many tests are already covered by
{@link org.apache.cassandra.db.VerifyTest}.
+ *
+ * Note: the complete coverage is composed of:
+ * - {@link StandaloneVerifierOnSSTablesTest}
+ * - {@link StandaloneVerifierTest}
+ * - {@link org.apache.cassandra.db.VerifyTest}
+ */
+public class StandaloneVerifierOnSSTablesTest extends OfflineToolUtils
+{
+ @BeforeClass
+ public static void setup()
+ {
+ // since legacy tables test data uses ByteOrderedPartitioner that's
what we need
+ // for the check version to work
+ System.setProperty("cassandra.partitioner",
"org.apache.cassandra.dht.ByteOrderedPartitioner");
+ System.setProperty(Util.ALLOW_TOOL_REINIT_FOR_TEST, "true"); //
Necessary for testing`
+ SchemaLoader.loadSchema();
+ StorageService.instance.initServer();
+ }
+
+ @AfterClass
+ public static void teardown() throws Exception
+ {
+ SchemaLoader.cleanupAndLeaveDirs();
+ System.clearProperty(Util.ALLOW_TOOL_REINIT_FOR_TEST);
+ }
+
+ @Test
+ public void testCheckVersionValidVersion() throws Exception
+ {
+ String keyspaceName = "StandaloneVerifierTestCheckVersionWorking";
+ String workingTable = "workingCheckTable";
+
+ createAndPopulateTable(keyspaceName, workingTable, x -> {});
+
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
keyspaceName, workingTable, "-c");
+ assertEquals(0, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
+ }
+
+ @Test
+ public void testCheckVersionWithWrongVersion() throws Exception
+ {
+ String keyspace = "StandaloneVerifierTestWrongVersions";
+ String tableName = "legacy_ma_simple";
+
+ createAndPopulateTable(keyspace, tableName, cfs -> {
+ // let's just copy old version files from test data into the
source dir
+ File testDataDir = new
File("test/data/legacy-sstables/ma/legacy_tables/legacy_ma_simple");
+ for (File cfsDir : cfs.getDirectories().getCFDirectories())
+ {
+ FileUtils.copyDirectory(testDataDir, cfsDir);
+ }
+ });
+
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
keyspace, tableName, "-c");
+
+ assertEquals(1, tool.getExitCode());
+ Assertions.assertThat(tool.getStdout()).contains("is not the latest
version, run upgradesstables");
+ }
+
+ @Test
+ public void testWorkingDataFile() throws Exception
+ {
+ String keyspaceName = "StandaloneVerifierTestWorkingDataKs";
+ String workingTable = "workingTable";
+
+ createAndPopulateTable(keyspaceName, workingTable, x -> {});
+
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
keyspaceName, workingTable);
+ assertEquals(0, tool.getExitCode());
+ assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
+ }
+
+ @Test
+ public void testCorruptStatsFile() throws Exception
+ {
+ String keyspaceName = "StandaloneVerifierTestCorruptStatsKs";
+ String corruptStatsTable = "corruptStatsTable";
+ createAndPopulateTable(keyspaceName, corruptStatsTable, cfs -> {
+ SSTableReader sstable = cfs.getLiveSSTables().iterator().next();
+ try (RandomAccessFile file = new
RandomAccessFile(sstable.descriptor.filenameFor(Component.STATS), "rw"))
+ {
+ file.seek(0);
+ file.writeBytes(StringUtils.repeat('z', 2));
+ }
+ });
+
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
keyspaceName, corruptStatsTable);
+
+ assertEquals(1, tool.getExitCode());
+ Assertions.assertThat(tool.getStderr()).contains("Error Loading",
corruptStatsTable);
+ }
+
+ @Test
+ public void testCorruptDataFile() throws Exception
+ {
+ String keyspaceName = "StandaloneVerifierTestCorruptDataKs";
+ String corruptDataTable = "corruptDataTable";
+
+ createAndPopulateTable(keyspaceName, corruptDataTable, cfs -> {
+ SSTableReader sstable = cfs.getLiveSSTables().iterator().next();
+ long row0Start =
sstable.getPosition(PartitionPosition.ForKey.get(ByteBufferUtil.bytes("0"),
cfs.getPartitioner()), SSTableReader.Operator.EQ).position;
+ long row1Start =
sstable.getPosition(PartitionPosition.ForKey.get(ByteBufferUtil.bytes("1"),
cfs.getPartitioner()), SSTableReader.Operator.EQ).position;
+ long startPosition = Math.min(row0Start, row1Start);
+
+ try (RandomAccessFile file = new
RandomAccessFile(sstable.getFilename(), "rw"))
+ {
+ file.seek(startPosition);
+ file.writeBytes(StringUtils.repeat('z', 2));
+ }
+ });
+
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
keyspaceName, corruptDataTable);
+ assertEquals(1, tool.getExitCode());
+ Assertions.assertThat(tool.getStdout()).contains("Invalid SSTable",
corruptDataTable);
+ }
+
+ /**
+ * Since we are testing a verifier, we'd like to corrupt files to verify
code paths
+ * This function definition is used by {@link this#createAndPopulateTable}.
+ *
+ * CFS is the open ColumnFamilyStore for a given keyspace, table
+ */
+ @FunctionalInterface
+ private interface CorruptFunction
+ {
+ public void apply(ColumnFamilyStore cfs) throws Exception;
+ }
+
+ /**
+ * This function sets up the keyspace, and table schema for a standardCFMD
table.
+ * <p>
+ * This will also populate the tableName with a few rows. After
completion the
+ * server will be shutdown.
+ *
+ * @param keyspace the name of the keyspace in which the table should be
created
+ * @param tableName new table name of the standard CFMD table
+ * @param corruptionFn function called to corrupt or change the contents
on disk, is passed the Cfs of the table name.
+ * @throws Exception on error.
+ */
+ private static void createAndPopulateTable(String keyspace, String
tableName, CorruptFunction corruptionFn) throws Exception
+ {
+ SchemaLoader.createKeyspace(keyspace,
+ KeyspaceParams.simple(1),
+ standardCFMD(keyspace, tableName));
+
+ CompactionManager.instance.disableAutoCompaction();
+
+ Keyspace k = Keyspace.open(keyspace);
+ ColumnFamilyStore cfs = k.getColumnFamilyStore(tableName);
+
+ populateTable(cfs, 2);
+
+ corruptionFn.apply(cfs);
+ }
+
+ private static void populateTable(ColumnFamilyStore cfs, int
partitionsPerSSTable)
+ {
+ for (int i = 0; i < partitionsPerSSTable; i++)
+ {
+ UpdateBuilder.create(cfs.metadata(), String.valueOf(i))
+ .newRow("c1").add("val", "1")
+ .newRow("c2").add("val", "2")
+ .apply();
+ }
+
+ cfs.forceBlockingFlush();
+ }
+}
diff --git a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
index 73890cd..9e6bb14 100644
--- a/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
+++ b/test/unit/org/apache/cassandra/tools/StandaloneVerifierTest.java
@@ -29,6 +29,12 @@ import org.hamcrest.CoreMatchers;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+/**
+* Note: the complete coverage is composed of:
+ * - {@link StandaloneVerifierOnSSTablesTest}
+ * - {@link StandaloneVerifierTest}
+ * - {@link org.apache.cassandra.db.VerifyTest}
+*/
public class StandaloneVerifierTest extends OfflineToolUtils
{
@Test
@@ -70,6 +76,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
assertEquals(0, tool.getExitCode());
assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
+
}
@Test
@@ -78,8 +86,9 @@ public class StandaloneVerifierTest extends OfflineToolUtils
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
"--debug", "system_schema", "tables");
assertThat(tool.getStdout(),
CoreMatchers.containsStringIgnoringCase("debug=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
- tool.assertOnExitCode();
assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
+
}
@Test
@@ -92,8 +101,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
"tables");
assertThat(tool.getStdout(),
CoreMatchers.containsStringIgnoringCase("extended=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
- tool.assertOnExitCode();
assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
});
}
@@ -107,8 +116,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
"tables");
assertThat(tool.getStdout(),
CoreMatchers.containsStringIgnoringCase("quick=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
- tool.assertOnExitCode();
assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
});
}
@@ -122,8 +131,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
"tables");
assertThat(tool.getStdout(),
CoreMatchers.containsStringIgnoringCase("mutateRepairStatus=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
- tool.assertOnExitCode();
assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
});
}
@@ -134,8 +143,8 @@ public class StandaloneVerifierTest extends OfflineToolUtils
ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
arg);
assertThat(tool.getStdout(),
CoreMatchers.containsStringIgnoringCase("usage:"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
- tool.assertOnExitCode();
assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
});
}
@@ -149,8 +158,17 @@ public class StandaloneVerifierTest extends
OfflineToolUtils
"tables");
assertThat(tool.getStdout(),
CoreMatchers.containsStringIgnoringCase("verbose=true"));
Assertions.assertThat(tool.getCleanedStderr()).isEmpty();
- tool.assertOnExitCode();
assertCorrectEnvPostTest();
+ tool.assertOnCleanExit();
});
}
+
+ @Test
+ public void testTooManyArgs()
+ {
+ ToolResult tool = ToolRunner.invokeClass(StandaloneVerifier.class,
"another arg", "system_schema", "tables");
+ assertThat(tool.getStdout(),
CoreMatchers.containsStringIgnoringCase("usage:"));
+ assertThat(tool.getCleanedStderr(),
CoreMatchers.containsStringIgnoringCase("Too many arguments"));
+ assertEquals(1, tool.getExitCode());
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]