mirror of https://github.com/apache/cassandra
Migrate threshold for minimum keyspace replication factor to guardrails
patch by Savni Nagarkar; reviewed by Andres de la Peña, David Capwell for CASSANDRA-17212
This commit is contained in:
parent
bb3749f2bb
commit
da7c662227
|
|
@ -1,4 +1,5 @@
|
|||
4.1
|
||||
* Migrate threshold for minimum keyspace replication factor to guardrails (CASSANDRA-17212)
|
||||
* Add guardrail to disallow TRUNCATE and DROP TABLE commands (CASSANDRA-17558)
|
||||
* Add plugin support for CQLSH (CASSANDRA-16456)
|
||||
* Add guardrail to disallow querying with ALLOW FILTERING (CASSANDRA-17370)
|
||||
|
|
|
|||
1
NEWS.txt
1
NEWS.txt
|
|
@ -79,6 +79,7 @@ New features
|
|||
- Allowed write consistency levels.
|
||||
- Collections size.
|
||||
- Query page size.
|
||||
- Minimum replication factor.
|
||||
- Data disk usage, defined either as a percentage or as an absolute size.
|
||||
- Whether user-defined timestamps are allowed.
|
||||
- Whether GROUP BY queries are allowed.
|
||||
|
|
|
|||
|
|
@ -1449,11 +1449,6 @@ compaction_tombstone_warning_threshold: 100000
|
|||
# Suggested value for use in production: 3
|
||||
# default_keyspace_rf: 1
|
||||
|
||||
# The minimum allowable replication factor. Creating a keyspace with a replication factor less than this value will be rejected.
|
||||
# This would also apply to system keyspaces.
|
||||
# Suggested value for use in production: 2 or higher
|
||||
# minimum_keyspace_rf: 0
|
||||
|
||||
# Track a metric per keyspace indicating whether replication achieved the ideal consistency
|
||||
# level for writes without timing out. This is different from the consistency level requested by
|
||||
# each write which may be lower in order to facilitate availability.
|
||||
|
|
@ -1690,6 +1685,11 @@ drop_compact_storage_enabled: false
|
|||
# Valid values are in [1, max available disk size of all data directories].
|
||||
# Defaults to null to disable and use the physically available disk size of data directories during calculations.
|
||||
# data_disk_usage_max_disk_size:
|
||||
# Guardrail to warn or fail when the minimum replication factor is lesser than threshold.
|
||||
# This would also apply to system keyspaces.
|
||||
# Suggested value for use in production: 2 or higher
|
||||
# minimum_replication_factor_warn_threshold: -1
|
||||
# minimum_replication_factor_fail_threshold: -1
|
||||
|
||||
# Startup Checks are executed as part of Cassandra startup process, not all of them
|
||||
# are configurable (so you can disable them) but these which are enumerated bellow.
|
||||
|
|
|
|||
|
|
@ -636,10 +636,9 @@ public class Config
|
|||
|
||||
public volatile boolean diagnostic_events_enabled = false;
|
||||
|
||||
// Default and minimum keyspace replication factors allow validation of newly created keyspaces
|
||||
// Default keyspace replication factors allow validation of newly created keyspaces
|
||||
// and good defaults if no replication factor is provided by the user
|
||||
public volatile int default_keyspace_rf = 1;
|
||||
public volatile int minimum_keyspace_rf = 0;
|
||||
|
||||
/**
|
||||
* flags for enabling tracking repaired state of data during reads
|
||||
|
|
@ -823,6 +822,8 @@ public class Config
|
|||
public volatile int data_disk_usage_percentage_warn_threshold = -1;
|
||||
public volatile int data_disk_usage_percentage_fail_threshold = -1;
|
||||
public volatile DataStorageSpec data_disk_usage_max_disk_size = null;
|
||||
public volatile int minimum_replication_factor_warn_threshold = -1;
|
||||
public volatile int minimum_replication_factor_fail_threshold = -1;
|
||||
|
||||
public volatile DurationSpec streaming_state_expires = DurationSpec.inDays(3);
|
||||
public volatile DataStorageSpec streaming_state_size = DataStorageSpec.inMebibytes(40);
|
||||
|
|
|
|||
|
|
@ -868,10 +868,10 @@ public class DatabaseDescriptor
|
|||
|
||||
validateMaxConcurrentAutoUpgradeTasksConf(conf.max_concurrent_automatic_sstable_upgrades);
|
||||
|
||||
if (conf.default_keyspace_rf < conf.minimum_keyspace_rf)
|
||||
if (conf.default_keyspace_rf < conf.minimum_replication_factor_fail_threshold)
|
||||
{
|
||||
throw new ConfigurationException(String.format("default_keyspace_rf (%d) cannot be less than minimum_keyspace_rf (%d)",
|
||||
conf.default_keyspace_rf, conf.minimum_keyspace_rf));
|
||||
throw new ConfigurationException(String.format("default_keyspace_rf (%d) cannot be less than minimum_replication_factor_fail_threshold (%d)",
|
||||
conf.default_keyspace_rf, conf.minimum_replication_factor_fail_threshold));
|
||||
}
|
||||
|
||||
if (conf.paxos_repair_parallelism <= 0)
|
||||
|
|
@ -4081,30 +4081,14 @@ public class DatabaseDescriptor
|
|||
throw new ConfigurationException("default_keyspace_rf cannot be less than 1");
|
||||
}
|
||||
|
||||
if (value < getMinimumKeyspaceRF())
|
||||
if (value < guardrails.getMinimumReplicationFactorFailThreshold())
|
||||
{
|
||||
throw new ConfigurationException(String.format("default_keyspace_rf to be set (%d) cannot be less than minimum_keyspace_rf (%d)", value, getMinimumKeyspaceRF()));
|
||||
throw new ConfigurationException(String.format("default_keyspace_rf to be set (%d) cannot be less than minimum_replication_factor_fail_threshold (%d)", value, guardrails.getMinimumReplicationFactorFailThreshold()));
|
||||
}
|
||||
|
||||
conf.default_keyspace_rf = value;
|
||||
}
|
||||
|
||||
public static int getMinimumKeyspaceRF() { return conf.minimum_keyspace_rf; }
|
||||
|
||||
public static void setMinimumKeyspaceRF(int value) throws ConfigurationException
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
throw new ConfigurationException("minimum_keyspace_rf cannot be negative");
|
||||
}
|
||||
|
||||
if (value > getDefaultKeyspaceRF())
|
||||
{
|
||||
throw new ConfigurationException(String.format("minimum_keyspace_rf to be set (%d) cannot be greater than default_keyspace_rf (%d)", value, getDefaultKeyspaceRF()));
|
||||
}
|
||||
|
||||
conf.minimum_keyspace_rf = value;
|
||||
}
|
||||
|
||||
public static boolean getUseStatementsEnabled()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,27 +61,27 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
public GuardrailsOptions(Config config)
|
||||
{
|
||||
this.config = config;
|
||||
validateIntThreshold(config.keyspaces_warn_threshold, config.keyspaces_fail_threshold, "keyspaces");
|
||||
validateIntThreshold(config.tables_warn_threshold, config.tables_fail_threshold, "tables");
|
||||
validateIntThreshold(config.columns_per_table_warn_threshold, config.columns_per_table_fail_threshold, "columns_per_table");
|
||||
validateIntThreshold(config.secondary_indexes_per_table_warn_threshold, config.secondary_indexes_per_table_fail_threshold, "secondary_indexes_per_table");
|
||||
validateIntThreshold(config.materialized_views_per_table_warn_threshold, config.materialized_views_per_table_fail_threshold, "materialized_views_per_table");
|
||||
validateMaxIntThreshold(config.keyspaces_warn_threshold, config.keyspaces_fail_threshold, "keyspaces");
|
||||
validateMaxIntThreshold(config.tables_warn_threshold, config.tables_fail_threshold, "tables");
|
||||
validateMaxIntThreshold(config.columns_per_table_warn_threshold, config.columns_per_table_fail_threshold, "columns_per_table");
|
||||
validateMaxIntThreshold(config.secondary_indexes_per_table_warn_threshold, config.secondary_indexes_per_table_fail_threshold, "secondary_indexes_per_table");
|
||||
validateMaxIntThreshold(config.materialized_views_per_table_warn_threshold, config.materialized_views_per_table_fail_threshold, "materialized_views_per_table");
|
||||
config.table_properties_warned = validateTableProperties(config.table_properties_warned, "table_properties_warned");
|
||||
config.table_properties_ignored = validateTableProperties(config.table_properties_ignored, "table_properties_ignored");
|
||||
config.table_properties_disallowed = validateTableProperties(config.table_properties_disallowed, "table_properties_disallowed");
|
||||
validateIntThreshold(config.page_size_warn_threshold, config.page_size_fail_threshold, "page_size");
|
||||
validateIntThreshold(config.partition_keys_in_select_warn_threshold,
|
||||
config.partition_keys_in_select_fail_threshold, "partition_keys_in_select");
|
||||
validateIntThreshold(config.in_select_cartesian_product_warn_threshold, config.in_select_cartesian_product_fail_threshold, "in_select_cartesian_product");
|
||||
validateMaxIntThreshold(config.page_size_warn_threshold, config.page_size_fail_threshold, "page_size");
|
||||
validateMaxIntThreshold(config.partition_keys_in_select_warn_threshold, config.partition_keys_in_select_fail_threshold, "partition_keys_in_select");
|
||||
validateMaxIntThreshold(config.in_select_cartesian_product_warn_threshold, config.in_select_cartesian_product_fail_threshold, "in_select_cartesian_product");
|
||||
config.read_consistency_levels_warned = validateConsistencyLevels(config.read_consistency_levels_warned, "read_consistency_levels_warned");
|
||||
config.read_consistency_levels_disallowed = validateConsistencyLevels(config.read_consistency_levels_disallowed, "read_consistency_levels_disallowed");
|
||||
config.write_consistency_levels_warned = validateConsistencyLevels(config.write_consistency_levels_warned, "write_consistency_levels_warned");
|
||||
config.write_consistency_levels_disallowed = validateConsistencyLevels(config.write_consistency_levels_disallowed, "write_consistency_levels_disallowed");
|
||||
validateSizeThreshold(config.collection_size_warn_threshold, config.collection_size_fail_threshold, false, "collection_size");
|
||||
validateIntThreshold(config.items_per_collection_warn_threshold, config.items_per_collection_fail_threshold, "items_per_collection");
|
||||
validateIntThreshold(config.fields_per_udt_warn_threshold, config.fields_per_udt_fail_threshold, "fields_per_udt");
|
||||
validateMaxIntThreshold(config.items_per_collection_warn_threshold, config.items_per_collection_fail_threshold, "items_per_collection");
|
||||
validateMaxIntThreshold(config.fields_per_udt_warn_threshold, config.fields_per_udt_fail_threshold, "fields_per_udt");
|
||||
validatePercentageThreshold(config.data_disk_usage_percentage_warn_threshold, config.data_disk_usage_percentage_fail_threshold, "data_disk_usage_percentage");
|
||||
validateDataDiskUsageMaxDiskSize(config.data_disk_usage_max_disk_size);
|
||||
validateMinRFThreshold(config.minimum_replication_factor_warn_threshold, config.minimum_replication_factor_fail_threshold, "minimum_replication_factor");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -98,7 +98,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setKeyspacesThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "keyspaces");
|
||||
validateMaxIntThreshold(warn, fail, "keyspaces");
|
||||
updatePropertyWithLogging("keyspaces_warn_threshold",
|
||||
warn,
|
||||
() -> config.keyspaces_warn_threshold,
|
||||
|
|
@ -123,7 +123,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setTablesThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "tables");
|
||||
validateMaxIntThreshold(warn, fail, "tables");
|
||||
updatePropertyWithLogging("tables_warn_threshold",
|
||||
warn,
|
||||
() -> config.tables_warn_threshold,
|
||||
|
|
@ -148,7 +148,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setColumnsPerTableThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "columns_per_table");
|
||||
validateMaxIntThreshold(warn, fail, "columns_per_table");
|
||||
updatePropertyWithLogging("columns_per_table_warn_threshold",
|
||||
warn,
|
||||
() -> config.columns_per_table_warn_threshold,
|
||||
|
|
@ -173,7 +173,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setSecondaryIndexesPerTableThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "secondary_indexes_per_table");
|
||||
validateMaxIntThreshold(warn, fail, "secondary_indexes_per_table");
|
||||
updatePropertyWithLogging("secondary_indexes_per_table_warn_threshold",
|
||||
warn,
|
||||
() -> config.secondary_indexes_per_table_warn_threshold,
|
||||
|
|
@ -204,7 +204,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setPartitionKeysInSelectThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "partition_keys_in_select");
|
||||
validateMaxIntThreshold(warn, fail, "partition_keys_in_select");
|
||||
updatePropertyWithLogging("partition_keys_in_select_warn_threshold",
|
||||
warn,
|
||||
() -> config.partition_keys_in_select_warn_threshold,
|
||||
|
|
@ -223,7 +223,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setMaterializedViewsPerTableThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "materialized_views_per_table");
|
||||
validateMaxIntThreshold(warn, fail, "materialized_views_per_table");
|
||||
updatePropertyWithLogging("materialized_views_per_table_warn_threshold",
|
||||
warn,
|
||||
() -> config.materialized_views_per_table_warn_threshold,
|
||||
|
|
@ -248,7 +248,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setPageSizeThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "page_size");
|
||||
validateMaxIntThreshold(warn, fail, "page_size");
|
||||
updatePropertyWithLogging("page_size_warn_threshold",
|
||||
warn,
|
||||
() -> config.page_size_warn_threshold,
|
||||
|
|
@ -427,7 +427,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setInSelectCartesianProductThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "in_select_cartesian_product");
|
||||
validateMaxIntThreshold(warn, fail, "in_select_cartesian_product");
|
||||
updatePropertyWithLogging("in_select_cartesian_product_warn_threshold",
|
||||
warn,
|
||||
() -> config.in_select_cartesian_product_warn_threshold,
|
||||
|
|
@ -534,7 +534,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setItemsPerCollectionThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "items_per_collection");
|
||||
validateMaxIntThreshold(warn, fail, "items_per_collection");
|
||||
updatePropertyWithLogging("items_per_collection_warn_threshold",
|
||||
warn,
|
||||
() -> config.items_per_collection_warn_threshold,
|
||||
|
|
@ -559,7 +559,7 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
|
||||
public void setFieldsPerUDTThreshold(int warn, int fail)
|
||||
{
|
||||
validateIntThreshold(warn, fail, "fields_per_udt");
|
||||
validateMaxIntThreshold(warn, fail, "fields_per_udt");
|
||||
updatePropertyWithLogging("fields_per_udt_warn_threshold",
|
||||
warn,
|
||||
() -> config.fields_per_udt_warn_threshold,
|
||||
|
|
@ -609,6 +609,31 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
x -> config.data_disk_usage_max_disk_size = x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumReplicationFactorWarnThreshold()
|
||||
{
|
||||
return config.minimum_replication_factor_warn_threshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumReplicationFactorFailThreshold()
|
||||
{
|
||||
return config.minimum_replication_factor_fail_threshold;
|
||||
}
|
||||
|
||||
public void setMinimumReplicationFactorThreshold(int warn, int fail)
|
||||
{
|
||||
validateMinRFThreshold(warn, fail, "minimum_replication_factor");
|
||||
updatePropertyWithLogging("minimum_replication_factor_warn_threshold",
|
||||
warn,
|
||||
() -> config.minimum_replication_factor_warn_threshold,
|
||||
x -> config.minimum_replication_factor_warn_threshold = x);
|
||||
updatePropertyWithLogging("minimum_replication_factor_fail_threshold",
|
||||
fail,
|
||||
() -> config.minimum_replication_factor_fail_threshold,
|
||||
x -> config.minimum_replication_factor_fail_threshold = x);
|
||||
}
|
||||
|
||||
private static <T> void updatePropertyWithLogging(String propertyName, T newValue, Supplier<T> getter, Consumer<T> setter)
|
||||
{
|
||||
T oldValue = getter.get();
|
||||
|
|
@ -643,18 +668,31 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
validatePositiveNumeric(value, 100, name);
|
||||
}
|
||||
|
||||
private static void validateIntThreshold(int warn, int fail, String name)
|
||||
private static void validatePercentageThreshold(int warn, int fail, String name)
|
||||
{
|
||||
validatePercentage(warn, name + "_warn_threshold");
|
||||
validatePercentage(fail, name + "_fail_threshold");
|
||||
validateWarnLowerThanFail(warn, fail, name);
|
||||
}
|
||||
|
||||
private static void validateMaxIntThreshold(int warn, int fail, String name)
|
||||
{
|
||||
validatePositiveNumeric(warn, Integer.MAX_VALUE, name + "_warn_threshold");
|
||||
validatePositiveNumeric(fail, Integer.MAX_VALUE, name + "_fail_threshold");
|
||||
validateWarnLowerThanFail(warn, fail, name);
|
||||
}
|
||||
|
||||
private static void validatePercentageThreshold(int warn, int fail, String name)
|
||||
private static void validateMinIntThreshold(int warn, int fail, String name)
|
||||
{
|
||||
validatePercentage(warn, name + "_warn_threshold");
|
||||
validatePercentage(fail, name + "_fail_threshold");
|
||||
validateWarnLowerThanFail(warn, fail, name);
|
||||
validatePositiveNumeric(warn, Integer.MAX_VALUE, name + "_warn_threshold");
|
||||
validatePositiveNumeric(fail, Integer.MAX_VALUE, name + "_fail_threshold");
|
||||
validateWarnGreaterThanFail(warn, fail, name);
|
||||
}
|
||||
|
||||
private static void validateMinRFThreshold(int warn, int fail, String name)
|
||||
{
|
||||
validateMinIntThreshold(warn, fail, name);
|
||||
validateMinRFVersusDefaultRF(fail, name);
|
||||
}
|
||||
|
||||
private static void validateWarnLowerThanFail(long warn, long fail, String name)
|
||||
|
|
@ -667,6 +705,25 @@ public class GuardrailsOptions implements GuardrailsConfig
|
|||
"than the fail threshold %d", warn, name, fail));
|
||||
}
|
||||
|
||||
private static void validateWarnGreaterThanFail(long warn, long fail, String name)
|
||||
{
|
||||
if (warn == -1 || fail == -1)
|
||||
return;
|
||||
|
||||
if (fail > warn)
|
||||
throw new IllegalArgumentException(format("The warn threshold %d for %s_warn_threshold should be greater " +
|
||||
"than the fail threshold %d", warn, name, fail));
|
||||
}
|
||||
|
||||
private static void validateMinRFVersusDefaultRF(int fail, String name) throws IllegalArgumentException
|
||||
{
|
||||
if (fail > DatabaseDescriptor.getDefaultKeyspaceRF())
|
||||
{
|
||||
throw new IllegalArgumentException(String.format("%s_fail_threshold to be set (%d) cannot be greater than default_keyspace_rf (%d)",
|
||||
name, fail, DatabaseDescriptor.getDefaultKeyspaceRF()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateSize(DataStorageSpec size, boolean allowZero, String name)
|
||||
{
|
||||
if (size == null)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public final class AlterKeyspaceStatement extends AlterSchemaStatement
|
|||
if (newKeyspace.params.replication.klass.equals(LocalStrategy.class))
|
||||
throw ire("Unable to use given strategy class: LocalStrategy is reserved for internal use.");
|
||||
|
||||
newKeyspace.params.validate(keyspaceName);
|
||||
newKeyspace.params.validate(keyspaceName, state);
|
||||
|
||||
validateNoRangeMovements();
|
||||
validateTransientReplication(keyspace.createReplicationStrategy(), newKeyspace.createReplicationStrategy());
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public final class CreateKeyspaceStatement extends AlterSchemaStatement
|
|||
if (keyspace.params.replication.klass.equals(LocalStrategy.class))
|
||||
throw ire("Unable to use given strategy class: LocalStrategy is reserved for internal use.");
|
||||
|
||||
keyspace.params.validate(keyspaceName);
|
||||
keyspace.params.validate(keyspaceName, state);
|
||||
return schema.withAddedOrUpdated(keyspace);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,51 +55,51 @@ public final class Guardrails implements GuardrailsMBean
|
|||
/**
|
||||
* Guardrail on the total number of user keyspaces.
|
||||
*/
|
||||
public static final Threshold keyspaces =
|
||||
new Threshold("keyspaces",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getKeyspacesWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getKeyspacesFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating keyspace %s, current number of keyspaces %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Cannot have more than %s keyspaces, aborting the creation of keyspace %s",
|
||||
threshold, what));
|
||||
public static final MaxThreshold keyspaces =
|
||||
new MaxThreshold("keyspaces",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getKeyspacesWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getKeyspacesFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating keyspace %s, current number of keyspaces %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Cannot have more than %s keyspaces, aborting the creation of keyspace %s",
|
||||
threshold, what));
|
||||
|
||||
/**
|
||||
* Guardrail on the total number of tables on user keyspaces.
|
||||
*/
|
||||
public static final Threshold tables =
|
||||
new Threshold("tables",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablesWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablesFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating table %s, current number of tables %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Cannot have more than %s tables, aborting the creation of table %s",
|
||||
threshold, what));
|
||||
public static final MaxThreshold tables =
|
||||
new MaxThreshold("tables",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablesWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablesFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating table %s, current number of tables %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Cannot have more than %s tables, aborting the creation of table %s",
|
||||
threshold, what));
|
||||
|
||||
/**
|
||||
* Guardrail on the number of columns per table.
|
||||
*/
|
||||
public static final Threshold columnsPerTable =
|
||||
new Threshold("columns_per_table",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("The table %s has %s columns, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Tables cannot have more than %s columns, but %s provided for table %s",
|
||||
threshold, value, what));
|
||||
public static final MaxThreshold columnsPerTable =
|
||||
new MaxThreshold("columns_per_table",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("The table %s has %s columns, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Tables cannot have more than %s columns, but %s provided for table %s",
|
||||
threshold, value, what));
|
||||
|
||||
public static final Threshold secondaryIndexesPerTable =
|
||||
new Threshold("secondary_indexes_per_table",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating secondary index %s, current number of indexes %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Tables cannot have more than %s secondary indexes, aborting the creation of secondary index %s",
|
||||
threshold, what));
|
||||
public static final MaxThreshold secondaryIndexesPerTable =
|
||||
new MaxThreshold("secondary_indexes_per_table",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating secondary index %s, current number of indexes %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Tables cannot have more than %s secondary indexes, aborting the creation of secondary index %s",
|
||||
threshold, what));
|
||||
|
||||
/**
|
||||
* Guardrail disabling user's ability to create secondary indexes
|
||||
|
|
@ -112,15 +112,15 @@ public final class Guardrails implements GuardrailsMBean
|
|||
/**
|
||||
* Guardrail on the number of materialized views per table.
|
||||
*/
|
||||
public static final Threshold materializedViewsPerTable =
|
||||
new Threshold("materialized_views_per_table",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating materialized view %s, current number of views %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Tables cannot have more than %s materialized views, aborting the creation of materialized view %s",
|
||||
threshold, what));
|
||||
public static final MaxThreshold materializedViewsPerTable =
|
||||
new MaxThreshold("materialized_views_per_table",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Creating materialized view %s, current number of views %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Tables cannot have more than %s materialized views, aborting the creation of materialized view %s",
|
||||
threshold, what));
|
||||
|
||||
/**
|
||||
* Guardrail warning about, ignoring or rejecting the usage of certain table properties.
|
||||
|
|
@ -169,30 +169,30 @@ public final class Guardrails implements GuardrailsMBean
|
|||
/**
|
||||
* Guardrail on the number of elements returned within page.
|
||||
*/
|
||||
public static final Threshold pageSize =
|
||||
new Threshold("page_size",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPageSizeWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPageSizeFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Query for table %s with page size %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Aborting query for table %s, page size %s exceeds fail threshold of %s.",
|
||||
what, value, threshold));
|
||||
public static final MaxThreshold pageSize =
|
||||
new MaxThreshold("page_size",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPageSizeWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPageSizeFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Query for table %s with page size %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Aborting query for table %s, page size %s exceeds fail threshold of %s.",
|
||||
what, value, threshold));
|
||||
|
||||
/**
|
||||
* Guardrail on the number of partition keys in the IN clause.
|
||||
*/
|
||||
public static final Threshold partitionKeysInSelect =
|
||||
new Threshold("partition_keys_in_select",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Query with partition keys in IN clause on table %s, with number of " +
|
||||
"partition keys %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Aborting query with partition keys in IN clause on table %s, " +
|
||||
"number of partition keys %s exceeds fail threshold of %s.",
|
||||
what, value, threshold));
|
||||
public static final MaxThreshold partitionKeysInSelect =
|
||||
new MaxThreshold("partition_keys_in_select",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Query with partition keys in IN clause on table %s, with number of " +
|
||||
"partition keys %s exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Aborting query with partition keys in IN clause on table %s, " +
|
||||
"number of partition keys %s exceeds fail threshold of %s.",
|
||||
what, value, threshold));
|
||||
|
||||
/**
|
||||
* Guardrail disabling operations on lists that require read before write.
|
||||
|
|
@ -213,17 +213,17 @@ public final class Guardrails implements GuardrailsMBean
|
|||
/**
|
||||
* Guardrail on the number of restrictions created by a cartesian product of a CQL's {@code IN} query.
|
||||
*/
|
||||
public static final Threshold inSelectCartesianProduct =
|
||||
new Threshold("in_select_cartesian_product",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("The cartesian product of the IN restrictions on %s produces %s values, " +
|
||||
"this exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Aborting query because the cartesian product of the IN restrictions on %s " +
|
||||
"produces %s values, this exceeds fail threshold of %s.",
|
||||
what, value, threshold));
|
||||
public static final MaxThreshold inSelectCartesianProduct =
|
||||
new MaxThreshold("in_select_cartesian_product",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("The cartesian product of the IN restrictions on %s produces %s values, " +
|
||||
"this exceeds warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Aborting query because the cartesian product of the IN restrictions on %s " +
|
||||
"produces %s values, this exceeds fail threshold of %s.",
|
||||
what, value, threshold));
|
||||
|
||||
/**
|
||||
* Guardrail on read consistency levels.
|
||||
|
|
@ -248,41 +248,41 @@ public final class Guardrails implements GuardrailsMBean
|
|||
/**
|
||||
* Guardrail on the size of a collection.
|
||||
*/
|
||||
public static final Threshold collectionSize =
|
||||
new Threshold("collection_size",
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeWarnThreshold()),
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeFailThreshold()),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Detected collection %s of size %s, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Detected collection %s of size %s, this exceeds the failure threshold of %s.",
|
||||
what, value, threshold));
|
||||
public static final MaxThreshold collectionSize =
|
||||
new MaxThreshold("collection_size",
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeWarnThreshold()),
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeFailThreshold()),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Detected collection %s of size %s, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Detected collection %s of size %s, this exceeds the failure threshold of %s.",
|
||||
what, value, threshold));
|
||||
|
||||
/**
|
||||
* Guardrail on the number of items of a collection.
|
||||
*/
|
||||
public static final Threshold itemsPerCollection =
|
||||
new Threshold("items_per_collection",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Detected collection %s with %s items, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Detected collection %s with %s items, this exceeds the failure threshold of %s.",
|
||||
what, value, threshold));
|
||||
public static final MaxThreshold itemsPerCollection =
|
||||
new MaxThreshold("items_per_collection",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("Detected collection %s with %s items, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("Detected collection %s with %s items, this exceeds the failure threshold of %s.",
|
||||
what, value, threshold));
|
||||
|
||||
/**
|
||||
* Guardrail on the number of fields on each UDT.
|
||||
*/
|
||||
public static final Threshold fieldsPerUDT =
|
||||
new Threshold("fields_per_udt",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("The user type %s has %s columns, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("User types cannot have more than %s columns, but %s provided for user type %s.",
|
||||
threshold, value, what));
|
||||
public static final MaxThreshold fieldsPerUDT =
|
||||
new MaxThreshold("fields_per_udt",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("The user type %s has %s columns, this exceeds the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("User types cannot have more than %s columns, but %s provided for user type %s.",
|
||||
threshold, value, what));
|
||||
|
||||
/**
|
||||
* Guardrail on the data disk usage on the local node, used by a periodic task to calculate and propagate that status.
|
||||
|
|
@ -320,6 +320,19 @@ public final class Guardrails implements GuardrailsMBean
|
|||
replicaDiskUsage.minNotifyIntervalInMs(minNotifyInterval);
|
||||
}
|
||||
|
||||
/**
|
||||
* Guardrail on the minimum replication factor.
|
||||
*/
|
||||
public static final MinThreshold minimumReplicationFactor =
|
||||
new MinThreshold("minimum_replication_factor",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMinimumReplicationFactorWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMinimumReplicationFactorFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
isWarning ? format("The keyspace %s has a replication factor of %s, below the warning threshold of %s.",
|
||||
what, value, threshold)
|
||||
: format("The keyspace %s has a replication factor of %s, below the failure threshold of %s.",
|
||||
what, value, threshold));
|
||||
|
||||
private Guardrails()
|
||||
{
|
||||
MBeanWrapper.instance.registerMBean(this, MBEAN_NAME);
|
||||
|
|
@ -835,6 +848,24 @@ public final class Guardrails implements GuardrailsMBean
|
|||
DEFAULT_CONFIG.setDataDiskUsageMaxDiskSize(sizeFromString(size));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumReplicationFactorWarnThreshold()
|
||||
{
|
||||
return DEFAULT_CONFIG.getMinimumReplicationFactorWarnThreshold();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumReplicationFactorFailThreshold()
|
||||
{
|
||||
return DEFAULT_CONFIG.getMinimumReplicationFactorFailThreshold();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMinimumReplicationFactorThreshold(int warn, int fail)
|
||||
{
|
||||
DEFAULT_CONFIG.setMinimumReplicationFactorThreshold(warn, fail);
|
||||
}
|
||||
|
||||
private static String toCSV(Set<String> values)
|
||||
{
|
||||
return values == null || values.isEmpty() ? "" : String.join(",", values);
|
||||
|
|
|
|||
|
|
@ -266,4 +266,15 @@ public interface GuardrailsConfig
|
|||
*/
|
||||
@Nullable
|
||||
DataStorageSpec getDataDiskUsageMaxDiskSize();
|
||||
|
||||
/**
|
||||
* @return The threshold to warn when replication factor is lesser than threshold.
|
||||
*/
|
||||
int getMinimumReplicationFactorWarnThreshold();
|
||||
|
||||
/**
|
||||
* @return The threshold to fail when replication factor is lesser than threshold.
|
||||
*/
|
||||
int getMinimumReplicationFactorFailThreshold();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -520,4 +520,23 @@ public interface GuardrailsMBean
|
|||
* A {@code null} value means disabled.
|
||||
*/
|
||||
void setDataDiskUsageMaxDiskSize(@Nullable String size);
|
||||
|
||||
/**
|
||||
* @return The threshold to warn when replication factor is lesser threshold.
|
||||
*/
|
||||
int getMinimumReplicationFactorWarnThreshold();
|
||||
|
||||
/**
|
||||
* @return The threshold to fail when replication factor is lesser threshold.
|
||||
*/
|
||||
int getMinimumReplicationFactorFailThreshold();
|
||||
|
||||
/**
|
||||
* @param warn the threshold to warn when the minimum replication factor is lesser than
|
||||
* threshold -1 means disabled.
|
||||
* @param fail the threshold to fail when the minimum replication factor is lesser than
|
||||
* threshold -1 means disabled.
|
||||
*/
|
||||
void setMinimumReplicationFactorThreshold (int warn, int fail);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.db.guardrails;
|
||||
|
||||
import java.util.function.ToLongFunction;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
||||
/**
|
||||
* {@link MaxThreshold} for maximum guardrails, the value is checked to see if it is greater than the warn and fail thresholds.
|
||||
*/
|
||||
public class MaxThreshold extends Threshold
|
||||
{
|
||||
/**
|
||||
* Creates a new threshold guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param warnThreshold a {@link ClientState}-based provider of the value above which a warning should be triggered.
|
||||
* @param failThreshold a {@link ClientState}-based provider of the value above which the operation should be aborted.
|
||||
* @param messageProvider a function to generate the warning or error message if the guardrail is triggered
|
||||
*/
|
||||
public MaxThreshold(String name,
|
||||
ToLongFunction<ClientState> warnThreshold,
|
||||
ToLongFunction<ClientState> failThreshold,
|
||||
Threshold.ErrorMessageProvider messageProvider)
|
||||
{
|
||||
super(name, warnThreshold, failThreshold, messageProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean compare(long value, long threshold)
|
||||
{
|
||||
return value > threshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long failValue(ClientState state)
|
||||
{
|
||||
long failValue = failThreshold.applyAsLong(state);
|
||||
return failValue <= 0 ? Long.MAX_VALUE : failValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long warnValue(ClientState state)
|
||||
{
|
||||
long warnValue = warnThreshold.applyAsLong(state);
|
||||
return warnValue <= 0 ? Long.MAX_VALUE : warnValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.db.guardrails;
|
||||
|
||||
import java.util.function.ToLongFunction;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
||||
/**
|
||||
* {@link MinThreshold} for minimum guardrails, the value is checked to see if it is lesser than the warn and fail thresholds.
|
||||
*/
|
||||
public class MinThreshold extends Threshold
|
||||
{
|
||||
/**
|
||||
* Creates a new minimum threshold guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param warnThreshold a {@link ClientState}-based provider of the value above which a warning should be triggered.
|
||||
* @param failThreshold a {@link ClientState}-based provider of the value above which the operation should be aborted.
|
||||
* @param messageProvider a function to generate the warning or error message if the guardrail is triggered
|
||||
*/
|
||||
public MinThreshold(String name,
|
||||
ToLongFunction<ClientState> warnThreshold,
|
||||
ToLongFunction<ClientState> failThreshold,
|
||||
Threshold.ErrorMessageProvider messageProvider)
|
||||
{
|
||||
super(name, warnThreshold, failThreshold, messageProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean compare(long value, long threshold)
|
||||
{
|
||||
return value < threshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long failValue(ClientState state)
|
||||
{
|
||||
long failValue = failThreshold.applyAsLong(state);
|
||||
return failValue <= 0 ? Long.MIN_VALUE : failValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long warnValue(ClientState state)
|
||||
{
|
||||
long warnValue = warnThreshold.applyAsLong(state);
|
||||
return warnValue <= 0 ? Long.MIN_VALUE : warnValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ import org.apache.cassandra.service.ClientState;
|
|||
* <p>
|
||||
* This works exactly as a {@link Threshold}, but provides slightly more convenient error messages for percentage
|
||||
*/
|
||||
public class PercentageThreshold extends Threshold
|
||||
public class PercentageThreshold extends MaxThreshold
|
||||
{
|
||||
/**
|
||||
* Creates a new threshold guardrail.
|
||||
|
|
|
|||
|
|
@ -26,15 +26,15 @@ import org.apache.cassandra.service.ClientState;
|
|||
/**
|
||||
* A guardrail based on numeric threshold(s).
|
||||
*
|
||||
* <p>A {@link Threshold} guardrail defines (up to) 2 thresholds, one at which a warning is issued, and a higher one
|
||||
* <p>A {@link Threshold} guardrail defines (up to) 2 thresholds, one at which a warning is issued, and a lower one
|
||||
* at which the operation is aborted with an exception. Only one of those thresholds can be activated if desired.
|
||||
*
|
||||
* <p>This guardrail only handles guarding positive values.
|
||||
*/
|
||||
public class Threshold extends Guardrail
|
||||
public abstract class Threshold extends Guardrail
|
||||
{
|
||||
private final ToLongFunction<ClientState> warnThreshold;
|
||||
private final ToLongFunction<ClientState> failThreshold;
|
||||
protected ToLongFunction<ClientState> warnThreshold;
|
||||
protected ToLongFunction<ClientState> failThreshold;
|
||||
protected final ErrorMessageProvider messageProvider;
|
||||
|
||||
/**
|
||||
|
|
@ -56,6 +56,8 @@ public class Threshold extends Guardrail
|
|||
this.messageProvider = messageProvider;
|
||||
}
|
||||
|
||||
protected abstract boolean compare(long value, long threshold);
|
||||
|
||||
protected String errMsg(boolean isWarning, String what, long value, long thresholdValue)
|
||||
{
|
||||
return messageProvider.createMessage(isWarning,
|
||||
|
|
@ -69,19 +71,10 @@ public class Threshold extends Guardrail
|
|||
return errMsg(isWarning, REDACTED, value, thresholdValue);
|
||||
}
|
||||
|
||||
private long failValue(ClientState state)
|
||||
{
|
||||
long failValue = failThreshold.applyAsLong(state);
|
||||
return failValue <= 0 ? Long.MAX_VALUE : failValue;
|
||||
}
|
||||
protected abstract long failValue(ClientState state);
|
||||
|
||||
private long warnValue(ClientState state)
|
||||
{
|
||||
long warnValue = warnThreshold.applyAsLong(state);
|
||||
return warnValue <= 0 ? Long.MAX_VALUE : warnValue;
|
||||
}
|
||||
protected abstract long warnValue(ClientState state);
|
||||
|
||||
@Override
|
||||
public boolean enabled(@Nullable ClientState state)
|
||||
{
|
||||
if (!super.enabled(state))
|
||||
|
|
@ -105,17 +98,17 @@ public class Threshold extends Guardrail
|
|||
*/
|
||||
public boolean triggersOn(long value, @Nullable ClientState state)
|
||||
{
|
||||
return enabled(state) && (value > Math.min(failValue(state), warnValue(state)));
|
||||
return enabled(state) && (compare(value, warnValue(state)) || compare(value, failValue(state)));
|
||||
}
|
||||
|
||||
public boolean warnsOn(long value, @Nullable ClientState state)
|
||||
{
|
||||
return enabled(state) && (value > warnValue(state) && value <= failValue(state));
|
||||
return enabled(state) && compare(value, warnValue(state));
|
||||
}
|
||||
|
||||
public boolean failsOn(long value, @Nullable ClientState state)
|
||||
{
|
||||
return enabled(state) && (value > failValue(state));
|
||||
return enabled(state) && compare(value, failValue(state));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -136,14 +129,14 @@ public class Threshold extends Guardrail
|
|||
return;
|
||||
|
||||
long failValue = failValue(state);
|
||||
if (value > failValue)
|
||||
if (compare(value, failValue))
|
||||
{
|
||||
triggerFail(value, failValue, what, containsUserData, state);
|
||||
return;
|
||||
}
|
||||
|
||||
long warnValue = warnValue(state);
|
||||
if (value > warnValue)
|
||||
if (compare(value, warnValue))
|
||||
triggerWarn(value, warnValue, what, containsUserData);
|
||||
}
|
||||
|
||||
|
|
@ -175,4 +168,4 @@ public class Threshold extends Guardrail
|
|||
*/
|
||||
String createMessage(boolean isWarning, String what, String value, String threshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ import org.apache.cassandra.dht.Token;
|
|||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.locator.ReplicaCollection.Builder.Conflict;
|
||||
import org.apache.cassandra.service.AbstractWriteResponseHandler;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.DatacenterSyncWriteResponseHandler;
|
||||
import org.apache.cassandra.service.DatacenterWriteResponseHandler;
|
||||
import org.apache.cassandra.service.WriteResponseHandler;
|
||||
|
|
@ -286,7 +287,17 @@ public abstract class AbstractReplicationStrategy
|
|||
|
||||
public abstract void validateOptions() throws ConfigurationException;
|
||||
|
||||
public abstract void maybeWarnOnOptions();
|
||||
@Deprecated // use #maybeWarnOnOptions(ClientState) instead
|
||||
public void maybeWarnOnOptions()
|
||||
{
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
public void maybeWarnOnOptions(ClientState state)
|
||||
{
|
||||
maybeWarnOnOptions();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The options recognized by the strategy.
|
||||
|
|
@ -384,12 +395,13 @@ public abstract class AbstractReplicationStrategy
|
|||
Class<? extends AbstractReplicationStrategy> strategyClass,
|
||||
TokenMetadata tokenMetadata,
|
||||
IEndpointSnitch snitch,
|
||||
Map<String, String> strategyOptions) throws ConfigurationException
|
||||
Map<String, String> strategyOptions,
|
||||
ClientState state) throws ConfigurationException
|
||||
{
|
||||
AbstractReplicationStrategy strategy = createInternal(keyspaceName, strategyClass, tokenMetadata, snitch, strategyOptions);
|
||||
strategy.validateExpectedOptions();
|
||||
strategy.validateOptions();
|
||||
strategy.maybeWarnOnOptions();
|
||||
strategy.maybeWarnOnOptions(state);
|
||||
if (strategy.hasTransientReplicas() && !DatabaseDescriptor.isTransientReplicationEnabled())
|
||||
{
|
||||
throw new ConfigurationException("Transient replication is disabled. Enable in cassandra.yaml to use.");
|
||||
|
|
@ -421,12 +433,7 @@ public abstract class AbstractReplicationStrategy
|
|||
try
|
||||
{
|
||||
ReplicationFactor rf = ReplicationFactor.fromString(s);
|
||||
|
||||
if (rf.fullReplicas < DatabaseDescriptor.getMinimumKeyspaceRF())
|
||||
{
|
||||
throw new ConfigurationException(String.format("Replication factor cannot be less than minimum_keyspace_rf (%d), found %d", DatabaseDescriptor.getMinimumKeyspaceRF(), rf.fullReplicas));
|
||||
}
|
||||
|
||||
|
||||
if (rf.hasTransientReplicas())
|
||||
{
|
||||
if (DatabaseDescriptor.getNumTokens() > 1)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.util.*;
|
|||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.db.guardrails.Guardrails;
|
||||
import org.apache.cassandra.locator.ReplicaCollection.Builder.Conflict;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -31,6 +32,7 @@ import org.apache.cassandra.exceptions.ConfigurationException;
|
|||
import org.apache.cassandra.dht.Token;
|
||||
import org.apache.cassandra.locator.TokenMetadata.Topology;
|
||||
import org.apache.cassandra.schema.SchemaConstants;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.ClientWarn;
|
||||
import org.apache.cassandra.service.StorageService;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
|
|
@ -336,7 +338,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy
|
|||
}
|
||||
|
||||
@Override
|
||||
public void maybeWarnOnOptions()
|
||||
public void maybeWarnOnOptions(ClientState state)
|
||||
{
|
||||
if (!SchemaConstants.isSystemKeyspace(keyspaceName))
|
||||
{
|
||||
|
|
@ -347,6 +349,7 @@ public class NetworkTopologyStrategy extends AbstractReplicationStrategy
|
|||
|
||||
String dc = e.getKey();
|
||||
ReplicationFactor rf = getReplicationFactor(dc);
|
||||
Guardrails.minimumReplicationFactor.guard(rf.fullReplicas, keyspaceName, false, state);
|
||||
int nodeCount = dcsNodes.get(dc).size();
|
||||
// nodeCount==0 on many tests
|
||||
if (rf.fullReplicas > nodeCount && nodeCount != 0)
|
||||
|
|
|
|||
|
|
@ -27,10 +27,12 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.db.guardrails.Guardrails;
|
||||
import org.apache.cassandra.dht.Range;
|
||||
import org.apache.cassandra.dht.Token;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.schema.SchemaConstants;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.ClientWarn;
|
||||
import org.apache.cassandra.service.StorageService;
|
||||
|
||||
|
|
@ -100,12 +102,13 @@ public class SimpleStrategy extends AbstractReplicationStrategy
|
|||
}
|
||||
|
||||
@Override
|
||||
public void maybeWarnOnOptions()
|
||||
public void maybeWarnOnOptions(ClientState state)
|
||||
{
|
||||
if (!SchemaConstants.isSystemKeyspace(keyspaceName))
|
||||
{
|
||||
int nodeCount = StorageService.instance.getHostIdToEndpoint().size();
|
||||
// nodeCount==0 on many tests
|
||||
Guardrails.minimumReplicationFactor.guard(rf.fullReplicas, keyspaceName, false, state);
|
||||
if (rf.fullReplicas > nodeCount && nodeCount != 0)
|
||||
{
|
||||
String msg = "Your replication factor " + rf.fullReplicas
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ public final class KeyspaceMetadata implements SchemaElement
|
|||
name));
|
||||
}
|
||||
|
||||
params.validate(name);
|
||||
params.validate(name, null);
|
||||
|
||||
tablesAndViews().forEach(TableMetadata::validate);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import com.google.common.annotations.VisibleForTesting;
|
|||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
||||
/**
|
||||
* An immutable class representing keyspace parameters (durability and replication).
|
||||
*/
|
||||
|
|
@ -89,9 +91,9 @@ public final class KeyspaceParams
|
|||
return new KeyspaceParams(true, ReplicationParams.nts(args));
|
||||
}
|
||||
|
||||
public void validate(String name)
|
||||
public void validate(String name, ClientState state)
|
||||
{
|
||||
replication.validate(name);
|
||||
replication.validate(name, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.CqlBuilder;
|
||||
import org.apache.cassandra.locator.*;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.StorageService;
|
||||
|
||||
public final class ReplicationParams
|
||||
|
|
@ -70,12 +71,12 @@ public final class ReplicationParams
|
|||
return new ReplicationParams(NetworkTopologyStrategy.class, options);
|
||||
}
|
||||
|
||||
public void validate(String name)
|
||||
public void validate(String name, ClientState state)
|
||||
{
|
||||
// Attempt to instantiate the ARS, which will throw a ConfigurationException if the options aren't valid.
|
||||
TokenMetadata tmd = StorageService.instance.getTokenMetadata();
|
||||
IEndpointSnitch eps = DatabaseDescriptor.getEndpointSnitch();
|
||||
AbstractReplicationStrategy.validateReplicationStrategy(name, klass, tmd, eps, options);
|
||||
AbstractReplicationStrategy.validateReplicationStrategy(name, klass, tmd, eps, options, state);
|
||||
}
|
||||
|
||||
public static ReplicationParams fromMap(Map<String, String> map) {
|
||||
|
|
|
|||
|
|
@ -6505,17 +6505,6 @@ public class StorageService extends NotificationBroadcasterSupport implements IE
|
|||
return DatabaseDescriptor.getDefaultKeyspaceRF();
|
||||
}
|
||||
|
||||
public void setMinimumKeyspaceReplicationFactor(int value)
|
||||
{
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(value);
|
||||
logger.info("set minimum keyspace rf to {}", value);
|
||||
}
|
||||
|
||||
public int getMinimumKeyspaceReplicationFactor()
|
||||
{
|
||||
return DatabaseDescriptor.getMinimumKeyspaceRF();
|
||||
}
|
||||
|
||||
public boolean getSkipPaxosRepairOnTopologyChange()
|
||||
{
|
||||
return DatabaseDescriptor.skipPaxosRepairOnTopologyChange();
|
||||
|
|
|
|||
|
|
@ -945,8 +945,6 @@ public interface StorageServiceMBean extends NotificationEmitter
|
|||
|
||||
public void setDefaultKeyspaceReplicationFactor(int value);
|
||||
public int getDefaultKeyspaceReplicationFactor();
|
||||
public void setMinimumKeyspaceReplicationFactor(int value);
|
||||
public int getMinimumKeyspaceReplicationFactor();
|
||||
|
||||
boolean getSkipPaxosRepairOnTopologyChange();
|
||||
void setSkipPaxosRepairOnTopologyChange(boolean v);
|
||||
|
|
|
|||
|
|
@ -2038,16 +2038,6 @@ public class NodeProbe implements AutoCloseable
|
|||
{
|
||||
return ssProxy.getDefaultKeyspaceReplicationFactor();
|
||||
}
|
||||
|
||||
public void setMinimumKeyspaceReplicationFactor(int value)
|
||||
{
|
||||
ssProxy.setMinimumKeyspaceReplicationFactor(value);
|
||||
}
|
||||
|
||||
public int getMinimumKeyspaceReplicationFactor()
|
||||
{
|
||||
return ssProxy.getMinimumKeyspaceReplicationFactor();
|
||||
}
|
||||
}
|
||||
|
||||
class ColumnFamilyStoreMBeanIterator implements Iterator<Map.Entry<String, ColumnFamilyStoreMBean>>
|
||||
|
|
|
|||
|
|
@ -144,7 +144,6 @@ public class NodeTool
|
|||
GetInterDCStreamThroughput.class,
|
||||
GetLoggingLevels.class,
|
||||
GetMaxHintWindow.class,
|
||||
GetMinimumKeyspaceRF.class,
|
||||
GetSSTables.class,
|
||||
GetSeeds.class,
|
||||
GetSnapshotThrottle.class,
|
||||
|
|
@ -204,7 +203,6 @@ public class NodeTool
|
|||
SetInterDCStreamThroughput.class,
|
||||
SetLoggingLevel.class,
|
||||
SetMaxHintWindow.class,
|
||||
SetMinimumKeyspaceRF.class,
|
||||
SetSnapshotThrottle.class,
|
||||
SetStreamThroughput.class,
|
||||
SetTimeout.class,
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* 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.nodetool;
|
||||
|
||||
import io.airlift.airline.Arguments;
|
||||
import io.airlift.airline.Command;
|
||||
import org.apache.cassandra.tools.NodeProbe;
|
||||
import org.apache.cassandra.tools.NodeTool;
|
||||
|
||||
@Command(name = "getminimumrf", description = "Gets minimum keyspace replication factor.")
|
||||
public class GetMinimumKeyspaceRF extends NodeTool.NodeToolCmd
|
||||
{
|
||||
protected void execute(NodeProbe probe)
|
||||
{
|
||||
probe.output().out.println(probe.getMinimumKeyspaceReplicationFactor());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
/*
|
||||
* 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.nodetool;
|
||||
|
||||
import io.airlift.airline.Arguments;
|
||||
import io.airlift.airline.Command;
|
||||
import org.apache.cassandra.tools.NodeProbe;
|
||||
import org.apache.cassandra.tools.NodeTool;
|
||||
|
||||
@Command(name = "setminimumrf", description = "Sets minimum keyspace replication factor.")
|
||||
public class SetMinimumKeyspaceRF extends NodeTool.NodeToolCmd
|
||||
{
|
||||
@Arguments(title = "minimum_rf", usage = "<value>", description = "Minimum replication factor", required = true)
|
||||
private Integer minimumRF = null;
|
||||
|
||||
protected void execute(NodeProbe probe)
|
||||
{
|
||||
probe.setMinimumKeyspaceReplicationFactor(minimumRF);
|
||||
}
|
||||
}
|
||||
|
|
@ -98,14 +98,11 @@ public class DatabaseDescriptorRefTest
|
|||
"org.apache.cassandra.db.guardrails.GuardrailsConfig",
|
||||
"org.apache.cassandra.db.guardrails.GuardrailsConfigMBean",
|
||||
"org.apache.cassandra.db.guardrails.GuardrailsConfig$ConsistencyLevels",
|
||||
"org.apache.cassandra.db.guardrails.GuardrailsConfig$IntThreshold",
|
||||
"org.apache.cassandra.db.guardrails.GuardrailsConfig$TableProperties",
|
||||
"org.apache.cassandra.config.GuardrailsOptions",
|
||||
"org.apache.cassandra.config.GuardrailsOptions$Config",
|
||||
"org.apache.cassandra.config.GuardrailsOptions$ConsistencyLevels",
|
||||
"org.apache.cassandra.config.GuardrailsOptions$IntThreshold",
|
||||
"org.apache.cassandra.config.GuardrailsOptions$TableProperties",
|
||||
"org.apache.cassandra.config.GuardrailsOptions$Threshold",
|
||||
"org.apache.cassandra.config.ReplicaFilteringProtectionOptions",
|
||||
"org.apache.cassandra.config.YamlConfigurationLoader",
|
||||
"org.apache.cassandra.config.YamlConfigurationLoader$PropertiesChecker",
|
||||
|
|
@ -114,8 +111,6 @@ public class DatabaseDescriptorRefTest
|
|||
"org.apache.cassandra.config.TransparentDataEncryptionOptions",
|
||||
"org.apache.cassandra.config.SubnetGroups",
|
||||
"org.apache.cassandra.config.TrackWarnings",
|
||||
"org.apache.cassandra.config.TrackWarnings$LongByteThreshold",
|
||||
"org.apache.cassandra.config.TrackWarnings$IntByteThreshold",
|
||||
"org.apache.cassandra.db.ConsistencyLevel",
|
||||
"org.apache.cassandra.db.commitlog.CommitLogSegmentManagerFactory",
|
||||
"org.apache.cassandra.db.commitlog.DefaultCommitLogSegmentMgrFactory",
|
||||
|
|
@ -124,7 +119,6 @@ public class DatabaseDescriptorRefTest
|
|||
"org.apache.cassandra.db.commitlog.CommitLogSegmentManagerStandard",
|
||||
"org.apache.cassandra.db.commitlog.CommitLog",
|
||||
"org.apache.cassandra.db.commitlog.CommitLogMBean",
|
||||
"org.apache.cassandra.db.guardrails.Threshold$Config",
|
||||
"org.apache.cassandra.db.guardrails.Values$Config",
|
||||
"org.apache.cassandra.dht.IPartitioner",
|
||||
"org.apache.cassandra.distributed.api.IInstance",
|
||||
|
|
|
|||
|
|
@ -756,24 +756,4 @@ public class DatabaseDescriptorTest
|
|||
{
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(0);
|
||||
}
|
||||
|
||||
@Test (expected = ConfigurationException.class)
|
||||
public void testInvalidSub0MinimumRFs() throws ConfigurationException
|
||||
{
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(-1);
|
||||
}
|
||||
|
||||
@Test (expected = ConfigurationException.class)
|
||||
public void testDefaultRfLessThanMinRF()
|
||||
{
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(2);
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(1);
|
||||
}
|
||||
|
||||
@Test (expected = ConfigurationException.class)
|
||||
public void testMinimumRfGreaterThanDefaultRF()
|
||||
{
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(1);
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -435,30 +435,6 @@ public class AlterTest extends CQLTester
|
|||
execute(String.format("DROP KEYSPACE IF EXISTS %s", ks6));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinimumRF() throws Throwable
|
||||
{
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(3);
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(2);
|
||||
|
||||
String ks1 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy' }");
|
||||
String ks2 = createKeyspace("CREATE KEYSPACE %s WITH replication={ 'class' : 'NetworkTopologyStrategy' }");
|
||||
|
||||
assertAlterTableThrowsException(ConfigurationException.class,
|
||||
String.format("Replication factor cannot be less than minimum_keyspace_rf (%s), found %s", DatabaseDescriptor.getMinimumKeyspaceRF(), "1"),
|
||||
String.format("ALTER KEYSPACE %s WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }", ks1));
|
||||
assertAlterTableThrowsException(ConfigurationException.class,
|
||||
String.format("Replication factor cannot be less than minimum_keyspace_rf (%s), found %s", DatabaseDescriptor.getMinimumKeyspaceRF(), "1"),
|
||||
String.format("ALTER KEYSPACE %s WITH replication={ 'class' : 'NetworkTopologyStrategy', '" + DATA_CENTER + "' : '1' }", ks2));
|
||||
|
||||
//clean up config change
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(0);
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(1);
|
||||
|
||||
//clean up keyspaces
|
||||
execute(String.format("DROP KEYSPACE IF EXISTS %s", ks1));
|
||||
execute(String.format("DROP KEYSPACE IF EXISTS %s", ks2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test {@link ConfigurationException} thrown when altering a keyspace to invalid DC option in replication configuration.
|
||||
|
|
|
|||
|
|
@ -738,26 +738,4 @@ public class CreateTest extends CQLTester
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinimumRF()
|
||||
{
|
||||
try
|
||||
{
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(3);
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(2);
|
||||
|
||||
assertThrowsConfigurationException(
|
||||
String.format("Replication factor cannot be less than minimum_keyspace_rf (%s), found %s", DatabaseDescriptor.getMinimumKeyspaceRF(), "1"),
|
||||
"CREATE KEYSPACE ks1 WITH replication={ 'class' : 'SimpleStrategy', 'replication_factor' : 1 }");
|
||||
|
||||
assertThrowsConfigurationException(
|
||||
String.format("Replication factor cannot be less than minimum_keyspace_rf (%s), found %s", DatabaseDescriptor.getMinimumKeyspaceRF(), "1"),
|
||||
"CREATE KEYSPACE ks2 WITH replication={ 'class' : 'NetworkTopologyStrategy', 'replication_factor' : 1 }");
|
||||
}
|
||||
finally
|
||||
{
|
||||
DatabaseDescriptor.setMinimumKeyspaceRF(0);
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ public class GuardrailColumnsPerTableTest extends ThresholdTester
|
|||
|
||||
private void assertCreateTableValid(String query) throws Throwable
|
||||
{
|
||||
assertThresholdValid(format(query, keyspace() + '.' + createTableName()));
|
||||
assertMaxThresholdValid(format(query, keyspace() + '.' + createTableName()));
|
||||
}
|
||||
|
||||
private void assertDropColumnValid(String query) throws Throwable
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ public class GuardrailKeyspacesTest extends ThresholdTester
|
|||
private String assertCreateKeyspaceValid() throws Throwable
|
||||
{
|
||||
String keyspaceName = createKeyspaceName();
|
||||
assertThresholdValid(createKeyspaceQuery(keyspaceName));
|
||||
assertMaxThresholdValid(createKeyspaceQuery(keyspaceName));
|
||||
return keyspaceName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* 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.db.guardrails;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.ServerTestUtils;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.db.Keyspace;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.locator.AbstractEndpointSnitch;
|
||||
import org.apache.cassandra.locator.IEndpointSnitch;
|
||||
import org.apache.cassandra.locator.InetAddressAndPort;
|
||||
import org.apache.cassandra.locator.Replica;
|
||||
import org.apache.cassandra.service.ClientWarn;
|
||||
import org.apache.cassandra.service.StorageService;
|
||||
import org.assertj.core.api.Assertions;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
public class GuardrailMinimumReplicationFactorTest extends ThresholdTester
|
||||
{
|
||||
private static final int MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD = 4;
|
||||
private static int MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD = 1;
|
||||
private static final int DEFAULT_REPLICATION_FACTOR = 2;
|
||||
private static final int DISABLED_GUARDRAIL = -1;
|
||||
private static final String WHAT = "minimum_replication_factor";
|
||||
private static final String DATACENTER1 = "datacenter1";
|
||||
private static final String KS = "ks";
|
||||
private final TriConsumer<Guardrails, Integer, Integer> setter;
|
||||
|
||||
public GuardrailMinimumReplicationFactorTest()
|
||||
{
|
||||
super(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD,
|
||||
MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD,
|
||||
Guardrails.minimumReplicationFactor,
|
||||
Guardrails::setMinimumReplicationFactorThreshold,
|
||||
Guardrails::getMinimumReplicationFactorWarnThreshold,
|
||||
Guardrails::getMinimumReplicationFactorFailThreshold);
|
||||
|
||||
this.setter = Guardrails::setMinimumReplicationFactorThreshold;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setupTest() throws Throwable
|
||||
{
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(DEFAULT_REPLICATION_FACTOR);
|
||||
MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD = 2;
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanupTest() throws Throwable
|
||||
{
|
||||
execute("DROP KEYSPACE IF EXISTS ks");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long currentValue()
|
||||
{
|
||||
return Long.parseLong((Keyspace.open(KS).getReplicationStrategy()).configOptions.get(DATACENTER1));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getWarnings()
|
||||
{
|
||||
List<String> warnings = ClientWarn.instance.getWarnings();
|
||||
|
||||
return warnings == null
|
||||
? Collections.emptyList()
|
||||
: warnings.stream()
|
||||
.filter(w -> !w.contains("keyspace ks is higher than the number of nodes 1 for datacenter") &&
|
||||
!w.contains("When increasing replication factor you need to run a full (-full) repair to distribute the data") &&
|
||||
!w.contains("keyspace ks is higher than the number of nodes") &&
|
||||
!w.contains("Your replication factor 4 for keyspace ks is higher than the number of nodes 2 for datacenter datacenter2"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfigValidation()
|
||||
{
|
||||
assertNotNull(guardrail);
|
||||
setter.accept(guardrails(), DISABLED_GUARDRAIL, DISABLED_GUARDRAIL);
|
||||
|
||||
assertInvalidPositiveIntProperty((g, a) -> setter.accept(g, DISABLED_GUARDRAIL, a), Integer.MIN_VALUE, Integer.MAX_VALUE, WHAT + "_fail_threshold");
|
||||
assertInvalidPositiveIntProperty((g, a) -> setter.accept(g, DISABLED_GUARDRAIL, a), -2, Integer.MAX_VALUE, WHAT + "_fail_threshold");
|
||||
assertValidProperty((g, a) -> setter.accept(g, DISABLED_GUARDRAIL, a), DISABLED_GUARDRAIL);
|
||||
assertInvalidPositiveIntProperty((g, a) -> setter.accept(g, DISABLED_GUARDRAIL, a), 0, Integer.MAX_VALUE, WHAT + "_fail_threshold");
|
||||
assertValidProperty((g, a) -> setter.accept(g, DISABLED_GUARDRAIL, a), 1);
|
||||
assertValidProperty((g, a) -> setter.accept(g, DISABLED_GUARDRAIL, a), 2);
|
||||
|
||||
assertInvalidPositiveIntProperty((g, w) -> setter.accept(g, w, DISABLED_GUARDRAIL), Integer.MIN_VALUE, Integer.MAX_VALUE, WHAT + "_warn_threshold");
|
||||
assertInvalidPositiveIntProperty((g, w) -> setter.accept(g, w, DISABLED_GUARDRAIL), -2, Integer.MAX_VALUE, WHAT + "_warn_threshold");
|
||||
assertValidProperty((g, w) -> setter.accept(g, w, DISABLED_GUARDRAIL), DISABLED_GUARDRAIL);
|
||||
assertInvalidPositiveIntProperty((g, w) -> setter.accept(g, w, DISABLED_GUARDRAIL), 0, Integer.MAX_VALUE, WHAT + "_warn_threshold");
|
||||
assertValidProperty((g, w) -> setter.accept(g, w, DISABLED_GUARDRAIL), 1);
|
||||
assertValidProperty((g, w) -> setter.accept(g, w, DISABLED_GUARDRAIL), 2);
|
||||
|
||||
Assertions.assertThatThrownBy(() -> setter.accept(guardrails(), 1, 2))
|
||||
.hasMessageContaining(guardrail.name + "_warn_threshold should be greater than the fail threshold");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFDisabled() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(DISABLED_GUARDRAIL, DISABLED_GUARDRAIL);
|
||||
assertMinThresholdValid("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 1}");
|
||||
assertMinThresholdValid("ALTER KEYSPACE ks WITH replication = { 'class' : 'NetworkTopologyStrategy', 'datacenter1': 3}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleStrategy() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
assertWarns("CREATE KEYSPACE ks WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': 3}",
|
||||
format("The keyspace %s has a replication factor of 3, below the warning threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD));
|
||||
assertFails("ALTER KEYSPACE ks WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': 1}",
|
||||
format("The keyspace %s has a replication factor of 1, below the failure threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleDatacenter() throws Throwable
|
||||
{
|
||||
IEndpointSnitch snitch = DatabaseDescriptor.getEndpointSnitch();
|
||||
DatabaseDescriptor.setEndpointSnitch(new AbstractEndpointSnitch()
|
||||
{
|
||||
public static final String RACK1 = ServerTestUtils.RACK1;
|
||||
|
||||
@Override
|
||||
public String getRack(InetAddressAndPort endpoint) { return RACK1; }
|
||||
|
||||
@Override
|
||||
public String getDatacenter(InetAddressAndPort endpoint) { return "datacenter2"; }
|
||||
|
||||
@Override
|
||||
public int compareEndpoints(InetAddressAndPort target, Replica a1, Replica a2) { return 0; }
|
||||
});
|
||||
|
||||
List<String> twoWarnings = Arrays.asList(format("The keyspace %s has a replication factor of 2, below the warning threshold of %d.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD),
|
||||
format("The keyspace %s has a replication factor of 2, below the warning threshold of %d.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD));
|
||||
|
||||
StorageService.instance.getTokenMetadata().updateHostId(UUID.randomUUID(), InetAddressAndPort.getByName("127.0.0.255"));
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
assertValid("CREATE KEYSPACE ks WITH replication = { 'class' : 'NetworkTopologyStrategy', 'datacenter1': 4, 'datacenter2' : 4 };");
|
||||
assertWarns("ALTER KEYSPACE ks WITH replication = { 'class' : 'NetworkTopologyStrategy', 'datacenter1': 4, 'datacenter2' : 2 };",
|
||||
format("The keyspace %s has a replication factor of 2, below the warning threshold of %d.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD));
|
||||
assertWarns("ALTER KEYSPACE ks WITH replication = { 'class' : 'NetworkTopologyStrategy', 'datacenter1': 2, 'datacenter2' : 2 };", twoWarnings);
|
||||
assertFails("ALTER KEYSPACE ks WITH replication = { 'class' : 'NetworkTopologyStrategy', 'datacenter1': 4, 'datacenter2' : 1 };",
|
||||
format("The keyspace %s has a replication factor of 1, below the failure threshold of %d.", KS, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD));
|
||||
assertFails("CREATE KEYSPACE ks1 WITH replication = { 'class' : 'NetworkTopologyStrategy', 'datacenter1': 1, 'datacenter2' : 1 };",
|
||||
format("The keyspace ks1 has a replication factor of 1, below the failure threshold of %d.", MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD));
|
||||
|
||||
DatabaseDescriptor.setEndpointSnitch(snitch);
|
||||
execute("DROP KEYSPACE IF EXISTS ks1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFOnlyWarnAbove() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, DISABLED_GUARDRAIL);
|
||||
assertMinThresholdValid("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 6}");
|
||||
assertMinThresholdValid("ALTER KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 5}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFOnlyWarnBelow() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, DISABLED_GUARDRAIL);
|
||||
assertWarns("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 3}",
|
||||
format("The keyspace %s has a replication factor of 3, below the warning threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD));
|
||||
assertWarns("ALTER KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 2}",
|
||||
format("The keyspace %s has a replication factor of 2, below the warning threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFOnlyFailAbove() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(DISABLED_GUARDRAIL, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
assertMinThresholdValid("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 4}");
|
||||
assertMinThresholdValid("ALTER KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 2}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFOnlyFailBelow() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(DISABLED_GUARDRAIL, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
assertFails("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 1}",
|
||||
format("The keyspace %s has a replication factor of 1, below the failure threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFOnlyFailBelowAlter() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(DISABLED_GUARDRAIL, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
execute("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 3}");
|
||||
assertFails("ALTER KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 1}",
|
||||
format("The keyspace %s has a replication factor of 1, below the failure threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFWarnAbove() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
assertMinThresholdValid("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 6}");
|
||||
assertMinThresholdValid("ALTER KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 5}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFWarnFailBetween() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
assertWarns("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 3}",
|
||||
format("The keyspace %s has a replication factor of 3, below the warning threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD));
|
||||
assertWarns("ALTER KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 2}",
|
||||
format("The keyspace %s has a replication factor of 2, below the warning threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFFailBelow() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
assertFails("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 1}",
|
||||
format("The keyspace %s has a replication factor of 1, below the failure threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinKeyspaceRFFailBelowAlter() throws Throwable
|
||||
{
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
execute("CREATE KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 4}");
|
||||
assertFails("ALTER KEYSPACE ks WITH replication = { 'class': 'NetworkTopologyStrategy', 'datacenter1': 1}",
|
||||
format("The keyspace %s has a replication factor of 1, below the failure threshold of %s.", KS, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinRFGreaterThanDefaultRF()
|
||||
{
|
||||
try
|
||||
{
|
||||
DatabaseDescriptor.setDefaultKeyspaceRF(1);
|
||||
guardrails().setMinimumReplicationFactorThreshold(MINIMUM_REPLICATION_FACTOR_WARN_THRESHOLD, MINIMUM_REPLICATION_FACTOR_FAIL_THRESHOLD);
|
||||
}
|
||||
catch (ConfigurationException e)
|
||||
{
|
||||
String expectedMessage = "";
|
||||
|
||||
if(guardrails().getMinimumReplicationFactorFailThreshold() > DatabaseDescriptor.getDefaultKeyspaceRF())
|
||||
expectedMessage = format("%s_fail_threshold to be set (%d) cannot be greater than default_keyspace_rf (%d)",
|
||||
WHAT, guardrails().getMinimumReplicationFactorFailThreshold(), DatabaseDescriptor.getDefaultKeyspaceRF());
|
||||
Assertions.assertThat(e.getMessage()).contains(expectedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -92,7 +92,7 @@ public class GuardrailSecondaryIndexesPerTable extends ThresholdTester
|
|||
|
||||
private void assertCreateIndexSucceeds(String column, String indexName) throws Throwable
|
||||
{
|
||||
assertThresholdValid(format("CREATE INDEX %s ON %s.%s(%s)", indexName, keyspace(), currentTable(), column));
|
||||
assertMaxThresholdValid(format("CREATE INDEX %s ON %s.%s(%s)", indexName, keyspace(), currentTable(), column));
|
||||
}
|
||||
|
||||
private void assertCreateIndexWarns(String column, String indexName) throws Throwable
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public class GuardrailTablesTest extends ThresholdTester
|
|||
private String assertCreateTableValid() throws Throwable
|
||||
{
|
||||
String tableName = createTableName();
|
||||
assertThresholdValid(createTableQuery(tableName));
|
||||
assertMaxThresholdValid(createTableQuery(tableName));
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@ public abstract class GuardrailTester extends CQLTester
|
|||
assertTrue(format("Expect no warning messages but got %s", warnings), warnings.isEmpty());
|
||||
}
|
||||
|
||||
private List<String> getWarnings()
|
||||
protected List<String> getWarnings()
|
||||
{
|
||||
List<String> warnings = ClientWarn.instance.getWarnings();
|
||||
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ public class GuardrailViewsPerTableTest extends ThresholdTester
|
|||
private String assertCreateViewSucceeds() throws Throwable
|
||||
{
|
||||
String viewName = createViewName();
|
||||
assertThresholdValid(format(CREATE_VIEW, viewName));
|
||||
assertMaxThresholdValid(format(CREATE_VIEW, viewName));
|
||||
return viewName;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public class GuardrailsConfigProviderTest extends GuardrailTester
|
|||
{
|
||||
String name = getClass().getCanonicalName() + '$' + CustomProvider.class.getSimpleName();
|
||||
GuardrailsConfigProvider provider = GuardrailsConfigProvider.build(name);
|
||||
Threshold guard = new Threshold("test_guardrail",
|
||||
MaxThreshold guard = new MaxThreshold("test_guardrail",
|
||||
state -> provider.getOrCreate(state).getTablesWarnThreshold(),
|
||||
state -> provider.getOrCreate(state).getTablesFailThreshold(),
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s > %s",
|
||||
|
|
|
|||
|
|
@ -39,12 +39,6 @@ public class GuardrailsTest extends GuardrailTester
|
|||
{
|
||||
public static final int DISABLED = -1;
|
||||
|
||||
@Test
|
||||
public void testDisabledThreshold() throws Throwable
|
||||
{
|
||||
Threshold.ErrorMessageProvider errorMessageProvider = (isWarn, what, v, t) -> "Should never trigger";
|
||||
testDisabledThreshold(new Threshold("x", state -> DISABLED, state -> DISABLED, errorMessageProvider));
|
||||
}
|
||||
|
||||
private void testDisabledThreshold(Threshold guard) throws Throwable
|
||||
{
|
||||
|
|
@ -61,9 +55,16 @@ public class GuardrailsTest extends GuardrailTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testThreshold() throws Throwable
|
||||
public void testDisabledMaxThreshold() throws Throwable
|
||||
{
|
||||
Threshold guard = new Threshold("x",
|
||||
Threshold.ErrorMessageProvider errorMessageProvider = (isWarn, what, v, t) -> "Should never trigger";
|
||||
testDisabledThreshold(new MaxThreshold("x", state -> DISABLED, state -> DISABLED, errorMessageProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxThreshold() throws Throwable
|
||||
{
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> 10,
|
||||
state -> 100,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s > %s",
|
||||
|
|
@ -87,9 +88,9 @@ public class GuardrailsTest extends GuardrailTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testWarnOnlyThreshold() throws Throwable
|
||||
public void testWarnOnlyMaxThreshold() throws Throwable
|
||||
{
|
||||
Threshold guard = new Threshold("x",
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> 10,
|
||||
state -> DISABLED,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s > %s",
|
||||
|
|
@ -105,9 +106,9 @@ public class GuardrailsTest extends GuardrailTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testFailOnlyThreshold() throws Throwable
|
||||
public void testFailOnlyMaxThreshold() throws Throwable
|
||||
{
|
||||
Threshold guard = new Threshold("x",
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> DISABLED,
|
||||
state -> 10,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s > %s",
|
||||
|
|
@ -123,9 +124,9 @@ public class GuardrailsTest extends GuardrailTester
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testThresholdUsers() throws Throwable
|
||||
public void testMaxThresholdUsers() throws Throwable
|
||||
{
|
||||
Threshold guard = new Threshold("x",
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> 10,
|
||||
state -> 100,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s > %s",
|
||||
|
|
@ -151,6 +152,104 @@ public class GuardrailsTest extends GuardrailTester
|
|||
assertValid(() -> guard.guard(101, "z", false, superClientState));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisabledMinThreshold() throws Throwable
|
||||
{
|
||||
Threshold.ErrorMessageProvider errorMessageProvider = (isWarn, what, v, t) -> "Should never trigger";
|
||||
testDisabledThreshold(new MinThreshold("x", state -> DISABLED, state -> DISABLED, errorMessageProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinThreshold() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
state -> 100,
|
||||
state -> 10,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
isWarn ? "Warning" : "Aborting", what, v, t));
|
||||
|
||||
assertTrue(guard.enabled(userClientState));
|
||||
|
||||
assertValid(() -> guard.guard(200, "Z", false, userClientState));
|
||||
assertWarns(() -> guard.guard(25, "A", false, userClientState), "Warning: for A, 25 < 100");
|
||||
assertWarns(() -> guard.guard(10, "B", false, userClientState), "Warning: for B, 10 < 100");
|
||||
assertFails(() -> guard.guard(9, "X", false, userClientState), "Aborting: for X, 9 < 10");
|
||||
assertFails(() -> guard.guard(1, "Y", false, userClientState), "Aborting: for Y, 1 < 10");
|
||||
assertValid(() -> guard.guard(200, "Z", false, userClientState));
|
||||
|
||||
assertValid(() -> guard.guard(200, "Z", true, userClientState));
|
||||
assertWarns(() -> guard.guard(25, "A", true, userClientState), "Warning: for A, 25 < 100", "Warning: for <redacted>, 25 < 100");
|
||||
assertWarns(() -> guard.guard(10, "B", true, userClientState), "Warning: for B, 10 < 100", "Warning: for <redacted>, 10 < 100");
|
||||
assertFails(() -> guard.guard(9, "X", true, userClientState), "Aborting: for X, 9 < 10", "Aborting: for <redacted>, 9 < 10");
|
||||
assertFails(() -> guard.guard(1, "Y", true, userClientState), "Aborting: for Y, 1 < 10", "Aborting: for <redacted>, 1 < 10");
|
||||
assertValid(() -> guard.guard(200, "Z", true, userClientState));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWarnOnlyMinThreshold() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
state -> 10,
|
||||
state -> DISABLED,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
isWarn ? "Warning" : "Aborting", what, v, t));
|
||||
|
||||
assertTrue(guard.enabled(userClientState));
|
||||
|
||||
assertValid(() -> guard.guard(11, "Z", false, userClientState));
|
||||
assertWarns(() -> guard.guard(5, "A", false, userClientState), "Warning: for A, 5 < 10");
|
||||
|
||||
assertValid(() -> guard.guard(11, "Z", true, userClientState));
|
||||
assertWarns(() -> guard.guard(5, "A", true, userClientState), "Warning: for A, 5 < 10", "Warning: for <redacted>, 5 < 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailOnlyMinThreshold() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
state -> DISABLED,
|
||||
state -> 10,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
isWarn ? "Warning" : "Aborting", what, v, t));
|
||||
|
||||
assertTrue(guard.enabled(userClientState));
|
||||
|
||||
assertValid(() -> guard.guard(11, "Z", false, userClientState));
|
||||
assertFails(() -> guard.guard(5, "A", false, userClientState), "Aborting: for A, 5 < 10");
|
||||
|
||||
assertValid(() -> guard.guard(11, "Z", true, userClientState));
|
||||
assertFails(() -> guard.guard(5, "A", true, userClientState), "Aborting: for A, 5 < 10", "Aborting: for <redacted>, 5 < 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinThresholdUsers() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
state -> 100,
|
||||
state -> 10,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
isWarn ? "Warning" : "Failure", what, v, t));
|
||||
|
||||
// value above both thresholds
|
||||
assertValid(() -> guard.guard(200, "x", false, null));
|
||||
assertValid(() -> guard.guard(200, "x", false, userClientState));
|
||||
assertValid(() -> guard.guard(200, "x", false, systemClientState));
|
||||
assertValid(() -> guard.guard(200, "x", false, superClientState));
|
||||
|
||||
// value under warning threshold
|
||||
assertWarns(() -> guard.guard(10, "y", false, null), "Warning: for y, 10 < 100");
|
||||
assertWarns(() -> guard.guard(10, "y", false, userClientState), "Warning: for y, 10 < 100");
|
||||
assertValid(() -> guard.guard(10, "y", false, systemClientState));
|
||||
assertValid(() -> guard.guard(10, "y", false, superClientState));
|
||||
|
||||
// value under fail threshold. An undefined user means that the check comes from a background process, so we
|
||||
// still emit failure messages and events, but we don't throw an exception to prevent interrupting that process.
|
||||
assertFails(() -> guard.guard(9, "z", false, null), false, "Failure: for z, 9 < 10");
|
||||
assertFails(() -> guard.guard(9, "z", false, userClientState), "Failure: for z, 9 < 10");
|
||||
assertValid(() -> guard.guard(9, "z", false, systemClientState));
|
||||
assertValid(() -> guard.guard(9, "z", false, superClientState));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDisableFlag() throws Throwable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -136,15 +136,41 @@ public abstract class ThresholdTester extends GuardrailTester
|
|||
.hasMessageContaining(guardrail.name + "_warn_threshold should be lower than the fail threshold");
|
||||
}
|
||||
|
||||
protected void assertThresholdValid(String query) throws Throwable
|
||||
protected void assertMaxThresholdValid(String query) throws Throwable
|
||||
{
|
||||
assertValid(query);
|
||||
|
||||
Assertions.assertThat(currentValue())
|
||||
.isLessThanOrEqualTo(warnGetter.applyAsLong(guardrails()))
|
||||
.isLessThanOrEqualTo(failGetter.applyAsLong(guardrails()));
|
||||
long warnValue = warnGetter.applyAsLong(guardrails());
|
||||
long failValue = failGetter.applyAsLong(guardrails());
|
||||
long current = currentValue();
|
||||
|
||||
if (warnValue != disabledValue)
|
||||
Assertions.assertThat(current)
|
||||
.isLessThanOrEqualTo(warnValue);
|
||||
|
||||
if (failValue != disabledValue)
|
||||
Assertions.assertThat(current)
|
||||
.isLessThanOrEqualTo(failValue);
|
||||
}
|
||||
|
||||
protected void assertMinThresholdValid(String query) throws Throwable
|
||||
{
|
||||
assertValid(query);
|
||||
|
||||
long warnValue = warnGetter.applyAsLong(guardrails());
|
||||
long failValue = failGetter.applyAsLong(guardrails());
|
||||
long current = currentValue();
|
||||
|
||||
if (warnValue != disabledValue)
|
||||
Assertions.assertThat(current)
|
||||
.isGreaterThanOrEqualTo(warnValue);
|
||||
|
||||
if (failValue != disabledValue)
|
||||
Assertions.assertThat(current)
|
||||
.isGreaterThanOrEqualTo(failValue);
|
||||
}
|
||||
|
||||
|
||||
protected void assertThresholdWarns(String query, String message) throws Throwable
|
||||
{
|
||||
assertThresholdWarns(query, message, message);
|
||||
|
|
@ -193,7 +219,7 @@ public abstract class ThresholdTester extends GuardrailTester
|
|||
.isEqualTo(failGetter.applyAsLong(guardrails()));
|
||||
}
|
||||
|
||||
private void assertInvalidPositiveProperty(BiConsumer<Guardrails, Long> setter,
|
||||
protected void assertInvalidPositiveProperty(BiConsumer<Guardrails, Long> setter,
|
||||
long value,
|
||||
long maxValue,
|
||||
String name)
|
||||
|
|
@ -238,6 +264,13 @@ public abstract class ThresholdTester extends GuardrailTester
|
|||
assertInvalidPositiveProperty(setter, value, maxValue, name);
|
||||
}
|
||||
|
||||
protected void assertInvalidPositiveIntProperty (BiConsumer<Guardrails, Integer> setter, int value,
|
||||
int maxValue,
|
||||
String name)
|
||||
{
|
||||
assertInvalidPositiveProperty((g, l) -> setter.accept(g, l.intValue()), (long) value, maxValue, name);
|
||||
}
|
||||
|
||||
protected void testValidationOfStrictlyPositiveProperty(BiConsumer<Guardrails, Long> setter, String name)
|
||||
{
|
||||
assertInvalidStrictlyPositiveProperty(setter, Integer.MIN_VALUE, name);
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ public class NetworkTopologyStrategyTest
|
|||
StorageService.instance.getTokenMetadata().updateHostId(UUID.randomUUID(), FBUtilities.getBroadcastAddressAndPort());
|
||||
|
||||
ClientWarn.instance.captureWarnings();
|
||||
strategy.maybeWarnOnOptions();
|
||||
strategy.maybeWarnOnOptions(null);
|
||||
assertTrue(ClientWarn.instance.getWarnings().stream().anyMatch(s -> s.contains("Your replication factor")));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ public class SimpleStrategyTest
|
|||
StorageService.instance.getTokenMetadata().updateHostId(UUID.randomUUID(), FBUtilities.getBroadcastAddressAndPort());
|
||||
|
||||
ClientWarn.instance.captureWarnings();
|
||||
strategy.maybeWarnOnOptions();
|
||||
strategy.maybeWarnOnOptions(null);
|
||||
assertTrue(ClientWarn.instance.getWarnings().stream().anyMatch(s -> s.contains("Your replication factor")));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
/*
|
||||
* 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.nodetool;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
import org.apache.cassandra.tools.ToolRunner;
|
||||
import org.assertj.core.api.Assertions;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class GetMinimumKeyspaceRFTest extends CQLTester
|
||||
{
|
||||
@BeforeClass
|
||||
public static void setup() throws Exception
|
||||
{
|
||||
requireNetwork();
|
||||
startJMXServer();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("SingleCharacterStringConcatenation")
|
||||
public void testMaybeChangeDocs()
|
||||
{
|
||||
// If you added, modified options or help, please update docs if necessary
|
||||
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "getminimumrf");
|
||||
tool.assertOnCleanExit();
|
||||
|
||||
String help = "NAME\n" +
|
||||
" nodetool getminimumrf - Gets minimum keyspace replication factor.\n" +
|
||||
"\n" +
|
||||
"SYNOPSIS\n" +
|
||||
" nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]\n" +
|
||||
" [(-pp | --print-port)] [(-pw <password> | --password <password>)]\n" +
|
||||
" [(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]\n" +
|
||||
" [(-u <username> | --username <username>)] getminimumrf\n" +
|
||||
"\n" +
|
||||
"OPTIONS\n" +
|
||||
" -h <host>, --host <host>\n" +
|
||||
" Node hostname or ip address\n" +
|
||||
"\n" +
|
||||
" -p <port>, --port <port>\n" +
|
||||
" Remote jmx agent port number\n" +
|
||||
"\n" +
|
||||
" -pp, --print-port\n" +
|
||||
" Operate in 4.0 mode with hosts disambiguated by port number\n" +
|
||||
"\n" +
|
||||
" -pw <password>, --password <password>\n" +
|
||||
" Remote jmx agent password\n" +
|
||||
"\n" +
|
||||
" -pwf <passwordFilePath>, --password-file <passwordFilePath>\n" +
|
||||
" Path to the JMX password file\n" +
|
||||
"\n" +
|
||||
" -u <username>, --username <username>\n" +
|
||||
" Remote jmx agent username\n" +
|
||||
"\n" +
|
||||
"\n";
|
||||
assertThat(tool.getStdout()).isEqualTo(help);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMinimumRF()
|
||||
{
|
||||
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("getminimumrf");
|
||||
tool.assertOnCleanExit();
|
||||
assertThat(tool.getStdout().trim()).isEqualTo(Integer.toString(DatabaseDescriptor.getMinimumKeyspaceRF()));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
/*
|
||||
* 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.nodetool;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.cql3.CQLTester;
|
||||
import org.apache.cassandra.tools.ToolRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class SetMinimumKeyspaceRFTest extends CQLTester
|
||||
{
|
||||
@BeforeClass
|
||||
public static void setup() throws Exception
|
||||
{
|
||||
requireNetwork();
|
||||
startJMXServer();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("SingleCharacterStringConcatenation")
|
||||
public void testMaybeChangeDocs()
|
||||
{
|
||||
// If you added, modified options or help, please update docs if necessary
|
||||
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("help", "setminimumrf");
|
||||
tool.assertOnCleanExit();
|
||||
|
||||
String help = "NAME\n" +
|
||||
" nodetool setminimumrf - Sets minimum keyspace replication factor.\n" +
|
||||
"\n" +
|
||||
"SYNOPSIS\n" +
|
||||
" nodetool [(-h <host> | --host <host>)] [(-p <port> | --port <port>)]\n" +
|
||||
" [(-pp | --print-port)] [(-pw <password> | --password <password>)]\n" +
|
||||
" [(-pwf <passwordFilePath> | --password-file <passwordFilePath>)]\n" +
|
||||
" [(-u <username> | --username <username>)] setminimumrf [--] <value>\n" +
|
||||
"\n" +
|
||||
"OPTIONS\n" +
|
||||
" -h <host>, --host <host>\n" +
|
||||
" Node hostname or ip address\n" +
|
||||
"\n" +
|
||||
" -p <port>, --port <port>\n" +
|
||||
" Remote jmx agent port number\n" +
|
||||
"\n" +
|
||||
" -pp, --print-port\n" +
|
||||
" Operate in 4.0 mode with hosts disambiguated by port number\n" +
|
||||
"\n" +
|
||||
" -pw <password>, --password <password>\n" +
|
||||
" Remote jmx agent password\n" +
|
||||
"\n" +
|
||||
" -pwf <passwordFilePath>, --password-file <passwordFilePath>\n" +
|
||||
" Path to the JMX password file\n" +
|
||||
"\n" +
|
||||
" -u <username>, --username <username>\n" +
|
||||
" Remote jmx agent username\n" +
|
||||
"\n" +
|
||||
" --\n" +
|
||||
" This option can be used to separate command-line options from the\n" +
|
||||
" list of argument, (useful when arguments might be mistaken for\n" +
|
||||
" command-line options\n" +
|
||||
"\n" +
|
||||
" <value>\n" +
|
||||
" Minimum replication factor\n" +
|
||||
"\n" +
|
||||
"\n";
|
||||
assertThat(tool.getStdout()).isEqualTo(help);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetMinimumRF()
|
||||
{
|
||||
ToolRunner.ToolResult tool = ToolRunner.invokeNodetool("setminimumrf", "1");
|
||||
tool.assertOnCleanExit();
|
||||
assertThat(DatabaseDescriptor.getMinimumKeyspaceRF()).isEqualTo(1);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue