mirror of https://github.com/apache/cassandra
Add reasons to guardrail messages and consider guardrails in the error message for needed ALLOW FILTERING
* Add optional reason for guarding an operation to every guardrail * Add reason for disabling ALLOW FILTERING into cassandra.yaml * Consider allow_filtering_enabled guardrail in the error message for needed ALLOW FILTERING patch by Andrés de la Peña; reviewed by Berenguer Blasi and Josh McKenzie for CASSANDRA-17967
This commit is contained in:
parent
6da9e33602
commit
9f99e72aae
|
|
@ -1,4 +1,5 @@
|
|||
4.2
|
||||
* Add reasons to guardrail messages and consider guardrails in the error message for needed ALLOW FILTERING (CASSANDRA-17967)
|
||||
* Add support for CQL functions on collections, tuples and UDTs (CASSANDRA-17811)
|
||||
* Add flag to exclude nodes from local DC when running nodetool rebuild (CASSANDRA-17870)
|
||||
* Adding endpoint verification option to client_encryption_options (CASSANDRA-18034)
|
||||
|
|
|
|||
|
|
@ -1814,6 +1814,7 @@ drop_compact_storage_enabled: false
|
|||
# items_per_collection_fail_threshold: -1
|
||||
#
|
||||
# Guardrail to allow/disallow querying with ALLOW FILTERING. Defaults to true.
|
||||
# ALLOW FILTERING can potentially visit all the data in the table and have unpredictable performance.
|
||||
# allow_filtering_enabled: true
|
||||
#
|
||||
# Guardrail to allow/disallow setting SimpleStrategy via keyspace creation or alteration. Defaults to true.
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.apache.cassandra.cql3.statements.Bound;
|
|||
import org.apache.cassandra.cql3.statements.StatementType;
|
||||
import org.apache.cassandra.db.*;
|
||||
import org.apache.cassandra.db.filter.RowFilter;
|
||||
import org.apache.cassandra.db.guardrails.Guardrails;
|
||||
import org.apache.cassandra.db.marshal.AbstractType;
|
||||
import org.apache.cassandra.dht.*;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
|
|
@ -50,10 +51,15 @@ import static org.apache.cassandra.cql3.statements.RequestValidations.invalidReq
|
|||
*/
|
||||
public final class StatementRestrictions
|
||||
{
|
||||
public static final String REQUIRES_ALLOW_FILTERING_MESSAGE =
|
||||
"Cannot execute this query as it might involve data filtering and " +
|
||||
"thus may have unpredictable performance. If you want to execute " +
|
||||
"this query despite the performance unpredictability, use ALLOW FILTERING";
|
||||
private static final String ALLOW_FILTERING_MESSAGE =
|
||||
"Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. ";
|
||||
|
||||
public static final String REQUIRES_ALLOW_FILTERING_MESSAGE = ALLOW_FILTERING_MESSAGE +
|
||||
"If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING";
|
||||
|
||||
public static final String CANNOT_USE_ALLOW_FILTERING_MESSAGE = ALLOW_FILTERING_MESSAGE +
|
||||
"Executing this query despite the performance unpredictability with ALLOW FILTERING has been disabled " +
|
||||
"by the allow_filtering_enabled property in cassandra.yaml";
|
||||
|
||||
/**
|
||||
* The type of statement
|
||||
|
|
@ -125,7 +131,8 @@ public final class StatementRestrictions
|
|||
this.notNullColumns = new HashSet<>();
|
||||
}
|
||||
|
||||
public StatementRestrictions(StatementType type,
|
||||
public StatementRestrictions(ClientState state,
|
||||
StatementType type,
|
||||
TableMetadata table,
|
||||
WhereClause whereClause,
|
||||
VariableSpecifications boundNames,
|
||||
|
|
@ -133,14 +140,15 @@ public final class StatementRestrictions
|
|||
boolean allowFiltering,
|
||||
boolean forView)
|
||||
{
|
||||
this(type, table, whereClause, boundNames, selectsOnlyStaticColumns, type.allowUseOfSecondaryIndices(), allowFiltering, forView);
|
||||
this(state, type, table, whereClause, boundNames, selectsOnlyStaticColumns, type.allowUseOfSecondaryIndices(), allowFiltering, forView);
|
||||
}
|
||||
|
||||
/*
|
||||
* We want to override allowUseOfSecondaryIndices flag from the StatementType for MV statements
|
||||
* to avoid initing the Keyspace and SecondaryIndexManager.
|
||||
*/
|
||||
public StatementRestrictions(StatementType type,
|
||||
public StatementRestrictions(ClientState state,
|
||||
StatementType type,
|
||||
TableMetadata table,
|
||||
WhereClause whereClause,
|
||||
VariableSpecifications boundNames,
|
||||
|
|
@ -214,7 +222,7 @@ public final class StatementRestrictions
|
|||
}
|
||||
|
||||
// At this point, the select statement if fully constructed, but we still have a few things to validate
|
||||
processPartitionKeyRestrictions(hasQueriableIndex, allowFiltering, forView);
|
||||
processPartitionKeyRestrictions(state, hasQueriableIndex, allowFiltering, forView);
|
||||
|
||||
// Some but not all of the partition key columns have been specified;
|
||||
// hence we need turn these restrictions into a row filter.
|
||||
|
|
@ -267,7 +275,7 @@ public final class StatementRestrictions
|
|||
if (hasQueriableIndex)
|
||||
usesSecondaryIndexing = true;
|
||||
else if (!allowFiltering)
|
||||
throw invalidRequest(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
|
||||
throw invalidRequest(allowFilteringMessage(state));
|
||||
|
||||
filterRestrictions.add(nonPrimaryKeyRestrictions);
|
||||
}
|
||||
|
|
@ -408,7 +416,7 @@ public final class StatementRestrictions
|
|||
return this.usesSecondaryIndexing;
|
||||
}
|
||||
|
||||
private void processPartitionKeyRestrictions(boolean hasQueriableIndex, boolean allowFiltering, boolean forView)
|
||||
private void processPartitionKeyRestrictions(ClientState state, boolean hasQueriableIndex, boolean allowFiltering, boolean forView)
|
||||
{
|
||||
if (!type.allowPartitionKeyRanges())
|
||||
{
|
||||
|
|
@ -445,7 +453,7 @@ public final class StatementRestrictions
|
|||
if (partitionKeyRestrictions.needFiltering(table))
|
||||
{
|
||||
if (!allowFiltering && !forView && !hasQueriableIndex)
|
||||
throw new InvalidRequestException(REQUIRES_ALLOW_FILTERING_MESSAGE);
|
||||
throw new InvalidRequestException(allowFilteringMessage(state));
|
||||
|
||||
isKeyRange = true;
|
||||
usesSecondaryIndexing = hasQueriableIndex;
|
||||
|
|
@ -874,4 +882,11 @@ public final class StatementRestrictions
|
|||
{
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
private static String allowFilteringMessage(ClientState state)
|
||||
{
|
||||
return Guardrails.allowFilteringEnabled.isEnabled(state)
|
||||
? REQUIRES_ALLOW_FILTERING_MESSAGE
|
||||
: CANNOT_USE_ALLOW_FILTERING_MESSAGE;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -647,7 +647,7 @@ public class BatchStatement implements CQLStatement
|
|||
public BatchStatement prepare(ClientState state)
|
||||
{
|
||||
List<ModificationStatement> statements = new ArrayList<>(parsedStatements.size());
|
||||
parsedStatements.forEach(s -> statements.add(s.prepare(bindVariables)));
|
||||
parsedStatements.forEach(s -> statements.add(s.prepare(state, bindVariables)));
|
||||
|
||||
Attributes prepAttrs = attrs.prepare("[batch]", "[batch]");
|
||||
prepAttrs.collectMarkerSpecification(bindVariables);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.apache.cassandra.db.partitions.PartitionUpdate;
|
|||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.schema.ColumnMetadata;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
|
@ -140,7 +141,8 @@ public class DeleteStatement extends ModificationStatement
|
|||
|
||||
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(TableMetadata metadata,
|
||||
protected ModificationStatement prepareInternal(ClientState state,
|
||||
TableMetadata metadata,
|
||||
VariableSpecifications bindVariables,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
|
|
@ -160,7 +162,8 @@ public class DeleteStatement extends ModificationStatement
|
|||
operations.add(op);
|
||||
}
|
||||
|
||||
StatementRestrictions restrictions = newRestrictions(metadata,
|
||||
StatementRestrictions restrictions = newRestrictions(state,
|
||||
metadata,
|
||||
bindVariables,
|
||||
operations,
|
||||
whereClause,
|
||||
|
|
|
|||
|
|
@ -940,10 +940,10 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
|||
|
||||
public ModificationStatement prepare(ClientState state)
|
||||
{
|
||||
return prepare(bindVariables);
|
||||
return prepare(state, bindVariables);
|
||||
}
|
||||
|
||||
public ModificationStatement prepare(VariableSpecifications bindVariables)
|
||||
public ModificationStatement prepare(ClientState state, VariableSpecifications bindVariables)
|
||||
{
|
||||
TableMetadata metadata = Schema.instance.validateTable(keyspace(), name());
|
||||
|
||||
|
|
@ -952,7 +952,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
|||
|
||||
Conditions preparedConditions = prepareConditions(metadata, bindVariables);
|
||||
|
||||
return prepareInternal(metadata, bindVariables, preparedConditions, preparedAttributes);
|
||||
return prepareInternal(state, metadata, bindVariables, preparedConditions, preparedAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1011,7 +1011,8 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
|||
return builder.build();
|
||||
}
|
||||
|
||||
protected abstract ModificationStatement prepareInternal(TableMetadata metadata,
|
||||
protected abstract ModificationStatement prepareInternal(ClientState state,
|
||||
TableMetadata metadata,
|
||||
VariableSpecifications bindVariables,
|
||||
Conditions conditions,
|
||||
Attributes attrs);
|
||||
|
|
@ -1026,7 +1027,8 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
|||
* @param conditions the conditions
|
||||
* @return the restrictions
|
||||
*/
|
||||
protected StatementRestrictions newRestrictions(TableMetadata metadata,
|
||||
protected StatementRestrictions newRestrictions(ClientState state,
|
||||
TableMetadata metadata,
|
||||
VariableSpecifications boundNames,
|
||||
Operations operations,
|
||||
WhereClause where,
|
||||
|
|
@ -1036,7 +1038,7 @@ public abstract class ModificationStatement implements CQLStatement.SingleKeyspa
|
|||
throw new InvalidRequestException(CUSTOM_EXPRESSIONS_NOT_ALLOWED);
|
||||
|
||||
boolean applyOnlyToStaticColumns = appliesOnlyToStaticColumns(operations, conditions);
|
||||
return new StatementRestrictions(type, metadata, where, boundNames, applyOnlyToStaticColumns, false, false);
|
||||
return new StatementRestrictions(state, type, metadata, where, boundNames, applyOnlyToStaticColumns, false, false);
|
||||
}
|
||||
|
||||
public List<Pair<ColumnIdentifier, ColumnCondition.Raw>> getConditions()
|
||||
|
|
|
|||
|
|
@ -1075,17 +1075,17 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
|||
{
|
||||
// Cache locally for use by Guardrails
|
||||
this.state = state;
|
||||
return prepare(false);
|
||||
return prepare(state, false);
|
||||
}
|
||||
|
||||
public SelectStatement prepare(boolean forView) throws InvalidRequestException
|
||||
public SelectStatement prepare(ClientState state, boolean forView) throws InvalidRequestException
|
||||
{
|
||||
TableMetadata table = Schema.instance.validateTable(keyspace(), name());
|
||||
|
||||
List<Selectable> selectables = RawSelector.toSelectables(selectClause, table);
|
||||
boolean containsOnlyStaticColumns = selectOnlyStaticColumns(table, selectables);
|
||||
|
||||
StatementRestrictions restrictions = prepareRestrictions(table, bindVariables, containsOnlyStaticColumns, forView);
|
||||
StatementRestrictions restrictions = prepareRestrictions(state, table, bindVariables, containsOnlyStaticColumns, forView);
|
||||
|
||||
// If we order post-query, the sorted column needs to be in the ResultSet for sorting,
|
||||
// even if we don't ultimately ship them to the client (CASSANDRA-4911).
|
||||
|
|
@ -1215,12 +1215,14 @@ public class SelectStatement implements CQLStatement.SingleKeyspaceCqlStatement
|
|||
* @return the restrictions
|
||||
* @throws InvalidRequestException if a problem occurs while building the restrictions
|
||||
*/
|
||||
private StatementRestrictions prepareRestrictions(TableMetadata metadata,
|
||||
private StatementRestrictions prepareRestrictions(ClientState state,
|
||||
TableMetadata metadata,
|
||||
VariableSpecifications boundNames,
|
||||
boolean selectsOnlyStaticColumns,
|
||||
boolean forView) throws InvalidRequestException
|
||||
{
|
||||
return new StatementRestrictions(StatementType.SELECT,
|
||||
return new StatementRestrictions(state,
|
||||
StatementType.SELECT,
|
||||
metadata,
|
||||
whereClause,
|
||||
boundNames,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.apache.cassandra.db.Slice;
|
|||
import org.apache.cassandra.db.partitions.PartitionUpdate;
|
||||
import org.apache.cassandra.schema.ColumnMetadata;
|
||||
import org.apache.cassandra.schema.TableMetadata;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
|
|
@ -135,7 +136,8 @@ public class UpdateStatement extends ModificationStatement
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(TableMetadata metadata,
|
||||
protected ModificationStatement prepareInternal(ClientState state,
|
||||
TableMetadata metadata,
|
||||
VariableSpecifications bindVariables,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
|
|
@ -176,7 +178,8 @@ public class UpdateStatement extends ModificationStatement
|
|||
|
||||
boolean applyOnlyToStaticColumns = !hasClusteringColumnsSet && appliesOnlyToStaticColumns(operations, conditions);
|
||||
|
||||
StatementRestrictions restrictions = new StatementRestrictions(type,
|
||||
StatementRestrictions restrictions = new StatementRestrictions(state,
|
||||
type,
|
||||
metadata,
|
||||
whereClause.build(),
|
||||
bindVariables,
|
||||
|
|
@ -210,7 +213,8 @@ public class UpdateStatement extends ModificationStatement
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(TableMetadata metadata,
|
||||
protected ModificationStatement prepareInternal(ClientState state,
|
||||
TableMetadata metadata,
|
||||
VariableSpecifications bindVariables,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
|
|
@ -244,7 +248,8 @@ public class UpdateStatement extends ModificationStatement
|
|||
|
||||
boolean applyOnlyToStaticColumns = !hasClusteringColumnsSet && appliesOnlyToStaticColumns(operations, conditions);
|
||||
|
||||
StatementRestrictions restrictions = new StatementRestrictions(type,
|
||||
StatementRestrictions restrictions = new StatementRestrictions(state,
|
||||
type,
|
||||
metadata,
|
||||
whereClause.build(),
|
||||
bindVariables,
|
||||
|
|
@ -291,7 +296,8 @@ public class UpdateStatement extends ModificationStatement
|
|||
}
|
||||
|
||||
@Override
|
||||
protected ModificationStatement prepareInternal(TableMetadata metadata,
|
||||
protected ModificationStatement prepareInternal(ClientState state,
|
||||
TableMetadata metadata,
|
||||
VariableSpecifications bindVariables,
|
||||
Conditions conditions,
|
||||
Attributes attrs)
|
||||
|
|
@ -309,7 +315,8 @@ public class UpdateStatement extends ModificationStatement
|
|||
operations.add(operation);
|
||||
}
|
||||
|
||||
StatementRestrictions restrictions = newRestrictions(metadata,
|
||||
StatementRestrictions restrictions = newRestrictions(state,
|
||||
metadata,
|
||||
bindVariables,
|
||||
operations,
|
||||
whereClause,
|
||||
|
|
|
|||
|
|
@ -272,7 +272,8 @@ public final class CreateViewStatement extends AlterSchemaStatement
|
|||
throw ire("WHERE clause for materialized view '%s' cannot contain custom index expressions", viewName);
|
||||
|
||||
StatementRestrictions restrictions =
|
||||
new StatementRestrictions(StatementType.SELECT,
|
||||
new StatementRestrictions(state,
|
||||
StatementType.SELECT,
|
||||
table,
|
||||
whereClause,
|
||||
VariableSpecifications.empty(),
|
||||
|
|
|
|||
|
|
@ -39,18 +39,31 @@ public class EnableFlag extends Guardrail
|
|||
* Creates a new {@link EnableFlag} guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param reason the optional description of the reason for guarding the operation
|
||||
* @param enabled a {@link ClientState}-based supplier of boolean indicating whether the feature guarded by this
|
||||
* guardrail is enabled.
|
||||
* @param featureName The feature that is guarded by this guardrail (for reporting in error messages), {@link
|
||||
* EnableFlag#ensureEnabled(String, ClientState)} can specify a different {@code featureName}.
|
||||
*/
|
||||
public EnableFlag(String name, Predicate<ClientState> enabled, String featureName)
|
||||
public EnableFlag(String name, @Nullable String reason, Predicate<ClientState> enabled, String featureName)
|
||||
{
|
||||
super(name);
|
||||
super(name, reason);
|
||||
this.enabled = enabled;
|
||||
this.featureName = featureName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the guarded feature is enabled or not.
|
||||
*
|
||||
* @param state The client state, used to skip the check if the query is internal or is done by a superuser.
|
||||
* A {@code null} value means that the check should be done regardless of the query.
|
||||
* @return {@code true} is the feature is enabled, {@code false} otherwise.
|
||||
*/
|
||||
public boolean isEnabled(@Nullable ClientState state)
|
||||
{
|
||||
return !enabled(state) || enabled.test(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts the operation if this guardrail is not enabled.
|
||||
*
|
||||
|
|
@ -80,7 +93,7 @@ public class EnableFlag extends Guardrail
|
|||
*/
|
||||
public void ensureEnabled(String featureName, @Nullable ClientState state)
|
||||
{
|
||||
if (enabled(state) && !enabled.test(state))
|
||||
if (!isEnabled(state))
|
||||
fail(featureName + " is not allowed", state);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ public abstract class Guardrail
|
|||
/** A name identifying the guardrail (mainly for shipping with diagnostic events). */
|
||||
public final String name;
|
||||
|
||||
/** An optional description of the reason for guarding the operation. */
|
||||
@Nullable
|
||||
public final String reason;
|
||||
|
||||
/** Minimum logging and triggering interval to avoid spamming downstream. */
|
||||
private long minNotifyIntervalInMs = 0;
|
||||
|
||||
|
|
@ -60,9 +64,10 @@ public abstract class Guardrail
|
|||
/** Time of last failure in milliseconds. */
|
||||
private volatile long lastFailInMs = 0;
|
||||
|
||||
Guardrail(String name)
|
||||
Guardrail(String name, @Nullable String reason)
|
||||
{
|
||||
this.name = name;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -138,8 +143,16 @@ public abstract class Guardrail
|
|||
@VisibleForTesting
|
||||
String decorateMessage(String message)
|
||||
{
|
||||
// Add a prefix to error message so user knows what threw the warning or cause the failure
|
||||
return String.format("Guardrail %s violated: %s", name, message);
|
||||
// Add a prefix to error message so user knows what threw the warning or cause the failure.
|
||||
String decoratedMessage = String.format("Guardrail %s violated: %s", name, message);
|
||||
|
||||
// Add the reason for the guardrail triggering, if there is any.
|
||||
if (reason != null)
|
||||
{
|
||||
decoratedMessage += (message.endsWith(".") ? ' ' : ". ") + reason;
|
||||
}
|
||||
|
||||
return decoratedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold keyspaces =
|
||||
new MaxThreshold("keyspaces",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getKeyspacesWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getKeyspacesFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -70,6 +71,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold tables =
|
||||
new MaxThreshold("tables",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablesWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablesFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -83,6 +85,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold columnsPerTable =
|
||||
new MaxThreshold("columns_per_table",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getColumnsPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -93,6 +96,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
|
||||
public static final MaxThreshold secondaryIndexesPerTable =
|
||||
new MaxThreshold("secondary_indexes_per_table",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -106,6 +110,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag createSecondaryIndexesEnabled =
|
||||
new EnableFlag("secondary_indexes",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSecondaryIndexesEnabled(),
|
||||
"User creation of secondary indexes");
|
||||
|
||||
|
|
@ -114,6 +119,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold materializedViewsPerTable =
|
||||
new MaxThreshold("materialized_views_per_table",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaterializedViewsPerTableFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -127,6 +133,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final Values<String> tableProperties =
|
||||
new Values<>("table_properties",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablePropertiesWarned(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablePropertiesIgnored(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getTablePropertiesDisallowed(),
|
||||
|
|
@ -137,11 +144,13 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag userTimestampsEnabled =
|
||||
new EnableFlag("user_timestamps",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getUserTimestampsEnabled(),
|
||||
"User provided timestamps (USING TIMESTAMP)");
|
||||
|
||||
public static final EnableFlag groupByEnabled =
|
||||
new EnableFlag("group_by",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getGroupByEnabled(),
|
||||
"GROUP BY functionality");
|
||||
|
||||
|
|
@ -150,14 +159,16 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag alterTableEnabled =
|
||||
new EnableFlag("alter_table",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getAlterTableEnabled(),
|
||||
"User access to ALTER TABLE statement for column mutation");
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getAlterTableEnabled(),
|
||||
"User access to ALTER TABLE statement for column mutation");
|
||||
|
||||
/**
|
||||
* Guardrail disabling DROP / TRUNCATE TABLE behavior
|
||||
*/
|
||||
public static final EnableFlag dropTruncateTableEnabled =
|
||||
new EnableFlag("drop_truncate_table_enabled",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getDropTruncateTableEnabled(),
|
||||
"DROP and TRUNCATE TABLE functionality");
|
||||
|
||||
|
|
@ -166,6 +177,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag dropKeyspaceEnabled =
|
||||
new EnableFlag("drop_keyspace_enabled",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getDropKeyspaceEnabled(),
|
||||
"DROP KEYSPACE functionality");
|
||||
|
||||
|
|
@ -174,6 +186,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag uncompressedTablesEnabled =
|
||||
new EnableFlag("uncompressed_tables_enabled",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getUncompressedTablesEnabled(),
|
||||
"Uncompressed table");
|
||||
|
||||
|
|
@ -182,6 +195,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag compactTablesEnabled =
|
||||
new EnableFlag("compact_tables",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getCompactTablesEnabled(),
|
||||
"Creation of new COMPACT STORAGE tables");
|
||||
|
||||
|
|
@ -190,6 +204,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold pageSize =
|
||||
new MaxThreshold("page_size",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPageSizeWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPageSizeFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -203,6 +218,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold partitionKeysInSelect =
|
||||
new MaxThreshold("partition_keys_in_select",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getPartitionKeysInSelectFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -218,6 +234,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag readBeforeWriteListOperationsEnabled =
|
||||
new EnableFlag("read_before_write_list_operations",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getReadBeforeWriteListOperationsEnabled(),
|
||||
"List operation requiring read before write");
|
||||
|
||||
|
|
@ -226,6 +243,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag allowFilteringEnabled =
|
||||
new EnableFlag("allow_filtering",
|
||||
"ALLOW FILTERING can potentially visit all the data in the table and have unpredictable performance.",
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getAllowFilteringEnabled(),
|
||||
"Querying with ALLOW FILTERING");
|
||||
|
||||
|
|
@ -234,6 +252,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final EnableFlag simpleStrategyEnabled =
|
||||
new EnableFlag("simplestrategy",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getSimpleStrategyEnabled(),
|
||||
"SimpleStrategy");
|
||||
|
||||
|
|
@ -242,6 +261,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold inSelectCartesianProduct =
|
||||
new MaxThreshold("in_select_cartesian_product",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getInSelectCartesianProductFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -257,6 +277,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final Values<ConsistencyLevel> readConsistencyLevels =
|
||||
new Values<>("read_consistency_levels",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getReadConsistencyLevelsWarned(),
|
||||
state -> Collections.emptySet(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getReadConsistencyLevelsDisallowed(),
|
||||
|
|
@ -267,6 +288,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final Values<ConsistencyLevel> writeConsistencyLevels =
|
||||
new Values<>("write_consistency_levels",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getWriteConsistencyLevelsWarned(),
|
||||
state -> Collections.emptySet(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getWriteConsistencyLevelsDisallowed(),
|
||||
|
|
@ -277,6 +299,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold columnValueSize =
|
||||
new MaxThreshold("column_value_size",
|
||||
null,
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getColumnValueSizeWarnThreshold()),
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getColumnValueSizeFailThreshold()),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -288,6 +311,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold collectionSize =
|
||||
new MaxThreshold("collection_size",
|
||||
null,
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeWarnThreshold()),
|
||||
state -> sizeToBytes(CONFIG_PROVIDER.getOrCreate(state).getCollectionSizeFailThreshold()),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -299,6 +323,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold itemsPerCollection =
|
||||
new MaxThreshold("items_per_collection",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getItemsPerCollectionFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -310,6 +335,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold fieldsPerUDT =
|
||||
new MaxThreshold("fields_per_udt",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getFieldsPerUDTFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -324,6 +350,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final PercentageThreshold localDataDiskUsage =
|
||||
new PercentageThreshold("local_data_disk_usage",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getDataDiskUsagePercentageWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getDataDiskUsagePercentageFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -339,6 +366,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final Predicates<InetAddressAndPort> replicaDiskUsage =
|
||||
new Predicates<>("replica_disk_usage",
|
||||
null,
|
||||
state -> DiskUsageBroadcaster.instance::isStuffed,
|
||||
state -> DiskUsageBroadcaster.instance::isFull,
|
||||
// not using `value` because it represents replica address which should be hidden from client.
|
||||
|
|
@ -359,6 +387,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MinThreshold minimumReplicationFactor =
|
||||
new MinThreshold("minimum_replication_factor",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMinimumReplicationFactorWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMinimumReplicationFactorFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
@ -370,6 +399,7 @@ public final class Guardrails implements GuardrailsMBean
|
|||
*/
|
||||
public static final MaxThreshold maximumReplicationFactor =
|
||||
new MaxThreshold("maximum_replication_factor",
|
||||
null,
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaximumReplicationFactorWarnThreshold(),
|
||||
state -> CONFIG_PROVIDER.getOrCreate(state).getMaximumReplicationFactorFailThreshold(),
|
||||
(isWarning, what, value, threshold) ->
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
package org.apache.cassandra.db.guardrails;
|
||||
|
||||
import java.util.function.ToLongFunction;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
||||
/**
|
||||
|
|
@ -30,16 +32,18 @@ public class MaxThreshold extends Threshold
|
|||
* Creates a new threshold guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param reason the optional description of the reason for guarding the operation
|
||||
* @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,
|
||||
@Nullable String reason,
|
||||
ToLongFunction<ClientState> warnThreshold,
|
||||
ToLongFunction<ClientState> failThreshold,
|
||||
Threshold.ErrorMessageProvider messageProvider)
|
||||
{
|
||||
super(name, warnThreshold, failThreshold, messageProvider);
|
||||
super(name, reason, warnThreshold, failThreshold, messageProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
package org.apache.cassandra.db.guardrails;
|
||||
|
||||
import java.util.function.ToLongFunction;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
||||
/**
|
||||
|
|
@ -30,16 +32,18 @@ public class MinThreshold extends Threshold
|
|||
* Creates a new minimum threshold guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param reason the optional description of the reason for guarding the operation
|
||||
* @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,
|
||||
@Nullable String reason,
|
||||
ToLongFunction<ClientState> warnThreshold,
|
||||
ToLongFunction<ClientState> failThreshold,
|
||||
Threshold.ErrorMessageProvider messageProvider)
|
||||
{
|
||||
super(name, warnThreshold, failThreshold, messageProvider);
|
||||
super(name, reason, warnThreshold, failThreshold, messageProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ package org.apache.cassandra.db.guardrails;
|
|||
|
||||
import java.util.function.ToLongFunction;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
||||
/**
|
||||
|
|
@ -33,16 +35,18 @@ public class PercentageThreshold extends MaxThreshold
|
|||
* Creates a new threshold guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param reason the optional description of the reason for guarding the operation
|
||||
* @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 PercentageThreshold(String name,
|
||||
@Nullable String reason,
|
||||
ToLongFunction<ClientState> warnThreshold,
|
||||
ToLongFunction<ClientState> failThreshold,
|
||||
ErrorMessageProvider messageProvider)
|
||||
{
|
||||
super(name, warnThreshold, failThreshold, messageProvider);
|
||||
super(name, reason, warnThreshold, failThreshold, messageProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -56,16 +56,18 @@ public class Predicates<T> extends Guardrail
|
|||
* Creates a new {@link Predicates} guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param reason the optional description of the reason for guarding the operation
|
||||
* @param warnPredicate a {@link ClientState}-based predicate provider that is used to check if given value should trigger a warning.
|
||||
* @param failurePredicate a {@link ClientState}-based predicate provider that is used to check if given value should trigger a failure.
|
||||
* @param messageProvider a function to generate the warning or error message if the guardrail is triggered
|
||||
*/
|
||||
Predicates(String name,
|
||||
@Nullable String reason,
|
||||
Function<ClientState, Predicate<T>> warnPredicate,
|
||||
Function<ClientState, Predicate<T>> failurePredicate,
|
||||
MessageProvider<T> messageProvider)
|
||||
{
|
||||
super(name);
|
||||
super(name, reason);
|
||||
this.warnPredicate = warnPredicate;
|
||||
this.failurePredicate = failurePredicate;
|
||||
this.messageProvider = messageProvider;
|
||||
|
|
|
|||
|
|
@ -41,16 +41,18 @@ public abstract class Threshold extends Guardrail
|
|||
* Creates a new threshold guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param reason the optional description of the reason for guarding the operation
|
||||
* @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 Threshold(String name,
|
||||
@Nullable String reason,
|
||||
ToLongFunction<ClientState> warnThreshold,
|
||||
ToLongFunction<ClientState> failThreshold,
|
||||
ErrorMessageProvider messageProvider)
|
||||
{
|
||||
super(name);
|
||||
super(name, reason);
|
||||
this.warnThreshold = warnThreshold;
|
||||
this.failThreshold = failThreshold;
|
||||
this.messageProvider = messageProvider;
|
||||
|
|
|
|||
|
|
@ -47,18 +47,20 @@ public class Values<T> extends Guardrail
|
|||
* Creates a new values guardrail.
|
||||
*
|
||||
* @param name the identifying name of the guardrail
|
||||
* @param reason the optional description of the reason for guarding the operation
|
||||
* @param warnedValues a {@link ClientState}-based provider of the values for which a warning is triggered.
|
||||
* @param ignoredValues a {@link ClientState}-based provider of the values that are ignored.
|
||||
* @param disallowedValues a {@link ClientState}-based provider of the values that are disallowed.
|
||||
* @param what The feature that is guarded by this guardrail (for reporting in error messages).
|
||||
*/
|
||||
public Values(String name,
|
||||
@Nullable String reason,
|
||||
Function<ClientState, Set<T>> warnedValues,
|
||||
Function<ClientState, Set<T>> ignoredValues,
|
||||
Function<ClientState, Set<T>> disallowedValues,
|
||||
String what)
|
||||
{
|
||||
super(name);
|
||||
super(name, reason);
|
||||
this.warnedValues = warnedValues;
|
||||
this.ignoredValues = ignoredValues;
|
||||
this.disallowedValues = disallowedValues;
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import org.apache.cassandra.schema.KeyspaceMetadata;
|
|||
import org.apache.cassandra.schema.Schema;
|
||||
import org.apache.cassandra.schema.TableMetadataRef;
|
||||
import org.apache.cassandra.schema.ViewMetadata;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
|
@ -177,7 +178,7 @@ public class View
|
|||
|
||||
rawSelect.setBindVariables(Collections.emptyList());
|
||||
|
||||
select = rawSelect.prepare(true);
|
||||
select = rawSelect.prepare(ClientState.forInternalCalls(), true);
|
||||
}
|
||||
|
||||
return select;
|
||||
|
|
|
|||
|
|
@ -306,7 +306,8 @@ public class SSTableGenerator
|
|||
new AbstractMarker.Raw(values.size() - 1)));
|
||||
}
|
||||
|
||||
StatementRestrictions restrictions = new StatementRestrictions(StatementType.DELETE,
|
||||
StatementRestrictions restrictions = new StatementRestrictions(null,
|
||||
StatementType.DELETE,
|
||||
metadata,
|
||||
builder.build(),
|
||||
new VariableSpecifications(variableNames),
|
||||
|
|
|
|||
|
|
@ -22,8 +22,11 @@ import org.junit.After;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.cql3.restrictions.StatementRestrictions;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.schema.SchemaConstants;
|
||||
import org.apache.cassandra.schema.SchemaKeyspaceTables;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
||||
public class GuardrailAllowFilteringTest extends GuardrailTester
|
||||
{
|
||||
|
|
@ -33,7 +36,7 @@ public class GuardrailAllowFilteringTest extends GuardrailTester
|
|||
public void setupTest()
|
||||
{
|
||||
createTable("CREATE TABLE %s (k int PRIMARY KEY, a int, b int)");
|
||||
enableState = getGuardrial();
|
||||
enableState = getGuardrail();
|
||||
}
|
||||
|
||||
@After
|
||||
|
|
@ -47,7 +50,7 @@ public class GuardrailAllowFilteringTest extends GuardrailTester
|
|||
guardrails().setAllowFilteringEnabled(allowFilteringEnabled);
|
||||
}
|
||||
|
||||
private boolean getGuardrial()
|
||||
private boolean getGuardrail()
|
||||
{
|
||||
return guardrails().getAllowFilteringEnabled();
|
||||
}
|
||||
|
|
@ -91,4 +94,24 @@ public class GuardrailAllowFilteringTest extends GuardrailTester
|
|||
SchemaKeyspaceTables.TABLES,
|
||||
currentTable()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequiredAllowFiltering()
|
||||
{
|
||||
setGuardrail(true);
|
||||
assertRequiredAllowFilteringThrows(systemClientState, StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
|
||||
assertRequiredAllowFilteringThrows(superClientState, StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
|
||||
assertRequiredAllowFilteringThrows(userClientState, StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
|
||||
|
||||
setGuardrail(false);
|
||||
assertRequiredAllowFilteringThrows(systemClientState, StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
|
||||
assertRequiredAllowFilteringThrows(superClientState, StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
|
||||
assertRequiredAllowFilteringThrows(userClientState, StatementRestrictions.CANNOT_USE_ALLOW_FILTERING_MESSAGE);
|
||||
}
|
||||
|
||||
private void assertRequiredAllowFilteringThrows(ClientState state, String message)
|
||||
{
|
||||
String query = "SELECT * FROM %s WHERE a = 5";
|
||||
assertThrows(() -> execute(state, query), InvalidRequestException.class, message);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,9 +353,17 @@ public abstract class GuardrailTester extends CQLTester
|
|||
|
||||
if (guardrail != null)
|
||||
{
|
||||
String prefix = guardrail.decorateMessage("");
|
||||
assertTrue(format("Full error message '%s' doesn't start with the prefix '%s'", e.getMessage(), prefix),
|
||||
e.getMessage().startsWith(prefix));
|
||||
String message = e.getMessage();
|
||||
String prefix = guardrail.decorateMessage("").replace(". " + guardrail.reason, "");
|
||||
assertTrue(format("Full error message '%s' doesn't start with the prefix '%s'", message, prefix),
|
||||
message.startsWith(prefix));
|
||||
|
||||
String reason = guardrail.reason;
|
||||
if (reason != null)
|
||||
{
|
||||
assertTrue(format("Full error message '%s' doesn't end with the reason '%s'", message, reason),
|
||||
message.endsWith(reason));
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(format("Full error message '%s' does not contain expected message '%s'", e.getMessage(), failMessage),
|
||||
|
|
@ -412,9 +420,16 @@ public abstract class GuardrailTester extends CQLTester
|
|||
String warning = warnings.get(i);
|
||||
if (guardrail != null)
|
||||
{
|
||||
String prefix = guardrail.decorateMessage("");
|
||||
String prefix = guardrail.decorateMessage("").replace(". " + guardrail.reason, "");
|
||||
assertTrue(format("Warning log message '%s' doesn't start with the prefix '%s'", warning, prefix),
|
||||
warning.startsWith(prefix));
|
||||
|
||||
String reason = guardrail.reason;
|
||||
if (reason != null)
|
||||
{
|
||||
assertTrue(format("Warning log message '%s' doesn't end with the reason '%s'", warning, reason),
|
||||
warning.endsWith(reason));
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(format("Warning log message '%s' does not contain expected message '%s'", warning, message),
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ public class GuardrailsConfigProviderTest extends GuardrailTester
|
|||
String name = getClass().getCanonicalName() + '$' + CustomProvider.class.getSimpleName();
|
||||
GuardrailsConfigProvider provider = GuardrailsConfigProvider.build(name);
|
||||
MaxThreshold guard = new MaxThreshold("test_guardrail",
|
||||
"Some reason",
|
||||
state -> provider.getOrCreate(state).getTablesWarnThreshold(),
|
||||
state -> provider.getOrCreate(state).getTablesFailThreshold(),
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s > %s",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import static org.junit.Assert.assertTrue;
|
|||
public class GuardrailsTest extends GuardrailTester
|
||||
{
|
||||
public static final int DISABLED = -1;
|
||||
|
||||
public static final String REASON = "Testing";
|
||||
|
||||
private void testDisabledThreshold(Threshold guard) throws Throwable
|
||||
{
|
||||
|
|
@ -58,17 +58,18 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testDisabledMaxThreshold() throws Throwable
|
||||
{
|
||||
Threshold.ErrorMessageProvider errorMessageProvider = (isWarn, what, v, t) -> "Should never trigger";
|
||||
testDisabledThreshold(new MaxThreshold("x", state -> DISABLED, state -> DISABLED, errorMessageProvider));
|
||||
testDisabledThreshold(new MaxThreshold("x", REASON, state -> DISABLED, state -> DISABLED, errorMessageProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxThreshold() throws Throwable
|
||||
{
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> 10,
|
||||
state -> 100,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Aborting", featureName, v, t));
|
||||
REASON,
|
||||
state -> 10,
|
||||
state -> 100,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Aborting", featureName, v, t));
|
||||
|
||||
assertTrue(guard.enabled(userClientState));
|
||||
|
||||
|
|
@ -91,10 +92,11 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testWarnOnlyMaxThreshold() throws Throwable
|
||||
{
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> 10,
|
||||
state -> DISABLED,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Aborting", featureName, v, t));
|
||||
REASON,
|
||||
state -> 10,
|
||||
state -> DISABLED,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Aborting", featureName, v, t));
|
||||
|
||||
assertTrue(guard.enabled(userClientState));
|
||||
|
||||
|
|
@ -109,10 +111,11 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testFailOnlyMaxThreshold() throws Throwable
|
||||
{
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> DISABLED,
|
||||
state -> 10,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Aborting", featureName, v, t));
|
||||
REASON,
|
||||
state -> DISABLED,
|
||||
state -> 10,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Aborting", featureName, v, t));
|
||||
|
||||
assertTrue(guard.enabled(userClientState));
|
||||
|
||||
|
|
@ -127,10 +130,11 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testMaxThresholdUsers() throws Throwable
|
||||
{
|
||||
MaxThreshold guard = new MaxThreshold("x",
|
||||
state -> 10,
|
||||
state -> 100,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Failure", featureName, v, t));
|
||||
REASON,
|
||||
state -> 10,
|
||||
state -> 100,
|
||||
(isWarn, featureName, v, t) -> format("%s: for %s, %s > %s",
|
||||
isWarn ? "Warning" : "Failure", featureName, v, t));
|
||||
|
||||
// value under both thresholds
|
||||
assertValid(() -> guard.guard(5, "x", false, null));
|
||||
|
|
@ -156,13 +160,14 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testDisabledMinThreshold() throws Throwable
|
||||
{
|
||||
Threshold.ErrorMessageProvider errorMessageProvider = (isWarn, what, v, t) -> "Should never trigger";
|
||||
testDisabledThreshold(new MinThreshold("x", state -> DISABLED, state -> DISABLED, errorMessageProvider));
|
||||
testDisabledThreshold(new MinThreshold("x", REASON, state -> DISABLED, state -> DISABLED, errorMessageProvider));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinThreshold() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
REASON,
|
||||
state -> 100,
|
||||
state -> 10,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
|
|
@ -189,6 +194,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testWarnOnlyMinThreshold() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
REASON,
|
||||
state -> 10,
|
||||
state -> DISABLED,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
|
|
@ -207,6 +213,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testFailOnlyMinThreshold() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
REASON,
|
||||
state -> DISABLED,
|
||||
state -> 10,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
|
|
@ -225,6 +232,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testMinThresholdUsers() throws Throwable
|
||||
{
|
||||
MinThreshold guard = new MinThreshold("x",
|
||||
REASON,
|
||||
state -> 100,
|
||||
state -> 10,
|
||||
(isWarn, what, v, t) -> format("%s: for %s, %s < %s",
|
||||
|
|
@ -253,23 +261,23 @@ public class GuardrailsTest extends GuardrailTester
|
|||
@Test
|
||||
public void testEnableFlag() throws Throwable
|
||||
{
|
||||
assertFails(() -> new EnableFlag("x", state -> false, "X").ensureEnabled(userClientState), "X is not allowed");
|
||||
assertValid(() -> new EnableFlag("x", state -> true, "X").ensureEnabled(userClientState));
|
||||
assertFails(() -> new EnableFlag("x", REASON, state -> false, "X").ensureEnabled(userClientState), "X is not allowed");
|
||||
assertValid(() -> new EnableFlag("x", REASON, state -> true, "X").ensureEnabled(userClientState));
|
||||
|
||||
assertFails(() -> new EnableFlag("x", state -> false, "X").ensureEnabled("Y", userClientState), "Y is not allowed");
|
||||
assertValid(() -> new EnableFlag("x", state -> true, "X").ensureEnabled("Y", userClientState));
|
||||
assertFails(() -> new EnableFlag("x", REASON, state -> false, "X").ensureEnabled("Y", userClientState), "Y is not allowed");
|
||||
assertValid(() -> new EnableFlag("x", REASON, state -> true, "X").ensureEnabled("Y", userClientState));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEnableFlagUsers() throws Throwable
|
||||
{
|
||||
EnableFlag enabled = new EnableFlag("x", state -> true, "X");
|
||||
EnableFlag enabled = new EnableFlag("x", REASON, state -> true, "X");
|
||||
assertValid(() -> enabled.ensureEnabled(null));
|
||||
assertValid(() -> enabled.ensureEnabled(userClientState));
|
||||
assertValid(() -> enabled.ensureEnabled(systemClientState));
|
||||
assertValid(() -> enabled.ensureEnabled(superClientState));
|
||||
|
||||
EnableFlag disabled = new EnableFlag("x", state -> false, "X");
|
||||
EnableFlag disabled = new EnableFlag("x", REASON, state -> false, "X");
|
||||
assertFails(() -> disabled.ensureEnabled(userClientState), "X is not allowed");
|
||||
assertValid(() -> disabled.ensureEnabled(systemClientState));
|
||||
assertValid(() -> disabled.ensureEnabled(superClientState));
|
||||
|
|
@ -280,6 +288,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
{
|
||||
// Using a sorted set below to ensure the order in the warning message checked below is not random
|
||||
Values<Integer> warned = new Values<>("x",
|
||||
REASON,
|
||||
state -> insertionOrderedSet(4, 6, 20),
|
||||
state -> Collections.emptySet(),
|
||||
state -> Collections.emptySet(),
|
||||
|
|
@ -300,6 +309,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
{
|
||||
// Using a sorted set below to ensure the order in the error message checked below are not random
|
||||
Values<Integer> ignored = new Values<>("x",
|
||||
REASON,
|
||||
state -> Collections.emptySet(),
|
||||
state -> insertionOrderedSet(4, 6, 20),
|
||||
state -> Collections.emptySet(),
|
||||
|
|
@ -334,6 +344,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
{
|
||||
// Using a sorted set below to ensure the order in the error message checked below are not random
|
||||
Values<Integer> disallowed = new Values<>("x",
|
||||
REASON,
|
||||
state -> Collections.emptySet(),
|
||||
state -> Collections.emptySet(),
|
||||
state -> insertionOrderedSet(4, 6, 20),
|
||||
|
|
@ -359,6 +370,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testValuesUsers() throws Throwable
|
||||
{
|
||||
Values<Integer> disallowed = new Values<>("x",
|
||||
REASON,
|
||||
state -> Collections.singleton(2),
|
||||
state -> Collections.singleton(3),
|
||||
state -> Collections.singleton(4),
|
||||
|
|
@ -396,6 +408,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testPredicates() throws Throwable
|
||||
{
|
||||
Predicates<Integer> guard = new Predicates<>("x",
|
||||
REASON,
|
||||
state -> x -> x > 10,
|
||||
state -> x -> x > 100,
|
||||
(isWarn, value) -> format("%s: %s", isWarn ? "Warning" : "Aborting", value));
|
||||
|
|
@ -412,6 +425,7 @@ public class GuardrailsTest extends GuardrailTester
|
|||
public void testPredicatesUsers() throws Throwable
|
||||
{
|
||||
Predicates<Integer> guard = new Predicates<>("x",
|
||||
REASON,
|
||||
state -> x -> x > 10,
|
||||
state -> x -> x > 100,
|
||||
(isWarn, value) -> format("%s: %s", isWarn ? "Warning" : "Aborting", value));
|
||||
|
|
|
|||
Loading…
Reference in New Issue