-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
autorepair v2 framework (against 4.1.6) #3367
base: cassandra-4.1
Are you sure you want to change the base?
autorepair v2 framework (against 4.1.6) #3367
Conversation
a81e10d
to
0e36ed5
Compare
@@ -552,7 +554,8 @@ private static void addTableParamsToRowBuilder(TableParams params, Row.SimpleBui | |||
.add("compaction", params.compaction.asMap()) | |||
.add("compression", params.compression.asMap()) | |||
.add("read_repair", params.readRepair.toString()) | |||
.add("extensions", params.extensions); | |||
.add("extensions", params.extensions) | |||
.add("disabled_automated_repair", params.disableAutomatedRepair); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 thanks for doing this. I imagine it could be useful to be able to configure things more granularly. I have some cases where we want to run full repair on a table, but never incremental (the main use case is analytics based where etl processes are loading a bunch of SSTables every few hours). Will come back to this as I review and experiment more with the PR, if there is a bunch of things that we would want configurable at table level maybe we could consider adding something like a RepairParams, otherwise maybe this is fine as is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be overkill and some of the options may not be applicable, but wonder if we could just have a single field that functions similarly to AutoRepairConfig.repair_type_overrides
:
So one could do something like:
ALTER TABLE x.y WITH automated_repair={'incremental': {'enabled': false}};
Most of the repair_type_overrides
aren't really applicable at a table level, so maybe we'd just have a separate class like AutoRepairParams
that functioned similarly to CompactionParams
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 thanks for doing this. I imagine it could be useful to be able to configure things more granularly. I have some cases where we want to run full repair on a table, but never incremental (the main use case is analytics based where etl processes are loading a bunch of SSTables every few hours). Will come back to this as I review and experiment more with the PR, if there is a bunch of things that we would want configurable at table level maybe we could consider adding something like a RepairParams, otherwise maybe this is fine as is.
Sure, makes sense. We can finalize all the configuration granularity that we want at a table level and then change it accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I incorporated this comment. Now, we have more granular configuration support as follows:
AND automated_repair_full = {'enabled': 'true'}
AND automated_repair_incremental = {'enabled': 'true'};
Please note: I had to use two separate configurations instead of merging due to the limitation of the CQL grammar, which does not allow nested configuration for DDL statements.
AND automated_repair = {'full': {'enabled': 'true'}, 'incremental': {'enabled': 'true'}}
public void setAutoRepairEnabled(RepairType repairType, boolean enabled) | ||
{ | ||
if (enabled && repairType == RepairType.incremental && | ||
(DatabaseDescriptor.getMaterializedViewsEnabled() || DatabaseDescriptor.isCDCEnabled())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think for a first pass this is ok, but eventually would like to see this apply more at a table label; Tables that have/are views would be skipped for incremental, and also those that have CDC enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, we can provide a more granular table-level configuration with multiple configuration knobs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done - please see above.
// this function will return the default value if the row doesn't have that column or the column data is null | ||
// This function is used to avoid the nullpointerexception | ||
public long getLongOrDefault(String column, long defaultValue) { | ||
if (!has(column)){ | ||
return defaultValue; | ||
} | ||
return getLong(column); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: there are some existing methods (getInt
, getUUID
) that are overloaded with an additional ifNull
parameter, for consistency we should do the same for getLong.
// this function will return the default value if the row doesn't have that column or the column data is null | |
// This function is used to avoid the nullpointerexception | |
public long getLongOrDefault(String column, long defaultValue) { | |
if (!has(column)){ | |
return defaultValue; | |
} | |
return getLong(column); | |
} | |
public long getLong(String column, long ifNull) { | |
ByteBuffer bytes = data.get(column); | |
return bytes == null ? ifNull : LongType.instance.compose(bytes); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: there are some existing methods (
getInt
,getUUID
) that are overloaded with an additionalifNull
parameter, for consistency we should do the same for getLong.
ACK - will update it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
"UPDATE %s.%s SET %s = %s + ? WHERE %s = ? AND %s = ?", SchemaConstants.AUTO_REPAIR_KEYSPACE_NAME, | ||
AutoRepairKeyspace.AUTO_REPAIR_PRIORITY, COL_REPAIR_PRIORITY, COL_REPAIR_PRIORITY, COL_REPAIR_TYPE, COL_PID); | ||
|
||
final static String INSERT_NEW_REPAIR_HISTORY = String.format( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there precedence for using Lightweight transactions in internal cassandra stuff? Looking at how this is used, I see we check to see if the CAS insert was applied, but that just effects logging, so I think there is little harm in removing IF NOT EXISTS
and just let it overwrite.
COL_HOST_ID, COL_REPAIR_START_TS, COL_REPAIR_FINISH_TS, COL_DELETE_HOSTS, COL_DELETE_HOSTS_UPDATE_TIME); | ||
|
||
final static String ADD_HOST_ID_TO_DELETE_HOSTS = String.format( | ||
"UPDATE %s.%s SET %s = %s + ?, %s = ? WHERE %s = ? AND %s = ? AND %s = ? IF EXISTS" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to INSERT_NEW_REPAIR_HISTORY
, we can just do a blind delete here (no need for IF EXISTS), if it doesn't exist it'll just create a row tombstone. Given how this is only executed when we detect the host isn't in the ring seems fine to just do that.
*/ | ||
public class AutoRepairMetrics | ||
{ | ||
public Gauge<Integer> repairsInProgress; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic to have these out of the gate 👍. We should add some docs to metrics.adoc
to document these.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic to have these out of the gate 👍. We should add some docs to
metrics.adoc
to document these.
ACK. Will extend the documentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorporated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
{ | ||
Options opts = new Options(); | ||
|
||
opts.enabled = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing worth discussing; by default I noticed that if I set:
auto_repair:
enabled: true
That it doesn't actually run repairs because by default both full and incremental repair are disabled. Is this how we want this to work? I think it's a bit subjective and I don't have a good answer, but felt it was worth discussing.
I think two possible good defaults would be:
- Keep it as-is, if you want to enable repair, you should have to choose which of the repair options you would want enabled by default.
- Full repair is enabled with the above config.
I think maybe what we should just do is go with option one and add a commented section in cassandra.yaml that looks something like this:
# Documentation about auto repair goes here
# auto_repair:
# # Enables auto repair, defaults to false (disabled)
# enabled: true
# repair_type_overrides:
# full:
# # Enables auto full repairs, defaults to false
# enabled: true
# # ... all other various property defaults here
# incremental:
# enabled: false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, by default, we should just enable full repair. For the IR, we should let an operator decide whether they want to enable or not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that seems sensible to me 👍. I'm not sure whether or not this would get too tricky, but I could see that the 'defaults' for overrides could be contextual based on the kind of repair, e.g. I would want a more frequent min_repair_interval_in_hours
for Incremental Repair than for Full Repair. I think that may be too involved though, I think it's ok to just special case full repair to be enabled by default if you have auto_repair.enabled: true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I changed the min_repair_interval_in_hours
to DurationSpec.IntSecondsBound
so we can now go up to the second level of granularity.
|
||
public class AutoRepairStateFactory | ||
{ | ||
public static AutoRepairState getAutoRepairState(RepairType repairType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small preference: It could be better to just put this on RepairType
, can just define a AutoRepairState getAutoRepairState()
on the RepairType
enum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
public volatile Options global_settings; | ||
|
||
public enum RepairType | ||
{full, incremental} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the way you did this. Seems like if I wanted to in theory add a new repair type to be scheduled (e.g. 'Preview repairs on repaired set', 'Preview repairs on unrepaired set', 'Paxos repairs'). I'd just have to add a new enum here, and then implement a AutoRepairState
for it and add it to the AutoRepairStateFactory
? Nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to be the case! That was incredibly easy!
git diff
diff --git a/src/java/org/apache/cassandra/repair/autorepair/AutoRepairConfig.java b/src/java/org/apache/cassandra/repair/autorepair/AutoRepairConfig.java
index 731d95a8dd..03d3b240c5 100644
--- a/src/java/org/apache/cassandra/repair/autorepair/AutoRepairConfig.java
+++ b/src/java/org/apache/cassandra/repair/autorepair/AutoRepairConfig.java
@@ -46,7 +46,7 @@ public class AutoRepairConfig implements Serializable
public volatile Options global_settings;
public enum RepairType
- {full, incremental}
+ {full, incremental, preview_repaired}
// repair_type_overrides overrides the global_settings for a specific repair type
public volatile Map<RepairType, Options> repair_type_overrides = new EnumMap<>(RepairType.class);
diff --git a/src/java/org/apache/cassandra/repair/autorepair/AutoRepairState.java b/src/java/org/apache/cassandra/repair/autorepair/AutoRepairState.java
index fc0281a7f0..ebda85c52a 100644
--- a/src/java/org/apache/cassandra/repair/autorepair/AutoRepairState.java
+++ b/src/java/org/apache/cassandra/repair/autorepair/AutoRepairState.java
@@ -283,6 +283,27 @@ public abstract class AutoRepairState implements ProgressListener
}
}
+class PreviewRepairedState extends AutoRepairState
+{
+ public PreviewRepairedState()
+ {
+ super(RepairType.preview_repaired);
+ }
+
+
+ @Override
+ public RepairRunnable getRepairRunnable(String keyspace, List<String> tables, Set<Range<Token>> ranges, boolean primaryRangeOnly)
+ {
+ RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, false, false,
+ AutoRepairService.instance.getAutoRepairConfig().getRepairThreads(repairType), ranges,
+ !ranges.isEmpty(), false, false, PreviewKind.REPAIRED, false, true, false, false);
+
+ option.getColumnFamilies().addAll(tables);
+
+ return getRepairRunnable(keyspace, option);
+ }
+}
+
class IncrementalRepairState extends AutoRepairState
{
public IncrementalRepairState()
diff --git a/src/java/org/apache/cassandra/repair/autorepair/AutoRepairStateFactory.java b/src/java/org/apache/cassandra/repair/autorepair/AutoRepairStateFactory.java
index 3812ed6e4d..8c3ba373db 100644
--- a/src/java/org/apache/cassandra/repair/autorepair/AutoRepairStateFactory.java
+++ b/src/java/org/apache/cassandra/repair/autorepair/AutoRepairStateFactory.java
@@ -30,6 +30,8 @@ public class AutoRepairStateFactory
return new FullRepairState();
case incremental:
return new IncrementalRepairState();
+ case preview_repaired:
+ return new PreviewRepairedState();
}
throw new IllegalArgumentException("Invalid repair type: " + repairType);
INFO [Thread-12] 2024-06-20 14:02:32,047 RepairRunnable.java:324 - Starting repair command #11 (a5a3fffa-2f37-11ef-a8c2-e106f7a22c03), repairing keyspace easy_cass_stress with repair options (parallelism: parallel, primary range: true, incremental: false, job threads: 1, ColumnFamilies: [keyvalue], dataCenters: [], hosts: [], previewKind: REPAIRED, # of ranges: 1, pull r
epair: false, force repair: false, optimise streams: false, ignore unreplicated keyspaces: true, repairPaxos: false, paxosOnly: false)
case "subranges": | ||
probe.setRepairSubRangeNum(repairType, Integer.parseInt(paramVal)); | ||
break; | ||
case "minrepairintervalinhours": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I just stumbled into is that the formatting of multi-word config here is different than the yaml configuration, e.g. minrepairinternvalinhours
in the yaml is min_repair_internval_in_hours
. Can we update the nodetool config to match the yaml one?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, will do
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@VisibleForTesting | ||
protected static final Options defaultOptions = getDefaultOptions(); | ||
|
||
public Options() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: implement toString()
to return all the options like a map, otherwise system_views.settings
can't read these:
auto_repair.repair_type_overrides | {incremental=org.apache.cassandra.repair.autorepair.AutoRepairConfig$Options@64af73a1, preview_repaired=org.apache.cassandra.repair.autorepair.AutoRepairConfig$Options@2ae56cae}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
public static KeyspaceMetadata metadata() | ||
{ | ||
Tables tables = Tables.of(AutoRepairHistory, AutoRepairPriority); | ||
return KeyspaceMetadata.create(SchemaConstants.AUTO_REPAIR_KEYSPACE_NAME, KeyspaceParams.simple(1), tables); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should do something similar we did for other system keyspaces in CassandraRelevantProperties
:
SYSTEM_AUTH_DEFAULT_RF("cassandra.system_auth.default_rf", "1"),
SYSTEM_TRACES_DEFAULT_RF("cassandra.system_traces.default_rf", "2"),
SYSTEM_DISTRIBUTED_DEFAULT_RF("cassandra.system_distributed.default_rf", "3"),
which is used like:
KeyspaceParams.simple(Math.max(DEFAULT_RF, DatabaseDescriptor.getDefaultKeyspaceRF()))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
although, i wonder if we should just leverage system_distributed
instead of creating a separate keyspace? This may reduce some operator burden. e.g. in our cluster bring up one of the first things we do is alter these keyspaces to network topology strategy and we also have allowlist/blocklist for system keyspaces that we'd have to update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Moved these two tables under system_distributed
itself.
@Override | ||
public RepairRunnable getRepairRunnable(String keyspace, List<String> tables, Set<Range<Token>> ranges, boolean primaryRangeOnly) | ||
{ | ||
RepairOption option = new RepairOption(RepairParallelism.PARALLEL, primaryRangeOnly, true, false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussion point: We are currently passing false
for optimiseStreams
. This is a nice enhancement in the form of CASSANDRA-3200
which cuts down the amount of overstreaming when inconsistencies are found.
I'm not sure it has a lot of precedence for use, but it's been in tree for a long time. We've only started to look into rolling it out ourselves.
I think we should consider setting it to true here, or making it configurable somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, changed it to true
@@ -57,7 +57,8 @@ public enum Option | |||
ADDITIONAL_WRITE_POLICY, | |||
CRC_CHECK_CHANCE, | |||
CDC, | |||
READ_REPAIR; | |||
READ_REPAIR, | |||
DISABLED_AUTOMATED_REPAIR; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: from a naming, having AUTOMATED_REPAIR: true
vs DISABLED_AUTOMATED_REPAIR: false
is easier for people to reason. Avoiding the double negatives and extra boolean logic for what people expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK - will update it accordingly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
// certain data centers. We can run multiple node repairs at the same time. | ||
// For example, if the value is “dc1,dc2|dc3|dc4|dc5,dc6,dc7”, there will be 4 groups {dc1, dc2}, {dc3}, {dc4} and {dc5, dc6, dc7}. | ||
// This means we can run repair parallely on 4 nodes, each in one group. | ||
public volatile Set<String> repair_dc_groups; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a global setting, I am not sure this is safe or necessary. Other keyspaces not in an island configuration to a single DC will not get repaired correctly (e.g., system_distributed, system_auth). For single keyspaces that are not replicated, there are already checks on a node if it is not replicated for a keyspace in checkNodeContainsKeyspaceReplica of AutoRepairUtils. Could simplify things a little bit and get rid of the pid in schema?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it is not necessary. I have removed it.
// enable/disable auto repair globally, overrides all other settings. Cannot be modified dynamically. | ||
public final Boolean enabled; | ||
// the interval in seconds between checks for eligible repair operations. Cannot be modified dynamically. | ||
public final Integer repair_check_interval_in_sec = 300; // 5 minutes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is kinda across all of these settings, but if look at Config.java . Can see now after 4.x that we for time settings instead of having _in_sec just pass a duration type. Same goes for using the mib options intead of _mb etc. Should probably stick with this kinda thing for most of the config options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the configs to match with the 4.x by using the DurationSpec.*
e91c424
to
ee68ae2
Compare
eea7a90
to
d189975
Compare
d189975
to
1eed60d
Compare
@tolbertam @clohfink I've incorporated all the review comments; please review the updated PR. Thanks! |
@@ -105,6 +112,13 @@ private TableParams(Builder builder) | |||
extensions = builder.extensions; | |||
cdc = builder.cdc; | |||
readRepair = builder.readRepair; | |||
automatedRepair = new HashMap<>() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this breaks java8 builds, cant use <> on anonymous inner classes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
automatedRepair = new EnumMap<>(AutoRepairConfig.RepairType.class);
automatedRepair.put(AutoRepairConfig.RepairType.full, builder.automatedRepairFull);
automatedRepair.put(AutoRepairConfig.RepairType.incremental, builder.automatedRepairIncremental);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorporated
@@ -84,12 +89,14 @@ public String toString() | |||
public final boolean cdc; | |||
public final ReadRepairStrategy readRepair; | |||
|
|||
public final Map<AutoRepairConfig.RepairType, AutoRepairParams> automatedRepair; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can change to EnumMap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorporated
// this function will be called when a node bootstrap finished | ||
UUID hostId = Gossiper.instance.getHostId(FBUtilities.getBroadcastAddressAndPort()); | ||
// insert the data first | ||
insertNewRepairHistory(repairType, System.currentTimeMillis(), System.currentTimeMillis()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fails checkstyles, use org.apache.cassandra.utils.Clock.Global or org.apache.cassandra.utils.Clock interface
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorporated
config.getRepairByKeyspace(repairType) ? tablesToBeRepaired : ImmutableList.of(tableName), | ||
ranges, primaryRangeOnly); | ||
repairState.resetWaitCondition(); | ||
new Thread(NamedThreadFactory.createAnonymousThread(new FutureTask<>(task, null))).start(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fails checkstyles requirements. Should create a single thread pool for this or reuse one of existing ones
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorporated
} | ||
|
||
// repair runs a repair session of the given type synchronously. | ||
public void repair(AutoRepairConfig.RepairType repairType, long millisToWait) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NP: maybe simplify this method a little breaking it up, its very long and deep
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Chopped it a bit - see if this looks better now
import org.mockito.MockitoAnnotations; | ||
|
||
import static org.apache.cassandra.Util.setAutoRepairEnabled; | ||
import static org.apache.cassandra.Util.token; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused import breaks build
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorporated
import org.junit.runner.RunWith; | ||
import org.junit.runners.Parameterized; | ||
|
||
import junit.framework.Assert; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change to org.junit.Assert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also in AutoRepairTest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorporated
f5f7c6f
to
1e4abe2
Compare
1e4abe2
to
d9e0703
Compare
Major work
More details to this design discussion doc
Sample configuration to enable the AutoRepair framework
nodetool command to view the current configuration
nodetool getautorepairconfig