blob: f46e3af035b5c76d0a2c88df70b60b9df794774b [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.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import com.google.common.collect.Sets;
import org.apache.cassandra.service.ClientState;
import static java.lang.String.format;
/**
* A guardrail that warns about some specific values, warns about but ignores some other values, and/or rejects the use
* of some other values.
*
* @param <T> The type of the values of which certain are warned, ignored and/or disallowed.
*/
public class Values<T> extends Guardrail
{
private final Function<ClientState, Set<T>> warnedValues;
private final Function<ClientState, Set<T>> ignoredValues;
private final Function<ClientState, Set<T>> disallowedValues;
private final String what;
/**
* Creates a new values guardrail.
*
* @param name the identifying name of the guardrail
* @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,
Function<ClientState, Set<T>> warnedValues,
Function<ClientState, Set<T>> ignoredValues,
Function<ClientState, Set<T>> disallowedValues,
String what)
{
super(name);
this.warnedValues = warnedValues;
this.ignoredValues = ignoredValues;
this.disallowedValues = disallowedValues;
this.what = what;
}
/**
* Triggers a warning for each of the provided values that is discouraged by this guardrail. If any of the values
* is disallowed it will abort the operation.
* <p>
* This assumes that there aren't any values to be ignored, thus it doesn't require an ignore action. If this is
* not the case and the provided value is set up to be ignored this will throw an assertion error.
*
* @param values The values to check.
* @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(Set<T> values, @Nullable ClientState state)
{
guard(values, x -> {
throw new AssertionError(format("There isn't an ignore action for %s, but value %s is setup to be ignored",
what, x));
}, state);
}
/**
* Triggers a warning for each of the provided values that is discouraged by this guardrail. Also triggers a warning
* for each of the provided values that is ignored by this guardrail and triggers the provided action to ignore it.
* If any of the values is disallowed it will abort the operation.
*
* @param values The values to check.
* @param ignoreAction An action called on the subset of {@code values} that should be ignored. This action
* should do whatever is necessary to make sure the value is ignored.
* @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.
*/
public void guard(Set<T> values, Consumer<T> ignoreAction, @Nullable ClientState state)
{
if (!enabled(state))
return;
Set<T> disallowed = disallowedValues.apply(state);
Set<T> toDisallow = Sets.intersection(values, disallowed);
if (!toDisallow.isEmpty())
fail(format("Provided values %s are not allowed for %s (disallowed values are: %s)",
toDisallow.stream().sorted().collect(Collectors.toList()), what, disallowed), state);
Set<T> ignored = ignoredValues.apply(state);
Set<T> toIgnore = Sets.intersection(values, ignored);
if (!toIgnore.isEmpty())
{
warn(format("Ignoring provided values %s as they are not supported for %s (ignored values are: %s)",
toIgnore.stream().sorted().collect(Collectors.toList()), what, ignored));
toIgnore.forEach(ignoreAction);
}
Set<T> warned = warnedValues.apply(state);
Set<T> toWarn = Sets.intersection(values, warned);
if (!toWarn.isEmpty())
warn(format("Provided values %s are not recommended for %s (warned values are: %s)",
toWarn.stream().sorted().collect(Collectors.toList()), what, warned));
}
}