Skip to content

Commit 4fbf95a

Browse files
CNDB-14477: Validate SAI Components in Verifier (#1816)
- **CNDB-14477: Validate SAI Components in Verifier** - **Add follow up comment to vector corruption check** ### What is the issue Fixes: riptano/cndb#14477 ### What does this PR fix and why was it fixed This PR leverages the existing SAI verification framework and wires it up to the `Verifier` logic in the code compaction code.
1 parent f837cff commit 4fbf95a

File tree

8 files changed

+533
-1
lines changed

8 files changed

+533
-1
lines changed

src/java/org/apache/cassandra/db/compaction/Verifier.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.apache.cassandra.io.util.FileUtils;
6565
import org.apache.cassandra.io.util.RandomAccessReader;
6666
import org.apache.cassandra.io.util.ReadPattern;
67+
import org.apache.cassandra.schema.IndexMetadata;
6768
import org.apache.cassandra.schema.TableMetadata;
6869
import org.apache.cassandra.service.ActiveRepairService;
6970
import org.apache.cassandra.service.StorageService;
@@ -237,6 +238,9 @@ public void verify()
237238
}
238239
}
239240

241+
// Validate the secondary indexes. Only validate checksums if quick is disabled.
242+
validateSecondaryIndexes(!options.quick);
243+
240244
if (options.quick)
241245
return;
242246

@@ -382,6 +386,34 @@ public void verify()
382386
outputHandler.output("Verify of " + sstable + " succeeded. All " + goodRows + " rows read successfully");
383387
}
384388

389+
private void validateSecondaryIndexes(boolean validateChecksum)
390+
{
391+
if (realm == null)
392+
{
393+
outputHandler.output("Skipping secondary index component validation for " + sstable +
394+
" because the compaction realm is not available");
395+
return;
396+
}
397+
398+
outputHandler.output(String.format("Checking secondary index components for %s, validateChecksum=%s", sstable, validateChecksum));
399+
try
400+
{
401+
var indexManager = realm.getIndexManager();
402+
for (IndexMetadata indexMetadata : sstable.metadata().indexes)
403+
{
404+
var index = indexManager.getIndexGroup(indexMetadata);
405+
if (index == null)
406+
throw new IllegalStateException("Cannot verify index components for " + sstable + " because the index " + indexMetadata.name + " is not registered");
407+
index.validateComponents(sstable, validateChecksum);
408+
}
409+
}
410+
catch (Throwable t)
411+
{
412+
outputHandler.warn(t);
413+
markAndThrow(t);
414+
}
415+
}
416+
385417
/**
386418
* Use the fact that check(..) is called with sorted tokens - we keep a pointer in to the normalized ranges
387419
* and only bump the pointer if the key given is out of range. This is done to avoid calling .contains(..) many

src/java/org/apache/cassandra/index/Index.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import org.apache.cassandra.schema.ColumnMetadata;
5858
import org.apache.cassandra.schema.IndexMetadata;
5959
import org.apache.cassandra.schema.TableMetadata;
60+
import org.apache.lucene.index.CorruptIndexException;
6061

6162

6263
/**
@@ -859,6 +860,19 @@ default void unload() { }
859860
*/
860861
Set<Component> activeComponents(SSTableReader sstable);
861862

863+
/**
864+
* Validate the sstable-attached components belonging to the group that are currently "active" for the
865+
* provided sstable. Method is side effect free.
866+
* <p>
867+
* The "active" components are those returned by {@link #activeComponents}.
868+
*
869+
* @param sstable the sstable to validate components for.
870+
* @param validateChecksum if {@code true}, the checksum of the components will be validated. Otherwise, only
871+
* basic checks on the header and footers will be performed.
872+
* @throws CorruptIndexException if the validation fails.
873+
*/
874+
void validateComponents(SSTableReader sstable, boolean validateChecksum) throws CorruptIndexException;
875+
862876
/**
863877
* @return true if this index group is capable of supporting multiple contains restrictions, false otherwise
864878
*/

src/java/org/apache/cassandra/index/IndexRegistry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ public Set<Component> activeComponents(SSTableReader sstable)
256256
{
257257
return null;
258258
}
259+
260+
@Override
261+
public void validateComponents(SSTableReader sstable, boolean validateChecksum)
262+
{
263+
}
259264
};
260265

261266
public void registerIndex(Index index, Index.Group.Key groupKey, Supplier<Index.Group> groupSupplier)

src/java/org/apache/cassandra/index/SingletonIndexGroup.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,10 @@ public Set<Component> activeComponents(SSTableReader sstable)
147147
// Same rermarks as for `componentsForNewBuid`.
148148
return Collections.emptySet();
149149
}
150+
151+
@Override
152+
public void validateComponents(SSTableReader sstable, boolean validateChecksum)
153+
{
154+
// Same remarks as for `componentsForNewBuid`.
155+
}
150156
}

src/java/org/apache/cassandra/index/sai/StorageAttachedIndexGroup.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.cassandra.index.sai;
1919

20+
import java.io.IOException;
2021
import java.lang.invoke.MethodHandles;
2122
import java.util.ArrayList;
2223
import java.util.Collection;
@@ -59,6 +60,7 @@
5960
import org.apache.cassandra.index.sai.plan.StorageAttachedIndexQueryPlan;
6061
import org.apache.cassandra.index.transactions.IndexTransaction;
6162
import org.apache.cassandra.io.sstable.Component;
63+
import org.apache.cassandra.io.sstable.CorruptSSTableException;
6264
import org.apache.cassandra.io.sstable.Descriptor;
6365
import org.apache.cassandra.io.sstable.SSTableWatcher;
6466
import org.apache.cassandra.io.sstable.format.SSTableFlushObserver;
@@ -71,6 +73,7 @@
7173
import org.apache.cassandra.notifications.SSTableListChangedNotification;
7274
import org.apache.cassandra.schema.TableMetadata;
7375
import org.apache.cassandra.utils.Throwables;
76+
import org.apache.lucene.index.CorruptIndexException;
7477

7578
/**
7679
* Orchestrates building of storage-attached indices, and manages lifecycle of resources shared between them.
@@ -307,6 +310,21 @@ public Set<Component> activeComponents(SSTableReader sstable)
307310
return components;
308311
}
309312

313+
@Override
314+
public void validateComponents(SSTableReader sstable, boolean validateChecksum) throws CorruptIndexException
315+
{
316+
IndexDescriptor indexDescriptor = descriptorFor(sstable);
317+
if (!indexDescriptor.perSSTableComponents().isValid(validateChecksum))
318+
throw new CorruptIndexException("Failed validation of per-SSTable components", sstable.getFilename());
319+
320+
for (StorageAttachedIndex index : indices)
321+
{
322+
IndexContext ctx = index.getIndexContext();
323+
if (!indexDescriptor.perIndexComponents(ctx).isValid(validateChecksum))
324+
throw new CorruptIndexException("Failed validation of per-column components for " + ctx, sstable.getFilename());
325+
}
326+
}
327+
310328
@Override
311329
public void handleNotification(INotification notification, Object sender)
312330
{

src/java/org/apache/cassandra/index/sai/disk/format/IndexDescriptor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ public boolean isValid(boolean validateChecksum, Consumer<IndexComponent> onInva
414414
for (IndexComponentType expected : expectedComponentsForVersion())
415415
{
416416
var component = components.get(expected);
417-
if (component == null)
417+
if (component == null || !component.file().exists())
418418
{
419419
logger.warn(logMessage("Missing index component {} from SSTable {}"), expected, descriptor);
420420
isValid = false;

test/unit/org/apache/cassandra/index/CustomIndexTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,11 @@ public Set<Component> activeComponents(SSTableReader sstable)
16871687
{
16881688
return Collections.emptySet();
16891689
}
1690+
1691+
@Override
1692+
public void validateComponents(SSTableReader sstable, boolean validateChecksum)
1693+
{
1694+
}
16901695
}
16911696
}
16921697
}

0 commit comments

Comments
 (0)