This is an automated email from the ASF dual-hosted git repository.
adelapena pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new 143a5e8 Add diagnostic events for guardrails
143a5e8 is described below
commit 143a5e8b064e442970182cfb349b4f0826683e85
Author: Andrés de la Peña <[email protected]>
AuthorDate: Thu Mar 3 18:17:38 2022 +0000
Add diagnostic events for guardrails
patch by Andrés de la Peña; reviewed by Berenguer Blasi and Stefan
Miklosovic for CASSANDRA-17197
Co-authored-by: Andrés de la Peña <[email protected]>
Co-authored-by: Aleksandr Sorokoumov <[email protected]>
---
CHANGES.txt | 1 +
conf/cassandra.yaml | 2 +-
src/java/org/apache/cassandra/cql3/Lists.java | 8 +-
src/java/org/apache/cassandra/cql3/Maps.java | 8 +-
src/java/org/apache/cassandra/cql3/Sets.java | 8 +-
.../restrictions/ClusteringColumnRestrictions.java | 2 +-
.../PartitionKeySingleRestrictionSet.java | 2 +-
.../cassandra/cql3/statements/SelectStatement.java | 4 +-
.../statements/schema/AlterTableStatement.java | 2 +-
.../statements/schema/CreateIndexStatement.java | 1 +
.../statements/schema/CreateKeyspaceStatement.java | 2 +-
.../statements/schema/CreateTableStatement.java | 4 +-
.../statements/schema/CreateViewStatement.java | 1 +
.../apache/cassandra/db/guardrails/Guardrail.java | 13 ++
.../cassandra/db/guardrails/GuardrailEvent.java | 73 +++++++
.../db/guardrails/GuardrailsDiagnostics.java | 63 ++++++
.../apache/cassandra/db/guardrails/Threshold.java | 38 ++--
.../cassandra/io/sstable/format/SSTableWriter.java | 4 +-
.../db/guardrails/GuardrailCollectionSizeTest.java | 17 ++
.../GuardrailInSelectCartesianProductTest.java | 5 +-
.../GuardrailItemsPerCollectionTest.java | 17 ++
.../guardrails/GuardrailTablePropertiesTest.java | 5 +
.../cassandra/db/guardrails/GuardrailTester.java | 220 +++++++++++++++++++--
.../guardrails/GuardrailsConfigProviderTest.java | 19 +-
.../cassandra/db/guardrails/GuardrailsTest.java | 92 +++++----
.../cassandra/db/guardrails/ThresholdTester.java | 40 +++-
26 files changed, 545 insertions(+), 106 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index db8d987..d0d021c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
4.1
+ * Add diagnostic events for guardrails (CASSANDRA-17197)
* Increase cqlsh version (CASSANDRA-17432)
* Update SUPPORTED_UPGRADE_PATHS to include 3.0 and 3.x to 4.1 paths and
remove obsolete tests (CASSANDRA-17362)
* Support DELETE in CQLSSTableWriter (CASSANDRA-14797)
diff --git a/conf/cassandra.yaml b/conf/cassandra.yaml
index ce79767..a2c4db8 100644
--- a/conf/cassandra.yaml
+++ b/conf/cassandra.yaml
@@ -1583,7 +1583,7 @@ drop_compact_storage_enabled: false
# abort_threshold_kb: 0
# Whether guardrails are enabled or not. Guardrails are disabled by default.
-# guardrails_enabled: true
+# guardrails_enabled: false
# Guardrail to warn or fail when creating more user keyspaces than threshold.
# The two thresholds default to -1 to disable.
# keyspaces_warn_threshold: -1
diff --git a/src/java/org/apache/cassandra/cql3/Lists.java
b/src/java/org/apache/cassandra/cql3/Lists.java
index dba9ba6..bdac046 100644
--- a/src/java/org/apache/cassandra/cql3/Lists.java
+++ b/src/java/org/apache/cassandra/cql3/Lists.java
@@ -513,7 +513,7 @@ public abstract class Lists
// Guardrails about collection size are only checked for the
added elements without considering
// already existent elements. This is done so to avoid
read-before-write, having additional checks
// during SSTable write.
- Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), params.clientState);
+ Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), false, params.clientState);
int dataSize = 0;
for (ByteBuffer buffer : elements)
@@ -522,13 +522,13 @@ public abstract class Lists
Cell<?> cell = params.addCell(column,
CellPath.create(uuid), buffer);
dataSize += cell.dataSize();
}
- Guardrails.collectionSize.guard(dataSize,
column.name.toString(), params.clientState);
+ Guardrails.collectionSize.guard(dataSize,
column.name.toString(), false, params.clientState);
}
else
{
- Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), params.clientState);
+ Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), false, params.clientState);
Cell<?> cell = params.addCell(column,
value.get(ProtocolVersion.CURRENT));
- Guardrails.collectionSize.guard(cell.dataSize(),
column.name.toString(), params.clientState);
+ Guardrails.collectionSize.guard(cell.dataSize(),
column.name.toString(), false, params.clientState);
}
}
}
diff --git a/src/java/org/apache/cassandra/cql3/Maps.java
b/src/java/org/apache/cassandra/cql3/Maps.java
index b5ee2ec..8bb7ae1 100644
--- a/src/java/org/apache/cassandra/cql3/Maps.java
+++ b/src/java/org/apache/cassandra/cql3/Maps.java
@@ -441,7 +441,7 @@ public abstract class Maps
// Guardrails about collection size are only checked for the
added elements without considering
// already existent elements. This is done so to avoid
read-before-write, having additional checks
// during SSTable write.
- Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), params.clientState);
+ Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), false, params.clientState);
int dataSize = 0;
for (Map.Entry<ByteBuffer, ByteBuffer> entry :
elements.entrySet())
@@ -449,13 +449,13 @@ public abstract class Maps
Cell<?> cell = params.addCell(column,
CellPath.create(entry.getKey()), entry.getValue());
dataSize += cell.dataSize();
}
- Guardrails.collectionSize.guard(dataSize,
column.name.toString(), params.clientState);
+ Guardrails.collectionSize.guard(dataSize,
column.name.toString(), false, params.clientState);
}
else
{
- Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), params.clientState);
+ Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), false, params.clientState);
Cell<?> cell = params.addCell(column,
value.get(ProtocolVersion.CURRENT));
- Guardrails.collectionSize.guard(cell.dataSize(),
column.name.toString(), params.clientState);
+ Guardrails.collectionSize.guard(cell.dataSize(),
column.name.toString(), false, params.clientState);
}
}
}
diff --git a/src/java/org/apache/cassandra/cql3/Sets.java
b/src/java/org/apache/cassandra/cql3/Sets.java
index 146f7b7..05ddf0a 100644
--- a/src/java/org/apache/cassandra/cql3/Sets.java
+++ b/src/java/org/apache/cassandra/cql3/Sets.java
@@ -368,7 +368,7 @@ public abstract class Sets
// Guardrails about collection size are only checked for the
added elements without considering
// already existent elements. This is done so to avoid
read-before-write, having additional checks
// during SSTable write.
- Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), params.clientState);
+ Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), false, params.clientState);
int dataSize = 0;
for (ByteBuffer bb : elements)
@@ -379,13 +379,13 @@ public abstract class Sets
Cell<?> cell = params.addCell(column, CellPath.create(bb),
ByteBufferUtil.EMPTY_BYTE_BUFFER);
dataSize += cell.dataSize();
}
- Guardrails.collectionSize.guard(dataSize,
column.name.toString(), params.clientState);
+ Guardrails.collectionSize.guard(dataSize,
column.name.toString(), false, params.clientState);
}
else
{
- Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), params.clientState);
+ Guardrails.itemsPerCollection.guard(elements.size(),
column.name.toString(), false, params.clientState);
Cell<?> cell = params.addCell(column,
value.get(ProtocolVersion.CURRENT));
- Guardrails.collectionSize.guard(cell.dataSize(),
column.name.toString(), params.clientState);
+ Guardrails.collectionSize.guard(cell.dataSize(),
column.name.toString(), false, params.clientState);
}
}
}
diff --git
a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
index bcf080e..c1d0c52 100644
---
a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
+++
b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringColumnRestrictions.java
@@ -111,7 +111,7 @@ final class ClusteringColumnRestrictions extends
RestrictionSetWrapper
r.appendTo(builder, options);
if (hasIN() && Guardrails.inSelectCartesianProduct.enabled(state))
- Guardrails.inSelectCartesianProduct.guard(builder.buildSize(),
"clustering key", state);
+ Guardrails.inSelectCartesianProduct.guard(builder.buildSize(),
"clustering key", false, state);
if (builder.hasMissingElements())
break;
diff --git
a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
index a137175..a6f227a 100644
---
a/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
+++
b/src/java/org/apache/cassandra/cql3/restrictions/PartitionKeySingleRestrictionSet.java
@@ -88,7 +88,7 @@ final class PartitionKeySingleRestrictionSet extends
RestrictionSetWrapper imple
r.appendTo(builder, options);
if (hasIN() && Guardrails.inSelectCartesianProduct.enabled(state))
- Guardrails.inSelectCartesianProduct.guard(builder.buildSize(),
"partition key", state);
+ Guardrails.inSelectCartesianProduct.guard(builder.buildSize(),
"partition key", false, state);
if (builder.hasMissingElements())
break;
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 54ea8c8..1637ba5 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -394,7 +394,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement
int userLimit,
long queryStartNanoTime) throws
RequestValidationException, RequestExecutionException
{
- Guardrails.pageSize.guard(pageSize, table(), state.getClientState());
+ Guardrails.pageSize.guard(pageSize, table(), false,
state.getClientState());
if (aggregationSpec != null)
{
@@ -579,7 +579,7 @@ public class SelectStatement implements
CQLStatement.SingleKeyspaceCqlStatement
if (restrictions.keyIsInRelation())
{
- Guardrails.partitionKeysInSelect.guard(keys.size(), table.name,
state);
+ Guardrails.partitionKeysInSelect.guard(keys.size(), table.name,
false, state);
}
ClusteringIndexFilter filter = makeClusteringIndexFilter(options,
state, columnFilter);
diff --git
a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
index 61cac8a..f6411c4 100644
---
a/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
+++
b/src/java/org/apache/cassandra/cql3/statements/schema/AlterTableStatement.java
@@ -187,7 +187,7 @@ public abstract class AlterTableStatement extends
AlterSchemaStatement
Views.Builder viewsBuilder = keyspace.views.unbuild();
newColumns.forEach(c -> addColumn(keyspace, table, c,
tableBuilder, viewsBuilder));
- Guardrails.columnsPerTable.guard(tableBuilder.numColumns(),
tableName, state);
+ Guardrails.columnsPerTable.guard(tableBuilder.numColumns(),
tableName, false, state);
TableMetadata tableMetadata = tableBuilder.build();
tableMetadata.validate();
diff --git
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
index 56cc2f9..686a9cd 100644
---
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
+++
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateIndexStatement.java
@@ -117,6 +117,7 @@ public final class CreateIndexStatement extends
AlterSchemaStatement
Strings.isNullOrEmpty(indexName)
? String.format("on table
%s", table.name)
: String.format("%s on table
%s", indexName, table.name),
+ false,
state);
List<IndexTarget> indexTargets =
Lists.newArrayList(transform(rawIndexTargets, t -> t.prepare(table)));
diff --git
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
index a6036a3..256b33b 100644
---
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
+++
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateKeyspaceStatement.java
@@ -117,7 +117,7 @@ public final class CreateKeyspaceStatement extends
AlterSchemaStatement
super.validate(state);
// Guardrail on number of keyspaces
- Guardrails.keyspaces.guard(Schema.instance.getUserKeyspaces().size() +
1, keyspaceName, state);
+ Guardrails.keyspaces.guard(Schema.instance.getUserKeyspaces().size() +
1, keyspaceName, false, state);
}
@Override
diff --git
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
index 8ab8383..6d65c5a 100644
---
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
+++
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateTableStatement.java
@@ -133,7 +133,7 @@ public final class CreateTableStatement extends
AlterSchemaStatement
Guardrails.tableProperties.guard(attrs.updatedProperties(),
attrs::removeProperty, state);
// Guardrail on columns per table
- Guardrails.columnsPerTable.guard(rawColumns.size(), tableName,
state);
+ Guardrails.columnsPerTable.guard(rawColumns.size(), tableName,
false, state);
// Guardrail on number of tables
if (Guardrails.tables.enabled(state))
@@ -144,7 +144,7 @@ public final class CreateTableStatement extends
AlterSchemaStatement
.map(Keyspace::open)
.mapToInt(keyspace ->
keyspace.getColumnFamilyStores().size())
.sum();
- Guardrails.tables.guard(totalUserTables + 1, tableName, state);
+ Guardrails.tables.guard(totalUserTables + 1, tableName, false,
state);
}
}
}
diff --git
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
index 4297d9c..e368d24 100644
---
a/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
+++
b/src/java/org/apache/cassandra/cql3/statements/schema/CreateViewStatement.java
@@ -157,6 +157,7 @@ public final class CreateViewStatement extends
AlterSchemaStatement
Iterable<ViewMetadata> tableViews = keyspace.views.forTable(table.id);
Guardrails.materializedViewsPerTable.guard(Iterables.size(tableViews)
+ 1,
String.format("%s on table
%s", viewName, table.name),
+ false,
state);
if (table.params.gcGraceSeconds == 0)
diff --git a/src/java/org/apache/cassandra/db/guardrails/Guardrail.java
b/src/java/org/apache/cassandra/db/guardrails/Guardrail.java
index 334d956..d49131d 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Guardrail.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Guardrail.java
@@ -44,6 +44,7 @@ public abstract class Guardrail
{
protected static final NoSpamLogger logger =
NoSpamLogger.getLogger(LoggerFactory.getLogger(Guardrail.class),
10,
TimeUnit.MINUTES);
+ protected static final String REDACTED = "<redacted>";
/** A name identifying the guardrail (mainly for shipping with diagnostic
events). */
public final String name;
@@ -69,6 +70,11 @@ public abstract class Guardrail
protected void warn(String message)
{
+ warn(message, message);
+ }
+
+ protected void warn(String message, String redactedMessage)
+ {
message = decorateMessage(message);
logger.warn(message);
@@ -77,10 +83,16 @@ public abstract class Guardrail
ClientWarn.instance.warn(message);
// Similarly, tracing will also ignore the message if we're not
running tracing on the current thread.
Tracing.trace(message);
+ GuardrailsDiagnostics.warned(name, decorateMessage(redactedMessage));
}
protected void fail(String message, @Nullable ClientState state)
{
+ fail(message, message, state);
+ }
+
+ protected void fail(String message, String redactedMessage, @Nullable
ClientState state)
+ {
message = decorateMessage(message);
logger.error(message);
@@ -89,6 +101,7 @@ public abstract class Guardrail
ClientWarn.instance.warn(message);
// Similarly, tracing will also ignore the message if we're not
running tracing on the current thread.
Tracing.trace(message);
+ GuardrailsDiagnostics.failed(name, decorateMessage(redactedMessage));
if (state != null)
throw new GuardrailViolatedException(message);
diff --git a/src/java/org/apache/cassandra/db/guardrails/GuardrailEvent.java
b/src/java/org/apache/cassandra/db/guardrails/GuardrailEvent.java
new file mode 100644
index 0000000..30eaafa
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailEvent.java
@@ -0,0 +1,73 @@
+/*
+ * 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.io.Serializable;
+import java.util.HashMap;
+
+import org.apache.cassandra.diag.DiagnosticEvent;
+
+/**
+ * {@link DiagnosticEvent} implementation for guardrail activation events.
+ */
+final class GuardrailEvent extends DiagnosticEvent
+{
+ enum GuardrailEventType
+ {
+ WARNED, FAILED
+ }
+
+ /** The type of activation, which is a warning or a failure. */
+ private final GuardrailEventType type;
+
+ /** The name that identifies the activated guardrail. */
+ private final String name;
+
+ /** The warn/fail message emitted by the activated guardrail. */
+ private final String message;
+
+ /**
+ * Creates new guardrail activation event.
+ *
+ * @param type The type of activation, which is warning or a failure.
+ * @param name The name that identifies the activated guardrail.
+ * @param message The warn/fail message emitted by the activated guardrail.
+ */
+ GuardrailEvent(GuardrailEventType type, String name, String message)
+ {
+ this.type = type;
+ this.name = name;
+ this.message = message;
+ }
+
+ @Override
+ public Enum<GuardrailEventType> getType()
+ {
+ return type;
+ }
+
+ @Override
+ public HashMap<String, Serializable> toMap()
+ {
+ HashMap<String, Serializable> ret = new HashMap<>();
+ ret.put("name", name);
+ ret.put("message", message);
+ return ret;
+ }
+}
diff --git
a/src/java/org/apache/cassandra/db/guardrails/GuardrailsDiagnostics.java
b/src/java/org/apache/cassandra/db/guardrails/GuardrailsDiagnostics.java
new file mode 100644
index 0000000..2905afa
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/guardrails/GuardrailsDiagnostics.java
@@ -0,0 +1,63 @@
+/*
+ * 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 org.apache.cassandra.db.guardrails.GuardrailEvent.GuardrailEventType;
+import org.apache.cassandra.diag.DiagnosticEventService;
+
+/**
+ * Utility methods for {@link GuardrailEvent} activities.
+ */
+final class GuardrailsDiagnostics
+{
+ private static final DiagnosticEventService service =
DiagnosticEventService.instance();
+
+ private GuardrailsDiagnostics()
+ {
+ }
+
+ /**
+ * Creates a new diagnostic event for the activation of the soft/warn
limit of a guardrail.
+ *
+ * @param name The name that identifies the activated guardrail.
+ * @param message The warning message emitted by the activated guardrail.
+ */
+ static void warned(String name, String message)
+ {
+ if (isEnabled(GuardrailEventType.WARNED))
+ service.publish(new GuardrailEvent(GuardrailEventType.WARNED,
name, message));
+ }
+
+ /**
+ * Creates a new diagnostic event for the activation of the hard/fail
limit of a guardrail.
+ *
+ * @param name The name that identifies the activated guardrail.
+ * @param message The failure message emitted by the activated guardrail.
+ */
+ static void failed(String name, String message)
+ {
+ if (isEnabled(GuardrailEventType.FAILED))
+ service.publish(new GuardrailEvent(GuardrailEventType.FAILED,
name, message));
+ }
+
+ private static boolean isEnabled(GuardrailEventType type)
+ {
+ return service.isEnabled(GuardrailEvent.class, type);
+ }
+}
diff --git a/src/java/org/apache/cassandra/db/guardrails/Threshold.java
b/src/java/org/apache/cassandra/db/guardrails/Threshold.java
index 3e63cb3..f88d1e0 100644
--- a/src/java/org/apache/cassandra/db/guardrails/Threshold.java
+++ b/src/java/org/apache/cassandra/db/guardrails/Threshold.java
@@ -64,6 +64,11 @@ public class Threshold extends Guardrail
thresholdValue);
}
+ private String redactedErrMsg(boolean isWarning, long value, long
thresholdValue)
+ {
+ return errMsg(isWarning, REDACTED, value, thresholdValue);
+ }
+
private long failValue(ClientState state)
{
long failValue = failThreshold.applyAsLong(state);
@@ -106,17 +111,16 @@ public class Threshold extends Guardrail
/**
* Apply the guardrail to the provided value, warning or failing if
appropriate.
*
- * @param value The value to check.
- * @param what A string describing what {@code value} is a value of. This
is used in the error message if the
- * guardrail is triggered. For instance, say the guardrail
guards the size of column values, then this
- * argument must describe which column of which row is
triggering the guardrail for convenience.
- * @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, although it won't
- * throw any exception if the failure threshold is exceeded.
This is so because checks without an
- * associated client come from asynchronous processes such as
compaction, and we don't want to
- * interrupt such processes.
+ * @param value The value to check.
+ * @param what A string describing what {@code value} is a
value of. This is used in the error message
+ * if the guardrail is triggered. For instance,
say the guardrail guards the size of column
+ * values, then this argument must describe which
column of which row is triggering the
+ * guardrail for convenience.
+ * @param containsUserData whether the {@code what} contains user data
that should be redacted on external systems.
+ * @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.
*/
- public void guard(long value, String what, @Nullable ClientState state)
+ public void guard(long value, String what, boolean containsUserData,
@Nullable ClientState state)
{
if (!enabled(state))
return;
@@ -124,23 +128,25 @@ public class Threshold extends Guardrail
long failValue = failValue(state);
if (value > failValue)
{
- triggerFail(value, failValue, what, state);
+ triggerFail(value, failValue, what, containsUserData, state);
return;
}
long warnValue = warnValue(state);
if (value > warnValue)
- triggerWarn(value, warnValue, what);
+ triggerWarn(value, warnValue, what, containsUserData);
}
- private void triggerFail(long value, long failValue, String what,
ClientState state)
+ private void triggerFail(long value, long failValue, String what, boolean
containsUserData, ClientState state)
{
- fail(errMsg(false, what, value, failValue), state);
+ String fullMessage = errMsg(false, what, value, failValue);
+ fail(fullMessage, containsUserData ? redactedErrMsg(false, value,
failValue) : fullMessage, state);
}
- private void triggerWarn(long value, long warnValue, String what)
+ private void triggerWarn(long value, long warnValue, String what, boolean
containsUserData)
{
- warn(errMsg(true, what, value, warnValue));
+ String fullMessage = errMsg(true, what, value, warnValue);
+ warn(fullMessage, containsUserData ? redactedErrMsg(true, value,
warnValue) : fullMessage);
}
/**
diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableWriter.java
b/src/java/org/apache/cassandra/io/sstable/format/SSTableWriter.java
index 0ff3e46..93447c2 100644
--- a/src/java/org/apache/cassandra/io/sstable/format/SSTableWriter.java
+++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableWriter.java
@@ -430,8 +430,8 @@ public abstract class SSTableWriter extends SSTable
implements Transactional
column.name.toString(),
keyString,
metadata);
- Guardrails.collectionSize.guard(cellsSize, msg, null);
- Guardrails.itemsPerCollection.guard(cellsCount, msg, null);
+ Guardrails.collectionSize.guard(cellsSize, msg, true, null);
+ Guardrails.itemsPerCollection.guard(cellsCount, msg, true, null);
}
}
}
diff --git
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailCollectionSizeTest.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailCollectionSizeTest.java
index f867783..482e37b 100644
---
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailCollectionSizeTest.java
+++
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailCollectionSizeTest.java
@@ -25,6 +25,7 @@ import java.util.Collections;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import org.junit.After;
import org.junit.Test;
import org.apache.cassandra.db.marshal.BytesType;
@@ -55,6 +56,14 @@ public class GuardrailCollectionSizeTest extends
ThresholdTester
Guardrails::getCollectionSizeFailThresholdInKiB);
}
+ @After
+ public void after()
+ {
+ // immediately drop the created table so its async cleanup doesn't
interfere with the next tests
+ if (currentTable() != null)
+ dropTable("DROP TABLE %s");
+ }
+
@Test
public void testSetSize() throws Throwable
{
@@ -206,6 +215,14 @@ public class GuardrailCollectionSizeTest extends
ThresholdTester
assertWarns("UPDATE %s SET v = v + ? WHERE k = 6",
map(allocate(FAIL_THRESHOLD / 4 + 1), allocate(FAIL_THRESHOLD / 4)));
}
+ @Override
+ protected String createTable(String query)
+ {
+ String table = super.createTable(query);
+ disableCompaction();
+ return table;
+ }
+
private void assertValid(String query, ByteBuffer... values) throws
Throwable
{
assertValid(execute(query, values));
diff --git
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailInSelectCartesianProductTest.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailInSelectCartesianProductTest.java
index 6575084..ef61ee4 100644
---
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailInSelectCartesianProductTest.java
+++
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailInSelectCartesianProductTest.java
@@ -19,6 +19,7 @@
package org.apache.cassandra.db.guardrails;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -167,9 +168,9 @@ public class GuardrailInSelectCartesianProductTest extends
ThresholdTester
else if (keys > WARN_THRESHOLD)
{
if (clusterings > FAIL_THRESHOLD)
- assertFails(function, keysWarnMessage, clusteringsFailMessage);
+ assertFails(function, Arrays.asList(keysWarnMessage,
clusteringsFailMessage));
else if (clusterings > WARN_THRESHOLD)
- assertWarns(function, keysWarnMessage, clusteringsWarnMessage);
+ assertWarns(function, Arrays.asList(keysWarnMessage,
clusteringsWarnMessage));
else
assertWarns(function, keysWarnMessage);
}
diff --git
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailItemsPerCollectionTest.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailItemsPerCollectionTest.java
index 66424aa..a13e9b3 100644
---
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailItemsPerCollectionTest.java
+++
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailItemsPerCollectionTest.java
@@ -24,6 +24,7 @@ import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import org.junit.After;
import org.junit.Test;
import org.apache.cassandra.db.marshal.Int32Type;
@@ -52,6 +53,14 @@ public class GuardrailItemsPerCollectionTest extends
ThresholdTester
Guardrails::getItemsPerCollectionFailThreshold);
}
+ @After
+ public void after()
+ {
+ // immediately drop the created table so its async cleanup doesn't
interfere with the next tests
+ if (currentTable() != null)
+ dropTable("DROP TABLE %s");
+ }
+
@Test
public void testSetSize() throws Throwable
{
@@ -208,6 +217,14 @@ public class GuardrailItemsPerCollectionTest extends
ThresholdTester
assertWarns("UPDATE %s SET v = v + ? WHERE k = 4", map(1,
FAIL_THRESHOLD + 1), FAIL_THRESHOLD);
}
+ @Override
+ protected String createTable(String query)
+ {
+ String table = super.createTable(query);
+ disableCompaction();
+ return table;
+ }
+
private void assertValid(String query, ByteBuffer collection) throws
Throwable
{
assertValid(execute(query, collection));
diff --git
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailTablePropertiesTest.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTablePropertiesTest.java
index 018700a..5754c51 100644
---
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailTablePropertiesTest.java
+++
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTablePropertiesTest.java
@@ -49,6 +49,11 @@ public class GuardrailTablePropertiesTest extends
GuardrailTester
private static final String IGNORED_PROPERTY_NAME =
"table_properties_ignored";
private static final String DISALLOWED_PROPERTY_NAME =
"table_properties_disallowed";
+ public GuardrailTablePropertiesTest()
+ {
+ super(Guardrails.tableProperties);
+ }
+
@Before
public void before()
{
diff --git a/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java
index 2592f85..4540a51 100644
--- a/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java
+++ b/test/unit/org/apache/cassandra/db/guardrails/GuardrailTester.java
@@ -18,11 +18,15 @@
package org.apache.cassandra.db.guardrails;
+import java.io.Serializable;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.TreeSet;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -31,18 +35,22 @@ import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableSet;
+import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.apache.cassandra.auth.AuthenticatedUser;
import org.apache.cassandra.auth.CassandraRoleManager;
+import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.cql3.CQLStatement;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.QueryOptions;
import org.apache.cassandra.cql3.QueryProcessor;
import org.apache.cassandra.db.ConsistencyLevel;
+import org.apache.cassandra.db.guardrails.GuardrailEvent.GuardrailEventType;
import org.apache.cassandra.db.view.View;
+import org.apache.cassandra.diag.DiagnosticEventService;
import org.apache.cassandra.index.sasi.SASIIndex;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.ClientWarn;
@@ -55,6 +63,7 @@ import org.assertj.core.api.Assertions;
import static java.lang.String.format;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -70,12 +79,13 @@ public abstract class GuardrailTester extends CQLTester
protected static ClientState systemClientState, userClientState,
superClientState;
- /**
- * The tested guardrail, if we are testing a specific one.
- */
+ /** The tested guardrail, if we are testing a specific one. */
@Nullable
protected final Guardrail guardrail;
+ /** A listener for emitted diagnostic events. */
+ protected final Listener listener;
+
public GuardrailTester()
{
this(null);
@@ -84,6 +94,7 @@ public abstract class GuardrailTester extends CQLTester
public GuardrailTester(@Nullable Guardrail guardrail)
{
this.guardrail = guardrail;
+ this.listener = new Listener();
}
@BeforeClass
@@ -93,6 +104,7 @@ public abstract class GuardrailTester extends CQLTester
requireAuthentication();
requireNetwork();
guardrails().setEnabled(true);
+ DatabaseDescriptor.setDiagnosticEventsEnabled(true);
systemClientState = ClientState.forInternalCalls();
userClientState =
ClientState.forExternalCalls(InetSocketAddress.createUnresolved("127.0.0.1",
123));
@@ -115,6 +127,14 @@ public abstract class GuardrailTester extends CQLTester
execute(userClientState, useKeyspaceQuery);
execute(systemClientState, useKeyspaceQuery);
execute(superClientState, useKeyspaceQuery);
+
+ DiagnosticEventService.instance().subscribe(GuardrailEvent.class,
listener);
+ }
+
+ @After
+ public void afterGuardrailTest() throws Throwable
+ {
+ DiagnosticEventService.instance().unsubscribe(listener);
}
static Guardrails guardrails()
@@ -176,6 +196,8 @@ public abstract class GuardrailTester extends CQLTester
{
function.apply();
assertEmptyWarnings();
+ listener.assertNotWarned();
+ listener.assertNotFailed();
}
catch (GuardrailViolatedException e)
{
@@ -184,6 +206,7 @@ public abstract class GuardrailTester extends CQLTester
finally
{
ClientWarn.instance.resetWarnings();
+ listener.clear();
}
}
@@ -192,7 +215,42 @@ public abstract class GuardrailTester extends CQLTester
assertValid(() -> execute(userClientState, query));
}
- protected void assertWarns(CheckedFunction function, String... messages)
throws Throwable
+ protected void assertWarns(String query, String message) throws Throwable
+ {
+ assertWarns(query, message, message);
+ }
+
+ protected void assertWarns(String query, String message, String
redactedMessage) throws Throwable
+ {
+ assertWarns(query, Collections.singletonList(message),
Collections.singletonList(redactedMessage));
+ }
+
+ protected void assertWarns(String query, List<String> messages) throws
Throwable
+ {
+ assertWarns(() -> execute(userClientState, query), messages, messages);
+ }
+
+ protected void assertWarns(String query, List<String> messages,
List<String> redactedMessages) throws Throwable
+ {
+ assertWarns(() -> execute(userClientState, query), messages,
redactedMessages);
+ }
+
+ protected void assertWarns(CheckedFunction function, String message)
throws Throwable
+ {
+ assertWarns(function, message, message);
+ }
+
+ protected void assertWarns(CheckedFunction function, String message,
String redactedMessage) throws Throwable
+ {
+ assertWarns(function, Collections.singletonList(message),
Collections.singletonList(redactedMessage));
+ }
+
+ protected void assertWarns(CheckedFunction function, List<String>
messages) throws Throwable
+ {
+ assertWarns(function, messages, messages);
+ }
+
+ protected void assertWarns(CheckedFunction function, List<String>
messages, List<String> redactedMessages) throws Throwable
{
// We use client warnings to check we properly warn as this is the
most convenient. Technically,
// this doesn't validate we also log the warning, but that's probably
fine ...
@@ -201,24 +259,67 @@ public abstract class GuardrailTester extends CQLTester
{
function.apply();
assertWarnings(messages);
+ listener.assertWarned(redactedMessages);
+ listener.assertNotFailed();
}
finally
{
ClientWarn.instance.resetWarnings();
+ listener.clear();
}
}
- protected void assertWarns(String query, String... messages) throws
Throwable
+ protected void assertFails(String query, String message) throws Throwable
+ {
+ assertFails(query, message, message);
+ }
+
+ protected void assertFails(String query, String message, String
redactedMessage) throws Throwable
+ {
+ assertFails(query, Collections.singletonList(message),
Collections.singletonList(redactedMessage));
+ }
+
+ protected void assertFails(String query, List<String> messages) throws
Throwable
+ {
+ assertFails(query, messages, messages);
+ }
+
+ protected void assertFails(String query, List<String> messages,
List<String> redactedMessages) throws Throwable
+ {
+ assertFails(() -> execute(userClientState, query), messages,
redactedMessages);
+ }
+
+ protected void assertFails(CheckedFunction function, String message)
throws Throwable
+ {
+ assertFails(function, message, message);
+ }
+
+ protected void assertFails(CheckedFunction function, String message,
String redactedMessage) throws Throwable
+ {
+ assertFails(function, true, message, redactedMessage);
+ }
+
+ protected void assertFails(CheckedFunction function, boolean thrown,
String message) throws Throwable
+ {
+ assertFails(function, thrown, message, message);
+ }
+
+ protected void assertFails(CheckedFunction function, boolean thrown,
String message, String redactedMessage) throws Throwable
{
- assertWarns(() -> execute(userClientState, query), messages);
+ assertFails(function, thrown, Collections.singletonList(message),
Collections.singletonList(redactedMessage));
}
- protected void assertFails(CheckedFunction function, String... messages)
throws Throwable
+ protected void assertFails(CheckedFunction function, List<String>
messages) throws Throwable
{
- assertFails(function, true, messages);
+ assertFails(function, messages, messages);
}
- protected void assertFails(CheckedFunction function, boolean thrown,
String... messages) throws Throwable
+ protected void assertFails(CheckedFunction function, List<String>
messages, List<String> redactedMessages) throws Throwable
+ {
+ assertFails(function, true, messages, redactedMessages);
+ }
+
+ protected void assertFails(CheckedFunction function, boolean thrown,
List<String> messages, List<String> redactedMessages) throws Throwable
{
ClientWarn.instance.captureWarnings();
try
@@ -233,7 +334,7 @@ public abstract class GuardrailTester extends CQLTester
assertTrue("Expect no exception thrown", thrown);
// the last message is the one raising the guardrail failure, the
previous messages are warnings
- String failMessage = messages[messages.length - 1];
+ String failMessage = messages.get(messages.size() - 1);
if (guardrail != null)
{
@@ -246,16 +347,22 @@ public abstract class GuardrailTester extends CQLTester
e.getMessage().contains(failMessage));
assertWarnings(messages);
+ if (messages.size() > 1)
+ listener.assertWarned(redactedMessages.subList(0,
messages.size() - 1));
+ else
+ listener.assertNotWarned();
+ listener.assertFailed(redactedMessages.get(messages.size() - 1));
}
finally
{
ClientWarn.instance.resetWarnings();
+ listener.clear();
}
}
protected void assertFails(String query, String... messages) throws
Throwable
{
- assertFails(() -> execute(userClientState, query), messages);
+ assertFails(() -> execute(userClientState, query),
Arrays.asList(messages));
}
protected void assertThrows(CheckedFunction function, Class<? extends
Throwable> exception, String message)
@@ -275,20 +382,19 @@ public abstract class GuardrailTester extends CQLTester
}
}
- private void assertWarnings(String... messages)
+ private void assertWarnings(List<String> messages)
{
List<String> warnings = getWarnings();
assertFalse("Expected to warn, but no warning was received", warnings
== null || warnings.isEmpty());
- assertEquals(format("Expected %d warnings but got %d: %s",
messages.length, warnings.size(), warnings),
- messages.length,
+ assertEquals(format("Expected %d warnings but got %d: %s",
messages.size(), warnings.size(), warnings),
+ messages.size(),
warnings.size());
- for (int i = 0; i < messages.length; i++)
+ for (int i = 0; i < messages.size(); i++)
{
+ String message = messages.get(i);
String warning = warnings.get(i);
-
- String message = messages[i];
if (guardrail != null)
{
String prefix = guardrail.decorateMessage("");
@@ -383,4 +489,84 @@ public abstract class GuardrailTester extends CQLTester
{
return String.join(",", (new
TreeSet<>(ImmutableSet.copyOf((csv.split(","))))));
}
+
+ /**
+ * A listener for guardrails diagnostic events.
+ */
+ public class Listener implements Consumer<GuardrailEvent>
+ {
+ private final List<String> warnings = new CopyOnWriteArrayList<>();
+ private final List<String> failures = new CopyOnWriteArrayList<>();
+
+ @Override
+ public void accept(GuardrailEvent event)
+ {
+ assertNotNull(event);
+ Map<String, Serializable> map = event.toMap();
+
+ if (guardrail != null)
+ assertEquals(guardrail.name, map.get("name"));
+
+ GuardrailEventType type = (GuardrailEventType) event.getType();
+ String message = map.toString();
+
+ switch (type)
+ {
+ case WARNED:
+ warnings.add(message);
+ break;
+ case FAILED:
+ failures.add(message);
+ break;
+ default:
+ fail("Unexpected diagnostic event:" + type);
+ }
+ }
+
+ public void clear()
+ {
+ warnings.clear();
+ failures.clear();
+ }
+
+ public void assertNotWarned()
+ {
+ assertTrue(format("Expect no warning diagnostic events but got
%s", warnings), warnings.isEmpty());
+ }
+
+ public void assertWarned(List<String> messages)
+ {
+ assertFalse("Expected to emit warning diagnostic event, but no
warning was emitted", warnings.isEmpty());
+ assertEquals(format("Expected %d warning diagnostic events but got
%d: %s)", messages.size(), warnings.size(), warnings),
+ messages.size(), warnings.size());
+
+ for (int i = 0; i < messages.size(); i++)
+ {
+ String message = messages.get(i);
+ String warning = warnings.get(i);
+ assertTrue(format("Warning diagnostic event '%s' does not
contain expected message '%s'", warning, message),
+ warning.contains(message));
+ }
+ }
+
+ public void assertNotFailed()
+ {
+ assertTrue(format("Expect no failure diagnostic events but got
%s", failures), failures.isEmpty());
+ }
+
+ public void assertFailed(String... messages)
+ {
+ assertFalse("Expected to emit failure diagnostic event, but no
failure was emitted", failures.isEmpty());
+ assertEquals(format("Expected %d failure diagnostic events but got
%d: %s)", messages.length, failures.size(), failures),
+ messages.length, failures.size());
+
+ for (int i = 0; i < messages.length; i++)
+ {
+ String message = messages[i];
+ String failure = failures.get(i);
+ assertTrue(format("Failure diagnostic event '%s' does not
contain expected message '%s'", failure, message),
+ failure.contains(message));
+ }
+ }
+ }
}
diff --git
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
index cf2e40f..991dcf7 100644
---
a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
+++
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsConfigProviderTest.java
@@ -42,12 +42,19 @@ public class GuardrailsConfigProviderTest extends
GuardrailTester
(isWarn, what, v, t) -> format("%s:
for %s, %s > %s",
isWarn
? "Warning" : "Aborting", what, v, t));
- assertValid(() -> guard.guard(5, "Z", userClientState));
- assertWarns(() -> guard.guard(25, "A", userClientState), "Warning: for
A, 25 > 10");
- assertWarns(() -> guard.guard(100, "B", userClientState), "Warning:
for B, 100 > 10");
- assertFails(() -> guard.guard(101, "X", userClientState), "Aborting:
for X, 101 > 100");
- assertFails(() -> guard.guard(200, "Y", userClientState), "Aborting:
for Y, 200 > 100");
- assertValid(() -> guard.guard(5, "Z", userClientState));
+ assertValid(() -> guard.guard(5, "Z", false, userClientState));
+ assertWarns(() -> guard.guard(25, "A", false, userClientState),
"Warning: for A, 25 > 10");
+ assertWarns(() -> guard.guard(100, "B", false, userClientState),
"Warning: for B, 100 > 10");
+ assertFails(() -> guard.guard(101, "X", false, userClientState),
"Aborting: for X, 101 > 100");
+ assertFails(() -> guard.guard(200, "Y", false, userClientState),
"Aborting: for Y, 200 > 100");
+ assertValid(() -> guard.guard(5, "Z", false, userClientState));
+
+ assertValid(() -> guard.guard(5, "Z", true, userClientState));
+ assertWarns(() -> guard.guard(25, "A", true, userClientState),
"Warning: for A, 25 > 10", "Warning: for <redacted>, 25 > 10");
+ assertWarns(() -> guard.guard(100, "B", true, userClientState),
"Warning: for B, 100 > 10", "Warning: for <redacted>, 100 > 10");
+ assertFails(() -> guard.guard(101, "X", true, userClientState),
"Aborting: for X, 101 > 100", "Aborting: for <redacted>, 101 > 100");
+ assertFails(() -> guard.guard(200, "Y", true, userClientState),
"Aborting: for Y, 200 > 100", "Aborting: for <redacted>, 200 > 100");
+ assertValid(() -> guard.guard(5, "Z", true, userClientState));
Assertions.assertThatThrownBy(() ->
GuardrailsConfigProvider.build("unexistent_class"))
.isInstanceOf(ConfigurationException.class)
diff --git a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java
b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java
index 70c4cfc..b98659e 100644
--- a/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java
+++ b/test/unit/org/apache/cassandra/db/guardrails/GuardrailsTest.java
@@ -50,17 +50,20 @@ public class GuardrailsTest extends GuardrailTester
{
assertFalse(guard.enabled(userClientState));
- assertValid(() -> guard.guard(5, "Z", null));
- assertValid(() -> guard.guard(25, "A", userClientState));
- assertValid(() -> guard.guard(100, "B", userClientState));
- assertValid(() -> guard.guard(101, "X", userClientState));
- assertValid(() -> guard.guard(200, "Y", userClientState));
+ for (boolean containsUserData : Arrays.asList(true, false))
+ {
+ assertValid(() -> guard.guard(5, "Z", containsUserData, null));
+ assertValid(() -> guard.guard(25, "A", containsUserData,
userClientState));
+ assertValid(() -> guard.guard(100, "B", containsUserData,
userClientState));
+ assertValid(() -> guard.guard(101, "X", containsUserData,
userClientState));
+ assertValid(() -> guard.guard(200, "Y", containsUserData,
userClientState));
+ }
}
@Test
public void testThreshold() throws Throwable
{
- Threshold guard = new Threshold("x",
+ Threshold guard = new Threshold("x",
state -> 10,
state -> 100,
(isWarn, what, v, t) -> format("%s:
for %s, %s > %s",
@@ -68,12 +71,19 @@ public class GuardrailsTest extends GuardrailTester
assertTrue(guard.enabled(userClientState));
- assertValid(() -> guard.guard(5, "Z", userClientState));
- assertWarns(() -> guard.guard(25, "A", userClientState), "Warning: for
A, 25 > 10");
- assertWarns(() -> guard.guard(100, "B", userClientState), "Warning:
for B, 100 > 10");
- assertFails(() -> guard.guard(101, "X", userClientState), "Aborting:
for X, 101 > 100");
- assertFails(() -> guard.guard(200, "Y", userClientState), "Aborting:
for Y, 200 > 100");
- assertValid(() -> guard.guard(5, "Z", userClientState));
+ assertValid(() -> guard.guard(5, "Z", false, userClientState));
+ assertWarns(() -> guard.guard(25, "A", false, userClientState),
"Warning: for A, 25 > 10");
+ assertWarns(() -> guard.guard(100, "B", false, userClientState),
"Warning: for B, 100 > 10");
+ assertFails(() -> guard.guard(101, "X", false, userClientState),
"Aborting: for X, 101 > 100");
+ assertFails(() -> guard.guard(200, "Y", false, userClientState),
"Aborting: for Y, 200 > 100");
+ assertValid(() -> guard.guard(5, "Z", false, userClientState));
+
+ assertValid(() -> guard.guard(5, "Z", true, userClientState));
+ assertWarns(() -> guard.guard(25, "A", true, userClientState),
"Warning: for A, 25 > 10", "Warning: for <redacted>, 25 > 10");
+ assertWarns(() -> guard.guard(100, "B", true, userClientState),
"Warning: for B, 100 > 10", "Warning: for <redacted>, 100 > 10");
+ assertFails(() -> guard.guard(101, "X", true, userClientState),
"Aborting: for X, 101 > 100", "Aborting: for <redacted>, 101 > 100");
+ assertFails(() -> guard.guard(200, "Y", true, userClientState),
"Aborting: for Y, 200 > 100", "Aborting: for <redacted>, 200 > 100");
+ assertValid(() -> guard.guard(5, "Z", true, userClientState));
}
@Test
@@ -87,8 +97,11 @@ public class GuardrailsTest extends GuardrailTester
assertTrue(guard.enabled(userClientState));
- assertValid(() -> guard.guard(5, "Z", userClientState));
- assertWarns(() -> guard.guard(11, "A", userClientState), "Warning: for
A, 11 > 10");
+ assertValid(() -> guard.guard(5, "Z", false, userClientState));
+ assertWarns(() -> guard.guard(11, "A", false, userClientState),
"Warning: for A, 11 > 10");
+
+ assertValid(() -> guard.guard(5, "Z", true, userClientState));
+ assertWarns(() -> guard.guard(11, "A", true, userClientState),
"Warning: for A, 11 > 10", "Warning: for <redacted>, 11 > 10");
}
@Test
@@ -102,8 +115,11 @@ public class GuardrailsTest extends GuardrailTester
assertTrue(guard.enabled(userClientState));
- assertValid(() -> guard.guard(5, "Z", userClientState));
- assertFails(() -> guard.guard(11, "A", userClientState), "Aborting:
for A, 11 > 10");
+ assertValid(() -> guard.guard(5, "Z", false, userClientState));
+ assertFails(() -> guard.guard(11, "A", false, userClientState),
"Aborting: for A, 11 > 10");
+
+ assertValid(() -> guard.guard(5, "Z", true, userClientState));
+ assertFails(() -> guard.guard(11, "A", true, userClientState),
"Aborting: for A, 11 > 10", "Aborting: for <redacted>, 11 > 10");
}
@Test
@@ -116,23 +132,23 @@ public class GuardrailsTest extends GuardrailTester
isWarn
? "Warning" : "Failure", what, v, t));
// value under both thresholds
- assertValid(() -> guard.guard(5, "x", null));
- assertValid(() -> guard.guard(5, "x", userClientState));
- assertValid(() -> guard.guard(5, "x", systemClientState));
- assertValid(() -> guard.guard(5, "x", superClientState));
+ assertValid(() -> guard.guard(5, "x", false, null));
+ assertValid(() -> guard.guard(5, "x", false, userClientState));
+ assertValid(() -> guard.guard(5, "x", false, systemClientState));
+ assertValid(() -> guard.guard(5, "x", false, superClientState));
// value over warning threshold
- assertWarns(() -> guard.guard(100, "y", null), "Warning: for y, 100 >
10");
- assertWarns(() -> guard.guard(100, "y", userClientState), "Warning:
for y, 100 > 10");
- assertValid(() -> guard.guard(100, "y", systemClientState));
- assertValid(() -> guard.guard(100, "y", superClientState));
-
- // value over fail threshold. An undefined user means that the check
comes from a background process,
- // so we warn instead of failing to prevent interrupting that process.
- assertWarns(() -> guard.guard(101, "z", null), "Failure: for z, 101 >
100");
- assertFails(() -> guard.guard(101, "z", userClientState), "Failure:
for z, 101 > 100");
- assertValid(() -> guard.guard(101, "z", systemClientState));
- assertValid(() -> guard.guard(101, "z", superClientState));
+ assertWarns(() -> guard.guard(100, "y", false, null), "Warning: for y,
100 > 10");
+ assertWarns(() -> guard.guard(100, "y", false, userClientState),
"Warning: for y, 100 > 10");
+ assertValid(() -> guard.guard(100, "y", false, systemClientState));
+ assertValid(() -> guard.guard(100, "y", false, superClientState));
+
+ // value over 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(101, "z", false, null), false, "Failure:
for z, 101 > 100");
+ assertFails(() -> guard.guard(101, "z", false, userClientState),
"Failure: for z, 101 > 100");
+ assertValid(() -> guard.guard(101, "z", false, systemClientState));
+ assertValid(() -> guard.guard(101, "z", false, superClientState));
}
@Test
@@ -164,7 +180,7 @@ public class GuardrailsTest extends GuardrailTester
public void testValuesWarned() throws Throwable
{
// Using a sorted set below to ensure the order in the warning message
checked below is not random
- Values<Integer> warned = new Values<>("x",
+ Values<Integer> warned = new Values<>("x",
state -> insertionOrderedSet(4,
6, 20),
state -> Collections.emptySet(),
state -> Collections.emptySet(),
@@ -184,7 +200,7 @@ public class GuardrailsTest extends GuardrailTester
public void testValuesIgnored() throws Throwable
{
// Using a sorted set below to ensure the order in the error message
checked below are not random
- Values<Integer> ignored = new Values<>("x",
+ Values<Integer> ignored = new Values<>("x",
state -> Collections.emptySet(),
state -> insertionOrderedSet(4,
6, 20),
state -> Collections.emptySet(),
@@ -218,7 +234,7 @@ public class GuardrailsTest extends GuardrailTester
public void testValuesDisallowed() throws Throwable
{
// Using a sorted set below to ensure the order in the error message
checked below are not random
- Values<Integer> disallowed = new Values<>("x",
+ Values<Integer> disallowed = new Values<>("x",
state ->
Collections.emptySet(),
state ->
Collections.emptySet(),
state ->
insertionOrderedSet(4, 6, 20),
@@ -234,16 +250,16 @@ public class GuardrailsTest extends GuardrailTester
assertValid(() -> disallowed.guard(set(200), action, userClientState));
assertValid(() -> disallowed.guard(set(1, 2, 3), action,
userClientState));
- assertWarns(() -> disallowed.guard(set(4, 6), action, null),
+ assertFails(() -> disallowed.guard(set(4, 6), action, null), false,
"Provided values [4, 6] are not allowed for integer
(disallowed values are: [4, 6, 20])");
- assertWarns(() -> disallowed.guard(set(4, 5, 6, 7), action, null),
+ assertFails(() -> disallowed.guard(set(4, 5, 6, 7), action, null),
false,
"Provided values [4, 6] are not allowed for integer
(disallowed values are: [4, 6, 20])");
}
@Test
public void testValuesUsers() throws Throwable
{
- Values<Integer> disallowed = new Values<>("x",
+ Values<Integer> disallowed = new Values<>("x",
state ->
Collections.singleton(2),
state ->
Collections.singleton(3),
state ->
Collections.singleton(4),
@@ -271,7 +287,7 @@ public class GuardrailsTest extends GuardrailTester
Assert.assertEquals(list(3, 3), triggeredOn);
message = "Provided values [4] are not allowed for integer (disallowed
values are: [4])";
- assertWarns(() -> disallowed.guard(set(4), action, null), message);
+ assertFails(() -> disallowed.guard(set(4), action, null), false,
message);
assertFails(() -> disallowed.guard(set(4), action, userClientState),
message);
assertValid(() -> disallowed.guard(set(4), action, systemClientState));
assertValid(() -> disallowed.guard(set(4), action, superClientState));
diff --git a/test/unit/org/apache/cassandra/db/guardrails/ThresholdTester.java
b/test/unit/org/apache/cassandra/db/guardrails/ThresholdTester.java
index a6ce3a1..885f626 100644
--- a/test/unit/org/apache/cassandra/db/guardrails/ThresholdTester.java
+++ b/test/unit/org/apache/cassandra/db/guardrails/ThresholdTester.java
@@ -18,6 +18,8 @@
package org.apache.cassandra.db.guardrails;
+import java.util.Collections;
+import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
@@ -125,18 +127,48 @@ public abstract class ThresholdTester extends
GuardrailTester
.isLessThanOrEqualTo(failGetter.applyAsLong(guardrails()));
}
- protected void assertThresholdWarns(String query, String... messages)
throws Throwable
+ protected void assertThresholdWarns(String query, String message) throws
Throwable
{
- assertWarns(query, messages);
+ assertThresholdWarns(query, message, message);
+ }
+
+ protected void assertThresholdWarns(String query, String message, String
redactedMessage) throws Throwable
+ {
+ assertThresholdWarns(query, Collections.singletonList(message),
Collections.singletonList(redactedMessage));
+ }
+
+ protected void assertThresholdWarns(String query, List<String> messages)
throws Throwable
+ {
+ assertThresholdWarns(query, messages, messages);
+ }
+
+ protected void assertThresholdWarns(String query, List<String> messages,
List<String> redactedMessages) throws Throwable
+ {
+ assertWarns(query, messages, redactedMessages);
Assertions.assertThat(currentValue())
.isGreaterThan(warnGetter.applyAsLong(guardrails()))
.isLessThanOrEqualTo(failGetter.applyAsLong(guardrails()));
}
- protected void assertThresholdFails(String query, String... messages)
throws Throwable
+ protected void assertThresholdFails(String query, String message) throws
Throwable
+ {
+ assertThresholdFails(query, message, message);
+ }
+
+ protected void assertThresholdFails(String query, String message, String
redactedMessage) throws Throwable
+ {
+ assertThresholdFails(query, Collections.singletonList(message),
Collections.singletonList(redactedMessage));
+ }
+
+ protected void assertThresholdFails(String query, List<String> messages)
throws Throwable
+ {
+ assertThresholdFails(query, messages, messages);
+ }
+
+ protected void assertThresholdFails(String query, List<String> messages,
List<String> redactedMessages) throws Throwable
{
- assertFails(query, messages);
+ assertFails(query, messages, redactedMessages);
Assertions.assertThat(currentValue())
.isGreaterThanOrEqualTo(warnGetter.applyAsLong(guardrails()))
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]