Skip to content

Commit a84abe7

Browse files
sunil9977maedhroz
authored andcommitted
Extend nodetool verify to (optionally) validate SAI files
patch by Sunil Ramchandra Pawar; reviewed by Caleb Rackliffe and David Capwell for CASSANDRA-20949
1 parent ba9790a commit a84abe7

File tree

9 files changed

+291
-17
lines changed

9 files changed

+291
-17
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
5.1
2+
* Extend nodetool verify to (optionally) validate SAI files (CASSANDRA-20949)
23
* Fix CompressionDictionary being closed while still in use (CASSANDRA-21047)
34
* When updating a multi cell collection element, if the update is rejected then the shared Row.Builder is not freed causing all future mutations to be rejected (CASSANDRA-21055)
45
* Schema annotations escape validation on CREATE and ALTER DDL statements (CASSANDRA-21046)

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

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
import org.apache.cassandra.dht.Token;
9090
import org.apache.cassandra.exceptions.ConfigurationException;
9191
import org.apache.cassandra.index.SecondaryIndexBuilder;
92+
import org.apache.cassandra.index.sai.StorageAttachedIndexGroup;
9293
import org.apache.cassandra.io.sstable.Descriptor;
9394
import org.apache.cassandra.io.sstable.ISSTableScanner;
9495
import org.apache.cassandra.io.sstable.IScrubber;
@@ -669,6 +670,15 @@ public void execute(LifecycleTransaction input)
669670
public AllSSTableOpStatus performVerify(ColumnFamilyStore cfs, IVerifier.Options options) throws InterruptedException, ExecutionException
670671
{
671672
assert !cfs.isIndex();
673+
StorageAttachedIndexGroup indexGroup = StorageAttachedIndexGroup.getIndexGroup(cfs);
674+
boolean skipSaiCheck = indexGroup == null;
675+
if (options.onlySai && skipSaiCheck)
676+
{
677+
logger.info("Skipping table {} during SAI-only verify because it has no SAI indexes.", cfs.getTableName());
678+
return AllSSTableOpStatus.SUCCESSFUL;
679+
680+
}
681+
672682
return parallelAllSSTableOperation(cfs, new OneSSTableOperation()
673683
{
674684
@Override
@@ -1493,17 +1503,37 @@ void scrubOne(ColumnFamilyStore cfs, LifecycleTransaction modifier, IScrubber.Op
14931503
@VisibleForTesting
14941504
void verifyOne(ColumnFamilyStore cfs, SSTableReader sstable, IVerifier.Options options, ActiveCompactionsTracker activeCompactions)
14951505
{
1506+
1507+
StorageAttachedIndexGroup indexGroup = StorageAttachedIndexGroup.getIndexGroup(cfs);
1508+
boolean skipSaiCheck = indexGroup == null;
1509+
1510+
// Skip early if no SAI indexes on table
1511+
if (options.onlySai && skipSaiCheck)
1512+
{
1513+
logger.info("Skipping SAI validation for table {} because it has no SAI indexes.", cfs.getTableName());
1514+
return;
1515+
}
1516+
14961517
CompactionInfo.Holder verifyInfo = null;
1497-
try (IVerifier verifier = sstable.getVerifier(cfs, new OutputHandler.LogOutput(), false, options))
1518+
1519+
if (!options.onlySai)
14981520
{
1499-
verifyInfo = verifier.getVerifyInfo();
1500-
activeCompactions.beginCompaction(verifyInfo);
1501-
verifier.verify();
1521+
try (IVerifier verifier = sstable.getVerifier(cfs, new OutputHandler.LogOutput(), false, options))
1522+
{
1523+
verifyInfo = verifier.getVerifyInfo();
1524+
activeCompactions.beginCompaction(verifyInfo);
1525+
verifier.verify();
1526+
}
1527+
finally
1528+
{
1529+
if (verifyInfo != null)
1530+
activeCompactions.finishCompaction(verifyInfo);
1531+
}
15021532
}
1503-
finally
1533+
1534+
if ((options.onlySai || options.includeSai) && !skipSaiCheck)
15041535
{
1505-
if (verifyInfo != null)
1506-
activeCompactions.finishCompaction(verifyInfo);
1536+
cfs.indexManager.validateSSTableAttachedIndexes(Collections.singleton(sstable), true, true);
15071537
}
15081538
}
15091539

src/java/org/apache/cassandra/io/sstable/IVerifier.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ class Options
6060
*/
6161
public final boolean quick;
6262

63+
/**
64+
* To verify only SAI checksum
65+
*/
66+
public final boolean onlySai;
67+
68+
/**
69+
* To include SAI verification along with data files
70+
*/
71+
public final boolean includeSai;
72+
6373
public final Function<String, ? extends Collection<Range<Token>>> tokenLookup;
6474

6575
private Options(boolean invokeDiskFailurePolicy,
@@ -68,14 +78,21 @@ private Options(boolean invokeDiskFailurePolicy,
6878
boolean mutateRepairStatus,
6979
boolean checkOwnsTokens,
7080
boolean quick,
81+
boolean onlySai,
82+
boolean includeSai,
7183
Function<String, ? extends Collection<Range<Token>>> tokenLookup)
7284
{
85+
if (onlySai && includeSai)
86+
throw new IllegalArgumentException("onlySai and includeSai both cannot be true at a time.");
87+
7388
this.invokeDiskFailurePolicy = invokeDiskFailurePolicy;
7489
this.extendedVerification = extendedVerification;
7590
this.checkVersion = checkVersion;
7691
this.mutateRepairStatus = mutateRepairStatus;
7792
this.checkOwnsTokens = checkOwnsTokens;
7893
this.quick = quick;
94+
this.onlySai = onlySai;
95+
this.includeSai = includeSai;
7996
this.tokenLookup = tokenLookup;
8097
}
8198

@@ -89,6 +106,8 @@ public String toString()
89106
", mutateRepairStatus=" + mutateRepairStatus +
90107
", checkOwnsTokens=" + checkOwnsTokens +
91108
", quick=" + quick +
109+
", onlySai=" + onlySai +
110+
", includeSai=" + includeSai +
92111
'}';
93112
}
94113

@@ -100,6 +119,8 @@ public static class Builder
100119
private boolean mutateRepairStatus = false; // mutating repair status can be dangerous
101120
private boolean checkOwnsTokens = false;
102121
private boolean quick = false;
122+
private boolean onlySai = false;
123+
private boolean includeSai = false;
103124
private Function<String, ? extends Collection<Range<Token>>> tokenLookup = StorageService.instance::getLocalAndPendingRanges;
104125

105126
public Builder invokeDiskFailurePolicy(boolean param)
@@ -138,6 +159,18 @@ public Builder quick(boolean param)
138159
return this;
139160
}
140161

162+
public Builder onlySai(boolean param)
163+
{
164+
this.onlySai = param;
165+
return this;
166+
}
167+
168+
public Builder includeSai(boolean param)
169+
{
170+
this.includeSai = param;
171+
return this;
172+
}
173+
141174
public Builder tokenLookup(Function<String, ? extends Collection<Range<Token>>> tokenLookup)
142175
{
143176
this.tokenLookup = tokenLookup;
@@ -146,7 +179,7 @@ public Builder tokenLookup(Function<String, ? extends Collection<Range<Token>>>
146179

147180
public Options build()
148181
{
149-
return new Options(invokeDiskFailurePolicy, extendedVerification, checkVersion, mutateRepairStatus, checkOwnsTokens, quick, tokenLookup);
182+
return new Options(invokeDiskFailurePolicy, extendedVerification, checkVersion, mutateRepairStatus, checkOwnsTokens, quick, onlySai, includeSai, tokenLookup);
150183
}
151184
}
152185
}

src/java/org/apache/cassandra/service/StorageService.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,18 +2690,28 @@ public int scrub(boolean disableSnapshot, IScrubber.Options options, int jobs, S
26902690
@Deprecated(since = "4.0")
26912691
public int verify(boolean extendedVerify, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException
26922692
{
2693-
return verify(extendedVerify, false, false, false, false, false, keyspaceName, tableNames);
2693+
return verify(extendedVerify, false, false, false, false, false, false, false, keyspaceName, tableNames);
26942694
}
26952695

2696+
/**
2697+
* Kept for backward compatibility with existing clients.
2698+
*/
26962699
public int verify(boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException
2700+
{
2701+
return verify(extendedVerify, checkVersion, diskFailurePolicy, mutateRepairStatus, checkOwnsTokens, quick, false, false, keyspaceName, tableNames);
2702+
}
2703+
2704+
public int verify(boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, boolean onlySai, boolean includeSai, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException
26972705
{
26982706
CompactionManager.AllSSTableOpStatus status = CompactionManager.AllSSTableOpStatus.SUCCESSFUL;
26992707
IVerifier.Options options = IVerifier.options().invokeDiskFailurePolicy(diskFailurePolicy)
27002708
.extendedVerification(extendedVerify)
27012709
.checkVersion(checkVersion)
27022710
.mutateRepairStatus(mutateRepairStatus)
27032711
.checkOwnsTokens(checkOwnsTokens)
2704-
.quick(quick).build();
2712+
.quick(quick)
2713+
.onlySai(onlySai)
2714+
.includeSai(includeSai).build();
27052715
logger.info("Staring {} on {}.{} with options = {}", OperationType.VERIFY, keyspaceName, Arrays.toString(tableNames), options);
27062716
for (ColumnFamilyStore cfStore : getValidColumnFamilies(false, false, keyspaceName, tableNames))
27072717
{

src/java/org/apache/cassandra/service/StorageServiceMBean.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,8 +452,17 @@ default int scrub(boolean disableSnapshot, boolean skipCorrupted, boolean checkD
452452
* The entire sstable will be read to ensure each cell validates if extendedVerify is true
453453
*/
454454
public int verify(boolean extendedVerify, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException;
455+
456+
/**
457+
* Kept for backward compatibility with existing clients.
458+
*/
455459
public int verify(boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException;
456460

461+
/**
462+
* Verify checksums of the given keyspace with extended options including SAI index validation.
463+
*/
464+
public int verify(boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, boolean onlySai, boolean includeSai, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException;
465+
457466
/**
458467
* Rewrite all sstables to the latest version.
459468
* Unlike scrub, it doesn't skip bad rows and do not snapshot sstables first.

src/java/org/apache/cassandra/tools/NodeProbe.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -391,9 +391,9 @@ public int scrub(boolean disableSnapshot, boolean skipCorrupted, boolean checkDa
391391
return ssProxy.scrub(disableSnapshot, skipCorrupted, checkData, reinsertOverflowedTTL, jobs, keyspaceName, tables);
392392
}
393393

394-
public int verify(boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException
394+
public int verify(boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, boolean onlySai, boolean includeSai, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException
395395
{
396-
return ssProxy.verify(extendedVerify, checkVersion, diskFailurePolicy, mutateRepairStatus, checkOwnsTokens, quick, keyspaceName, tableNames);
396+
return ssProxy.verify(extendedVerify, checkVersion, diskFailurePolicy, mutateRepairStatus, checkOwnsTokens, quick, onlySai, includeSai, keyspaceName, tableNames);
397397
}
398398

399399
public int upgradeSSTables(String keyspaceName, boolean excludeCurrentVersion, long maxSSTableTimestamp, int jobs, String... tableNames) throws IOException, ExecutionException, InterruptedException
@@ -434,10 +434,10 @@ public void scrub(PrintStream out, boolean disableSnapshot, boolean skipCorrupte
434434
"scrubbing");
435435
}
436436

437-
public void verify(PrintStream out, boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException
437+
public void verify(PrintStream out, boolean extendedVerify, boolean checkVersion, boolean diskFailurePolicy, boolean mutateRepairStatus, boolean checkOwnsTokens, boolean quick, boolean onlySai, boolean includeSai, String keyspaceName, String... tableNames) throws IOException, ExecutionException, InterruptedException
438438
{
439439
perform(out, keyspaceName,
440-
() -> verify(extendedVerify, checkVersion, diskFailurePolicy, mutateRepairStatus, checkOwnsTokens, quick, keyspaceName, tableNames),
440+
() -> verify(extendedVerify, checkVersion, diskFailurePolicy, mutateRepairStatus, checkOwnsTokens, quick, onlySai, includeSai, keyspaceName, tableNames),
441441
"verifying");
442442
}
443443

src/java/org/apache/cassandra/tools/nodetool/Verify.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ public class Verify extends AbstractCommand
7878
description = "Do a quick check - avoid reading all data to verify checksums")
7979
private boolean quick = false;
8080

81+
@Option(paramLabel = "sai_only",
82+
names = { "-s", "--sai-only"},
83+
description = "Verify only sai index")
84+
private boolean onlySai = false;
85+
86+
@Option(paramLabel = "include_sai",
87+
names = { "-i", "--include-sai"},
88+
description = "Include SAI index verification along with data files")
89+
private boolean includeSai = false;
90+
8191
@Override
8292
public void execute(NodeProbe probe)
8393
{
@@ -89,6 +99,9 @@ public void execute(NodeProbe probe)
8999
System.exit(1);
90100
}
91101

102+
if (onlySai && includeSai)
103+
throw new IllegalArgumentException("Cannot specify both --sai-only and --include-sai");
104+
92105
List<String> keyspaces = parseOptionalKeyspace(args, probe);
93106
String[] tableNames = parseOptionalTables(args);
94107

@@ -103,7 +116,7 @@ public void execute(NodeProbe probe)
103116
{
104117
try
105118
{
106-
probe.verify(out, extendedVerify, checkVersion, diskFailurePolicy, mutateRepairStatus, checkOwnsTokens, quick, keyspace, tableNames);
119+
probe.verify(out, extendedVerify, checkVersion, diskFailurePolicy, mutateRepairStatus, checkOwnsTokens, quick, onlySai, includeSai, keyspace, tableNames);
107120
} catch (Exception e)
108121
{
109122
throw new RuntimeException("Error occurred during verifying", e);

test/resources/nodetool/help/verify

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ SYNOPSIS
77
[(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]
88
[(-u <username> | --username <username>)] verify
99
[(-c | --check-version)] [(-d | --dfp)] [(-e | --extended-verify)]
10-
[(-f | --force)] [(-q | --quick)] [(-r | --rsc)]
11-
[(-t | --check-tokens)] [--] [<keyspace> <tables>...]
10+
[(-f | --force)] [(-i | --include-sai)] [(-q | --quick)] [(-r | --rsc)]
11+
[(-s | --sai-only)] [(-t | --check-tokens)] [--] [<keyspace>
12+
<tables>...]
1213

1314
OPTIONS
1415
-c, --check-version
@@ -26,6 +27,9 @@ OPTIONS
2627
-h <host>, --host <host>
2728
Node hostname or ip address
2829

30+
-i, --include-sai
31+
Include SAI index verification along with data files
32+
2933
-p <port>, --port <port>
3034
Remote jmx agent port number
3135

@@ -44,6 +48,9 @@ OPTIONS
4448
-r, --rsc
4549
Mutate the repair status on corrupt sstables
4650

51+
-s, --sai-only
52+
Verify only sai index
53+
4754
-t, --check-tokens
4855
Verify that all tokens in sstables are owned by this node
4956

0 commit comments

Comments
 (0)