| /* |
| * 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); |
| } |
| } |