blob: 257ab013b7603769a13ba709bc0ab7a7cb739743 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.db.guardrails;
import java.util.function.ToLongFunction;
import javax.annotation.Nullable;
import org.apache.cassandra.service.ClientState;
/**
* A guardrail based on numeric threshold(s).
*
* <p>A {@link Threshold} guardrail defines (up to) 2 thresholds, one at which a warning is issued, and a lower one
* at which the operation is aborted with an exception. Only one of those thresholds can be activated if desired.
*
* <p>This guardrail only handles guarding positive values.
*/
public abstract class Threshold extends Guardrail
{
protected ToLongFunction<ClientState> warnThreshold;
protected ToLongFunction<ClientState> failThreshold;
protected final ErrorMessageProvider messageProvider;
/**
* 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, reason);
this.warnThreshold = warnThreshold;
this.failThreshold = failThreshold;
this.messageProvider = messageProvider;
}
protected abstract boolean compare(long value, long threshold);
protected String errMsg(boolean isWarning, String what, long value, long thresholdValue)
{
return messageProvider.createMessage(isWarning,
what,
Long.toString(value),
Long.toString(thresholdValue));
}
private String redactedErrMsg(boolean isWarning, long value, long thresholdValue)
{
return errMsg(isWarning, REDACTED, value, thresholdValue);
}
protected abstract long failValue(ClientState state);
protected abstract long warnValue(ClientState state);
public boolean enabled(@Nullable ClientState state)
{
if (!super.enabled(state))
return false;
return failThreshold.applyAsLong(state) > 0 || warnThreshold.applyAsLong(state) > 0;
}
/**
* Checks whether the provided value would trigger a warning or failure if passed to {@link #guard}.
*
* <p>This method is optional (does not have to be called) but can be used in the case where the "what"
* argument to {@link #guard} is expensive to build to save doing so in the common case (of the guardrail
* not being triggered).
*
* @param value the value to test.
* @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} if {@code value} is above the warning or failure thresholds of this guardrail,
* {@code false otherwise}.
*/
public boolean triggersOn(long value, @Nullable ClientState state)
{
return enabled(state) && (compare(value, warnValue(state)) || compare(value, failValue(state)));
}
public boolean warnsOn(long value, @Nullable ClientState state)
{
return enabled(state) && compare(value, warnValue(state));
}
public boolean failsOn(long value, @Nullable ClientState state)
{
return enabled(state) && compare(value, failValue(state));
}
/**
* 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 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, boolean containsUserData, @Nullable ClientState state)
{
if (!enabled(state))
return;
long failValue = failValue(state);
if (compare(value, failValue))
{
triggerFail(value, failValue, what, containsUserData, state);
return;
}
long warnValue = warnValue(state);
if (compare(value, warnValue))
triggerWarn(value, warnValue, what, containsUserData);
}
private void triggerFail(long value, long failValue, String what, boolean containsUserData, ClientState 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, boolean containsUserData)
{
String fullMessage = errMsg(true, what, value, warnValue);
warn(fullMessage, containsUserData ? redactedErrMsg(true, value, warnValue) : fullMessage);
}
/**
* A function used to build the error message of a triggered {@link Threshold} guardrail.
*/
interface ErrorMessageProvider
{
/**
* Called when the guardrail is triggered to build the corresponding error message.
*
* @param isWarning Whether the trigger is a warning one; otherwise it is a failure one.
* @param what A string, provided by the call to the {@link #guard} method, describing what the guardrail
* has been applied to (and that has triggered it).
* @param value The value that triggered the guardrail (as a string).
* @param threshold The threshold that was passed to trigger the guardrail (as a string).
*/
String createMessage(boolean isWarning, String what, String value, String threshold);
}
}