Deserialize sstable metadata in nodetool verify

Patch by marcuse; reviewed by Jason Brown for CASSANDRA-13922


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/e400b976
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/e400b976
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/e400b976

Branch: refs/heads/cassandra-3.11
Commit: e400b976751110d41405bac614189152bf88f7ef
Parents: b32a9e6
Author: Marcus Eriksson <marc...@apache.org>
Authored: Mon Oct 2 10:11:17 2017 +0200
Committer: Marcus Eriksson <marc...@apache.org>
Committed: Wed Oct 4 08:15:23 2017 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  4 +++
 .../cassandra/db/compaction/Verifier.java       | 32 ++++++++++++++++----
 .../org/apache/cassandra/db/VerifyTest.java     | 25 +++++++++++++++
 3 files changed, 55 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/e400b976/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index d6423b4..df05f7f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,7 @@
+3.0.16
+  * Deserialise sstable metadata in nodetool verify (CASSANDRA-13922)
+
+
 3.0.15
  * Improve TRUNCATE performance (CASSANDRA-13909)
  * Implement short read protection on partition boundaries (CASSANDRA-13595)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e400b976/src/java/org/apache/cassandra/db/compaction/Verifier.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/compaction/Verifier.java 
b/src/java/org/apache/cassandra/db/compaction/Verifier.java
index 88bc3a7..68088b3 100644
--- a/src/java/org/apache/cassandra/db/compaction/Verifier.java
+++ b/src/java/org/apache/cassandra/db/compaction/Verifier.java
@@ -26,13 +26,15 @@ import org.apache.cassandra.io.sstable.Component;
 import org.apache.cassandra.io.sstable.CorruptSSTableException;
 import org.apache.cassandra.io.sstable.SSTableIdentityIterator;
 import org.apache.cassandra.io.sstable.format.SSTableReader;
+import org.apache.cassandra.io.sstable.metadata.MetadataComponent;
+import org.apache.cassandra.io.sstable.metadata.MetadataType;
+import org.apache.cassandra.io.sstable.metadata.ValidationMetadata;
 import org.apache.cassandra.io.util.DataIntegrityMetadata;
 import org.apache.cassandra.io.util.DataIntegrityMetadata.FileDigestValidator;
 import org.apache.cassandra.io.util.FileUtils;
 import org.apache.cassandra.io.util.RandomAccessReader;
 import org.apache.cassandra.service.ActiveRepairService;
 import org.apache.cassandra.utils.ByteBufferUtil;
-import org.apache.cassandra.utils.FBUtilities;
 import org.apache.cassandra.utils.OutputHandler;
 import org.apache.cassandra.utils.UUIDGen;
 
@@ -58,7 +60,6 @@ public class Verifier implements Closeable
     private final RowIndexEntry.IndexSerializer rowIndexEntrySerializer;
 
     private int goodRows;
-    private int badRows;
 
     private final OutputHandler outputHandler;
     private FileDigestValidator validator;
@@ -89,6 +90,20 @@ public class Verifier implements Closeable
         long rowStart = 0;
 
         outputHandler.output(String.format("Verifying %s (%s bytes)", sstable, 
dataFile.length()));
+        outputHandler.output(String.format("Deserializing sstable metadata for 
%s ", sstable));
+        try
+        {
+            EnumSet<MetadataType> types = EnumSet.of(MetadataType.VALIDATION, 
MetadataType.STATS, MetadataType.HEADER);
+            Map<MetadataType, MetadataComponent> sstableMetadata = 
sstable.descriptor.getMetadataSerializer().deserialize(sstable.descriptor, 
types);
+            if (sstableMetadata.containsKey(MetadataType.VALIDATION) &&
+                
!((ValidationMetadata)sstableMetadata.get(MetadataType.VALIDATION)).partitioner.equals(sstable.getPartitioner().getClass().getCanonicalName()))
+                throw new IOException("Partitioner does not match validation 
metadata");
+        }
+        catch (Throwable t)
+        {
+            outputHandler.debug(t.getMessage());
+            markAndThrow(false);
+        }
         outputHandler.output(String.format("Checking computed hash of %s ", 
sstable));
 
 
@@ -187,7 +202,7 @@ public class Verifier implements Closeable
                     if (key == null || dataSize > dataFile.length())
                         markAndThrow();
 
-                    //mimic the scrub read path
+                    //mimic the scrub read path, intentionally unused
                     try (UnfilteredRowIterator iterator = new 
SSTableIdentityIterator(sstable, dataFile, key))
                     {
                     }
@@ -204,7 +219,6 @@ public class Verifier implements Closeable
                 }
                 catch (Throwable th)
                 {
-                    badRows++;
                     markAndThrow();
                 }
             }
@@ -235,8 +249,14 @@ public class Verifier implements Closeable
 
     private void markAndThrow() throws IOException
     {
-        
sstable.descriptor.getMetadataSerializer().mutateRepairedAt(sstable.descriptor, 
ActiveRepairService.UNREPAIRED_SSTABLE);
-        throw new CorruptSSTableException(new Exception(String.format("Invalid 
SSTable %s, please force repair", sstable.getFilename())), 
sstable.getFilename());
+        markAndThrow(true);
+    }
+
+    private void markAndThrow(boolean mutateRepaired) throws IOException
+    {
+        if (mutateRepaired) // if we are able to mutate repaired flag, an 
incremental repair should be enough
+            
sstable.descriptor.getMetadataSerializer().mutateRepairedAt(sstable.descriptor, 
ActiveRepairService.UNREPAIRED_SSTABLE);
+        throw new CorruptSSTableException(new Exception(String.format("Invalid 
SSTable %s, please force %srepair", sstable.getFilename(), mutateRepaired ? "" 
: "a full ")), sstable.getFilename());
     }
 
     public CompactionInfo.Holder getVerifyInfo()

http://git-wip-us.apache.org/repos/asf/cassandra/blob/e400b976/test/unit/org/apache/cassandra/db/VerifyTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/VerifyTest.java 
b/test/unit/org/apache/cassandra/db/VerifyTest.java
index 9de01c1..fc87520 100644
--- a/test/unit/org/apache/cassandra/db/VerifyTest.java
+++ b/test/unit/org/apache/cassandra/db/VerifyTest.java
@@ -341,9 +341,34 @@ public class VerifyTest
             }
             fail("Expected a CorruptSSTableException to be thrown");
         }
+    }
+
+    @Test(expected = CorruptSSTableException.class)
+    public void testVerifyBrokenSSTableMetadata() throws IOException, 
WriteTimeoutException
+    {
+        CompactionManager.instance.disableAutoCompaction();
+        Keyspace keyspace = Keyspace.open(KEYSPACE);
+        ColumnFamilyStore cfs = keyspace.getColumnFamilyStore(CORRUPT_CF2);
+
+        fillCF(cfs, 2);
+
+        Util.getAll(Util.cmd(cfs).build());
 
+        SSTableReader sstable = cfs.getLiveSSTables().iterator().next();
+
+        String filenameToCorrupt = 
sstable.descriptor.filenameFor(Component.STATS);
+        RandomAccessFile file = new RandomAccessFile(filenameToCorrupt, "rw");
+        file.seek(0);
+        file.writeBytes(StringUtils.repeat('z', 2));
+        file.close();
+
+        try (Verifier verifier = new Verifier(cfs, sstable, false))
+        {
+            verifier.verify(false);
+        }
     }
 
+
     protected void fillCF(ColumnFamilyStore cfs, int partitionsPerSSTable)
     {
         for (int i = 0; i < partitionsPerSSTable; i++)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to