Extend DurationSpec and DataStorageSpec for smallest unit and transfer denylist parameters to the new framework

patch by Ekaterina Dimitrova; reviewed by Caleb Rackliffe, David Capwell, Michael Semb Wever and Benjamin Lerer for CASSANDRA-15234
This commit is contained in:
Ekaterina Dimitrova 2022-02-02 12:47:41 -05:00
parent a3258d66bc
commit b9e2ab75f8
23 changed files with 701 additions and 93 deletions

View File

@ -841,7 +841,7 @@ rpc_keepalive: true
# Uncomment to set socket buffer size for internode communication
# Note that when setting this, the buffer size is limited by net.core.wmem_max
# and when not setting it it is defined by net.ipv4.tcp_wmem
# internode_socket_receive_buffer_size_in_bytes:
# internode_socket_receive_buffer_size:
# Set to true to have Cassandra create a hard link to each sstable
# flushed or streamed locally in a backups/ subdirectory of the
@ -1076,19 +1076,19 @@ slow_query_log_timeout_in_ms: 500
# Allows denying configurable access (rw/rr) to operations on configured ks, table, and partitions, intended for use by
# operators to manage cluster health vs application access. See CASSANDRA-12106 and CEP-13 for more details.
# enable_partition_denylist: false
# partition_denylist_enabled: false
# enable_denylist_writes: true
# enable_denylist_reads: true
# enable_denylist_range_reads: true
# denylist_writes_enabled: true
# denylist_reads_enabled: true
# denylist_range_reads_enabled: true
# The interval at which keys in the cache for denylisting will "expire" and async refresh from the backing DB.
# Note: this serves only as a fail-safe, as the usage pattern is expected to be "mutate state, refresh cache" on any
# changes to the underlying denylist entries. See documentation for details.
# denylist_refresh_seconds: 600
# denylist_refresh: 600s
# In the event of errors on attempting to load the denylist cache, retry on this interval.
# denylist_initial_load_retry_seconds: 5
# denylist_initial_load_retry: 5s
# We cap the number of denylisted keys allowed per table to keep things from growing unbounded. Nodes will warn above
# this limit while allowing new denylisted keys to be inserted. Denied keys are loaded in natural query / clustering

View File

@ -546,17 +546,17 @@ public class Config
/** This feature allows denying access to operations on certain key partitions, intended for use by operators to
* provide another tool to manage cluster health vs application access. See CASSANDRA-12106 and CEP-13 for more details.
*/
public volatile boolean enable_partition_denylist = false;
public volatile boolean partition_denylist_enabled = false;
public volatile boolean enable_denylist_writes = true;
public volatile boolean denylist_writes_enabled = true;
public volatile boolean enable_denylist_reads = true;
public volatile boolean denylist_reads_enabled = true;
public volatile boolean enable_denylist_range_reads = true;
public volatile boolean denylist_range_reads_enabled = true;
public int denylist_refresh_seconds = 600;
public SmallestDurationSeconds denylist_refresh = new SmallestDurationSeconds("600s");
public int denylist_initial_load_retry_seconds = 5;
public SmallestDurationSeconds denylist_initial_load_retry = new SmallestDurationSeconds("5s");
/** We cap the number of denylisted keys allowed per table to keep things from growing unbounded. Operators will
* receive warnings and only denylist_max_keys_per_table in natural query ordering will be processed on overflow.

View File

@ -36,30 +36,30 @@ public enum Converters
*/
IDENTITY(null, o -> o, o-> o),
MILLIS_DURATION(Long.class,
o -> DurationSpec.inMilliseconds((Long) o),
o -> ((DurationSpec)o).toMilliseconds()),
o -> SmallestDurationMilliseconds.inMilliseconds((Long) o),
o -> ((SmallestDurationMilliseconds)o).toMilliseconds()),
MILLIS_DOUBLE_DURATION(Double.class,
o -> DurationSpec.inDoubleMilliseconds((Double) o),
o -> ((DurationSpec)o).toMilliseconds()),
o -> SmallestDurationMilliseconds.inDoubleMilliseconds((Double) o),
o -> ((SmallestDurationMilliseconds)o).toMilliseconds()),
/**
* This converter is used to support backward compatibility for parameters where in the past -1 was used as a value
* Example: credentials_update_interval_in_ms = -1 and credentials_update_interval = null (quantity of 0ms) are equal.
*/
MILLIS_CUSTOM_DURATION(Long.class,
o -> (Long)o == -1 ? new DurationSpec("0ms") : DurationSpec.inMilliseconds((Long) o),
o -> ((DurationSpec)o).toMilliseconds() == 0 ? -1 : ((DurationSpec)o).toMilliseconds()),
o -> (Long)o == -1 ? new SmallestDurationMilliseconds("0ms") : SmallestDurationMilliseconds.inMilliseconds((Long) o),
o -> ((SmallestDurationMilliseconds)o).toMilliseconds() == 0 ? -1 : ((SmallestDurationMilliseconds)o).toMilliseconds()),
SECONDS_DURATION(Long.class,
o -> DurationSpec.inSeconds((Long) o),
o -> ((DurationSpec)o).toSeconds()),
o -> SmallestDurationSeconds.inSeconds((Long) o),
o -> ((SmallestDurationSeconds)o).toSeconds()),
MINUTES_DURATION(Long.class,
o -> DurationSpec.inMinutes((Long) o),
o -> ((DurationSpec)o).toMinutes()),
o -> SmallestDurationMinutes.inMinutes((Long) o),
o -> ((SmallestDurationMinutes)o).toMinutes()),
MEBIBYTES_DATA_STORAGE(Long.class,
o -> DataStorageSpec.inMebibytes((Long) o),
o -> ((DataStorageSpec)o).toMebibytes()),
o -> SmallestDataStorageMebibytes.inMebibytes((Long) o),
o -> ((SmallestDataStorageMebibytes)o).toMebibytes()),
KIBIBYTES_DATASTORAGE(Long.class,
o -> DataStorageSpec.inKibibytes((Long) o),
o -> ((DataStorageSpec)o).toKibibytes()),
o -> SmallestDataStorageKibibytes.inKibibytes((Long) o),
o -> ((SmallestDataStorageKibibytes)o).toKibibytes()),
BYTES_DATASTORAGE(Long.class,
o -> DataStorageSpec.inBytes((Long) o),
o -> ((DataStorageSpec)o).toBytes()),

View File

@ -23,17 +23,29 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
import org.apache.cassandra.exceptions.ConfigurationException;
import static org.apache.cassandra.config.DataStorageSpec.DataStorageUnit.GIBIBYTES;
import static org.apache.cassandra.config.DataStorageSpec.DataStorageUnit.KIBIBYTES;
import static org.apache.cassandra.config.DataStorageSpec.DataStorageUnit.MEBIBYTES;
/**
* Represents an amount of data storage. Wrapper class for Cassandra configuration parameters, providing to the
* users the opportunity to be able to provide config with a unit of their choice in cassandra.yaml as per the available
* options. (CASSANDRA-15234)
*/
public final class DataStorageSpec
public class DataStorageSpec
{
/**
* Immutable map that matches supported time units according to a provided smallest supported time unit
*/
private static final ImmutableMap<DataStorageUnit, ImmutableSet<DataStorageUnit>> MAP_UNITS_PER_MIN_UNIT =
ImmutableMap.of(KIBIBYTES, ImmutableSet.of(KIBIBYTES, MEBIBYTES, GIBIBYTES),
MEBIBYTES, ImmutableSet.of(MEBIBYTES, GIBIBYTES));
/**
* The Regexp used to parse the storage provided as String.
*/
@ -48,7 +60,7 @@ public final class DataStorageSpec
if (value == null || value.equals("null"))
{
quantity = 0;
unit = DataStorageUnit.MEBIBYTES; // the unit doesn't really matter as 0 is 0 in all units
unit = MEBIBYTES; // the unit doesn't really matter as 0 is 0 in all units
return;
}
@ -74,6 +86,35 @@ public final class DataStorageSpec
this.unit = unit;
}
public DataStorageSpec (String value, DataStorageUnit minUnit)
{
if (value == null || value.equals("null"))
{
quantity = 0;
unit = minUnit;
return;
}
if (!MAP_UNITS_PER_MIN_UNIT.containsKey(minUnit))
throw new ConfigurationException("Invalid smallest unit set for " + value);
//parse the string field value
Matcher matcher = STORAGE_UNITS_PATTERN.matcher(value);
if (matcher.find())
{
quantity = Long.parseLong(matcher.group(1));
unit = DataStorageUnit.fromSymbol(matcher.group(2));
if (!MAP_UNITS_PER_MIN_UNIT.get(minUnit).contains(unit))
throw new ConfigurationException("Invalid data storage: " + value + " Accepted units:" + MAP_UNITS_PER_MIN_UNIT);
}
else
{
throw new ConfigurationException("Invalid data storage: " + value + " Accepted units:" + MAP_UNITS_PER_MIN_UNIT.get(minUnit) +
" where case matters and only non-negative values are accepted");
}
}
/**
* Creates a {@code DataStorageSpec} of the specified amount of bytes.
*
@ -93,7 +134,7 @@ public final class DataStorageSpec
*/
public static DataStorageSpec inKibibytes(long kibibytes)
{
return new DataStorageSpec(kibibytes, DataStorageUnit.KIBIBYTES);
return new DataStorageSpec(kibibytes, KIBIBYTES);
}
/**
@ -104,7 +145,7 @@ public final class DataStorageSpec
*/
public static DataStorageSpec inMebibytes(long mebibytes)
{
return new DataStorageSpec(mebibytes, DataStorageUnit.MEBIBYTES);
return new DataStorageSpec(mebibytes, MEBIBYTES);
}
/**

View File

@ -3609,68 +3609,70 @@ public class DatabaseDescriptor
conf.consecutive_message_errors_threshold = value;
}
public static boolean getEnablePartitionDenylist()
public static boolean getPartitionDenylistEnabled()
{
return conf.enable_partition_denylist;
return conf.partition_denylist_enabled;
}
public static void setEnablePartitionDenylist(boolean enabled)
public static void setPartitionDenylistEnabled(boolean enabled)
{
conf.enable_partition_denylist = enabled;
conf.partition_denylist_enabled = enabled;
}
public static boolean getEnableDenylistWrites()
public static boolean getDenylistWritesEnabled()
{
return conf.enable_denylist_writes;
return conf.denylist_writes_enabled;
}
public static void setEnableDenylistWrites(boolean enabled)
public static void setDenylistWritesEnabled(boolean enabled)
{
conf.enable_denylist_writes = enabled;
conf.denylist_writes_enabled = enabled;
}
public static boolean getEnableDenylistReads()
public static boolean getDenylistReadsEnabled()
{
return conf.enable_denylist_reads;
return conf.denylist_reads_enabled;
}
public static void setEnableDenylistReads(boolean enabled)
public static void setDenylistReadsEnabled(boolean enabled)
{
conf.enable_denylist_reads = enabled;
conf.denylist_reads_enabled = enabled;
}
public static boolean getEnableDenylistRangeReads()
public static boolean getDenylistRangeReadsEnabled()
{
return conf.enable_denylist_range_reads;
return conf.denylist_range_reads_enabled;
}
public static void setEnableDenylistRangeReads(boolean enabled)
public static void setDenylistRangeReadsEnabled(boolean enabled)
{
conf.enable_denylist_range_reads = enabled;
conf.denylist_range_reads_enabled = enabled;
}
public static int getDenylistRefreshSeconds()
{
return conf.denylist_refresh_seconds;
return conf.denylist_refresh.toSecondsAsInt();
}
public static void setDenylistRefreshSeconds(int seconds)
{
if (seconds <= 0)
throw new IllegalArgumentException("denylist_refresh_seconds must be a positive integer.");
conf.denylist_refresh_seconds = seconds;
throw new IllegalArgumentException("denylist_refresh must be a positive integer.");
conf.denylist_refresh = SmallestDurationSeconds.inSeconds(seconds);
}
public static int getDenylistInitialLoadRetrySeconds()
{
return conf.denylist_initial_load_retry_seconds;
return conf.denylist_initial_load_retry.toSecondsAsInt();
}
public static void setDenylistInitialLoadRetrySeconds(int seconds)
{
if (seconds <= 0)
throw new IllegalArgumentException("denylist_initial_load_retry_seconds must be a positive integer.");
conf.denylist_initial_load_retry_seconds = seconds;
throw new IllegalArgumentException("denylist_initial_load_retry must be a positive integer.");
conf.denylist_initial_load_retry = SmallestDurationSeconds.inSeconds(seconds);
}
public static ConsistencyLevel getDenylistConsistencyLevel()

View File

@ -25,24 +25,40 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
import org.apache.cassandra.exceptions.ConfigurationException;
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* Represents a positive time duration. Wrapper class for Cassandra duration configuration parameters, providing to the
* users the opportunity to be able to provide config with a unit of their choice in cassandra.yaml as per the available
* options. (CASSANDRA-15234)
*/
public final class DurationSpec
public class DurationSpec
{
/**
* Immutable map that matches supported time units according to a provided smallest supported time unit
*/
private static final ImmutableMap<TimeUnit, ImmutableSet<TimeUnit>> MAP_UNITS_PER_MIN_UNIT =
ImmutableMap.of(MILLISECONDS, ImmutableSet.of(MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS),
SECONDS, ImmutableSet.of(SECONDS, MINUTES, HOURS, DAYS),
MINUTES, ImmutableSet.of(MINUTES, HOURS, DAYS));
/**
* The Regexp used to parse the duration provided as String.
*/
private static final Pattern TIME_UNITS_PATTERN = Pattern.compile(("^(\\d+)(d|h|s|ms|us|µs|ns|m)"));
private static final Pattern TIME_UNITS_PATTERN = Pattern.compile(("^(\\d+)(d|h|s|ms|us|µs|ns|m)$"));
private static final Pattern VALUES_PATTERN = Pattern.compile(("\\d+"));
public final long quantity;
private final long quantity;
private final TimeUnit unit;
@ -51,7 +67,7 @@ public final class DurationSpec
if (value == null || value.equals("null") || value.toLowerCase(Locale.ROOT).equals("nan"))
{
quantity = 0;
unit = TimeUnit.MILLISECONDS;
unit = MILLISECONDS;
return;
}
@ -84,6 +100,35 @@ public final class DurationSpec
this(Math.round(quantity), unit);
}
public DurationSpec(String value, TimeUnit minUnit)
{
if (value == null || value.equals("null") || value.toLowerCase(Locale.ROOT).equals("nan"))
{
quantity = 0;
unit = minUnit;
return;
}
if (!MAP_UNITS_PER_MIN_UNIT.containsKey(minUnit))
throw new ConfigurationException("Invalid smallest unit set for " + value);
Matcher matcher = TIME_UNITS_PATTERN.matcher(value);
if(matcher.find())
{
quantity = Long.parseLong(matcher.group(1));
unit = fromSymbol(matcher.group(2));
if (!MAP_UNITS_PER_MIN_UNIT.get(minUnit).contains(unit))
throw new ConfigurationException("Invalid duration: " + value + " Accepted units:" + MAP_UNITS_PER_MIN_UNIT.get(minUnit));
}
else
{
throw new ConfigurationException("Invalid duration: " + value + " Accepted units:" + MAP_UNITS_PER_MIN_UNIT.get(minUnit) +
" where case matters and only non-negative values.");
}
}
/**
* Creates a {@code DurationSpec} of the specified amount of milliseconds.
*
@ -92,12 +137,12 @@ public final class DurationSpec
*/
public static DurationSpec inMilliseconds(long milliseconds)
{
return new DurationSpec(milliseconds, TimeUnit.MILLISECONDS);
return new DurationSpec(milliseconds, MILLISECONDS);
}
public static DurationSpec inDoubleMilliseconds(double milliseconds)
{
return new DurationSpec(milliseconds, TimeUnit.MILLISECONDS);
return new DurationSpec(milliseconds, MILLISECONDS);
}
/**
@ -108,7 +153,7 @@ public final class DurationSpec
*/
public static DurationSpec inSeconds(long seconds)
{
return new DurationSpec(seconds, TimeUnit.SECONDS);
return new DurationSpec(seconds, SECONDS);
}
/**
@ -119,7 +164,7 @@ public final class DurationSpec
*/
public static DurationSpec inMinutes(long minutes)
{
return new DurationSpec(minutes, TimeUnit.MINUTES);
return new DurationSpec(minutes, MINUTES);
}
/**
@ -130,7 +175,7 @@ public final class DurationSpec
*/
public static DurationSpec inHours(long hours)
{
return new DurationSpec(hours, TimeUnit.HOURS);
return new DurationSpec(hours, HOURS);
}
/**
@ -151,7 +196,7 @@ public final class DurationSpec
if (matcher.matches())
{
seconds = Long.parseLong(value);
return new DurationSpec(seconds, TimeUnit.SECONDS);
return new DurationSpec(seconds, SECONDS);
}
//otherwise we just use the standard constructors
@ -166,11 +211,11 @@ public final class DurationSpec
{
switch (symbol.toLowerCase())
{
case "d": return TimeUnit.DAYS;
case "h": return TimeUnit.HOURS;
case "m": return TimeUnit.MINUTES;
case "s": return TimeUnit.SECONDS;
case "ms": return TimeUnit.MILLISECONDS;
case "d": return DAYS;
case "h": return HOURS;
case "m": return MINUTES;
case "s": return SECONDS;
case "ms": return MILLISECONDS;
case "us":
case "µs": return TimeUnit.MICROSECONDS;
case "ns": return TimeUnit.NANOSECONDS;

View File

@ -0,0 +1,55 @@
/*
* 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.config;
/**
* Wrapper class for Cassandra data storage configuration parameters which are internally represented in Kibibytes. In order
* not to lose precision while converting to smaller units (until we migrate those parameters to use internally the smallest
* supported unit) we restrict those parameters to use only kibibytes or larger units. (CASSANDRA-15234)
*/
public class SmallestDataStorageKibibytes extends DataStorageSpec
{
/**
* Creates a {@code SmallestDataStorageKibibytes} of the specified amount of seconds and provides the smallest
* required unit of Kibibytes for the respective parameter of type {@code SmallestDurationSeconds}.
*
* @param value the data storage
*
*/
public SmallestDataStorageKibibytes(String value)
{
super(value, DataStorageSpec.DataStorageUnit.KIBIBYTES);
}
private SmallestDataStorageKibibytes(long quantity, DataStorageSpec.DataStorageUnit unit)
{
super(quantity, unit);
}
/**
* Creates a {@code SmallestDataStorageKibibytes} of the specified amount of kibibytes.
*
* @param kibibytes the amount of kibibytes
* @return a data storage
*/
public static SmallestDataStorageKibibytes inKibibytes(long kibibytes)
{
return new SmallestDataStorageKibibytes(kibibytes, DataStorageSpec.DataStorageUnit.KIBIBYTES);
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.config;
/**
* Wrapper class for Cassandra data storage configuration parameters which are internally represented in mebibytes. In order
* not to lose precision while converting to smaller units (until we migrate those parameters to use internally the smallest
* supported unit) we restrict those parameters to use only mebibytes or larger units. (CASSANDRA-15234)
*/
public class SmallestDataStorageMebibytes extends DataStorageSpec
{
/**
* Creates a {@code SmallestDataStoragemebibytes} of the specified amount of seconds and provides the smallest
* required unit of mebibytes for the respective parameter of type {@code SmallestDurationSeconds}.
*
* @param value the data storage
*
*/
public SmallestDataStorageMebibytes(String value)
{
super(value, DataStorageSpec.DataStorageUnit.MEBIBYTES);
}
private SmallestDataStorageMebibytes(long quantity, DataStorageSpec.DataStorageUnit unit)
{
super(quantity, unit);
}
/**
* Creates a {@code SmallestDataStorageMebibytes} of the specified amount of mebibytes.
*
* @param mebibytes the amount of mebibytes
* @return a data storage
*/
public static SmallestDataStorageMebibytes inMebibytes(long mebibytes)
{
return new SmallestDataStorageMebibytes(mebibytes, DataStorageSpec.DataStorageUnit.MEBIBYTES);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.config;
import java.util.concurrent.TimeUnit;
/**
* Wrapper class for Cassandra duration configuration parameters which are internally represented in Milliseconds. In order
* not to lose precision while converting to smaller units (until we migrate those parameters to use internally the smallest
* supported unit) we restrict those parameters to use only Milliseconds or larger units. (CASSANDRA-15234)
*/
public class SmallestDurationMilliseconds extends DurationSpec
{
/**
* Creates a {@code SmallestDurationMilliseconds} of the specified amount of milliseconds and provides the smallest
* required unit of milliseconds for the respective parameter of type {@code SmallestDurationMilliseconds}.
*
* @param value the duration
*
*/
public SmallestDurationMilliseconds(String value)
{
super(value, TimeUnit.MILLISECONDS);
}
private SmallestDurationMilliseconds(long quantity, TimeUnit unit)
{
super(quantity, unit);
}
/**
* Creates a {@code SmallestDurationMilliseconds} of the specified amount of milliseconds.
*
* @param milliseconds the amount of milliseconds
* @return a duration
*/
public static SmallestDurationMilliseconds inMilliseconds(long milliseconds)
{
return new SmallestDurationMilliseconds(milliseconds, TimeUnit.MILLISECONDS);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.config;
import java.util.concurrent.TimeUnit;
/**
* Wrapper class for Cassandra duration configuration parameters which are internally represented in Minuts. In order
* not to lose precision while converting to smaller units (until we migrate those parameters to use internally the smallest
* supported unit) we restrict those parameters to use only Minutes or larger units. (CASSANDRA-15234)
*/
public class SmallestDurationMinutes extends DurationSpec
{
/**
* Creates a {@code SmallestDurationMinutes} of the specified amount of minutes and provides the smallest
* required unit of minutes for the respective parameter of type {@code SmallestDurationMinutes}.
*
* @param value the duration
*
*/
public SmallestDurationMinutes(String value)
{
super(value, TimeUnit.MINUTES);
}
private SmallestDurationMinutes(long quantity, TimeUnit unit)
{
super(quantity, unit);
}
/**
* Creates a {@code SmallestDurationMinutes} of the specified amount of minutes.
*
* @param minutes the amount of minutes
* @return a duration
*/
public static SmallestDurationMinutes inMinutes(long minutes)
{
return new SmallestDurationMinutes(minutes, TimeUnit.MINUTES);
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.config;
import java.util.concurrent.TimeUnit;
/**
* Wrapper class for Cassandra duration configuration parameters which are internally represented in Seconds. In order
* not to lose precision while converting to smaller units (until we migrate those parameters to use internally the smallest
* supported unit) we restrict those parameters to use only Seconds or larger units. (CASSANDRA-15234)
*/
public class SmallestDurationSeconds extends DurationSpec
{
/**
* Creates a {@code SmallestDurationSeconds} of the specified amount of seconds and provides the smallest
* required unit of seconds for the respective parameter of type {@code SmallestDurationSeconds}.
*
* @param value the duration
*
*/
public SmallestDurationSeconds(String value)
{
super(value, TimeUnit.SECONDS);
}
private SmallestDurationSeconds(long quantity, TimeUnit unit)
{
super(quantity, unit);
}
/**
* Creates a {@code SmallestDurationSeconds} of the specified amount of seconds.
*
* @param seconds the amount of seconds
* @return a duration
*/
public static SmallestDurationSeconds inSeconds(long seconds)
{
return new SmallestDurationSeconds(seconds, TimeUnit.SECONDS);
}
}

View File

@ -131,7 +131,7 @@ public class PartitionDenylist
*/
public void initialLoad()
{
if (!DatabaseDescriptor.getEnablePartitionDenylist())
if (!DatabaseDescriptor.getPartitionDenylistEnabled())
return;
synchronized (this)
@ -309,7 +309,7 @@ public class PartitionDenylist
final TableMetadata tmd = Schema.instance.getTableMetadata(tid);
// We have a few quick state checks to get out of the way first; this is hot path so we want to do these first if possible.
if (!DatabaseDescriptor.getEnablePartitionDenylist() || tid == null || tmd == null || !canDenylistKeyspace(tmd.keyspace))
if (!DatabaseDescriptor.getPartitionDenylistEnabled() || tid == null || tmd == null || !canDenylistKeyspace(tmd.keyspace))
return true;
try
@ -351,7 +351,7 @@ public class PartitionDenylist
public int getDeniedKeysInRangeCount(final TableId tid, final AbstractBounds<PartitionPosition> range)
{
final TableMetadata tmd = Schema.instance.getTableMetadata(tid);
if (!DatabaseDescriptor.getEnablePartitionDenylist() || tid == null || tmd == null || !canDenylistKeyspace(tmd.keyspace))
if (!DatabaseDescriptor.getPartitionDenylistEnabled() || tid == null || tmd == null || !canDenylistKeyspace(tmd.keyspace))
return 0;
try

View File

@ -287,7 +287,7 @@ public class StorageProxy implements StorageProxyMBean
{
TableMetadata metadata = Schema.instance.validateTable(keyspaceName, cfName);
if (DatabaseDescriptor.getEnablePartitionDenylist() && DatabaseDescriptor.getEnableDenylistWrites() && !partitionDenylist.isKeyPermitted(keyspaceName, cfName, key.getKey()))
if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistWritesEnabled() && !partitionDenylist.isKeyPermitted(keyspaceName, cfName, key.getKey()))
{
denylistMetrics.incrementWritesRejected();
throw new InvalidRequestException(String.format("Unable to CAS write to denylisted partition [0x%s] in %s/%s",
@ -1079,7 +1079,7 @@ public class StorageProxy implements StorageProxyMBean
long queryStartNanoTime)
throws WriteTimeoutException, WriteFailureException, UnavailableException, OverloadedException, InvalidRequestException
{
if (DatabaseDescriptor.getEnablePartitionDenylist() && DatabaseDescriptor.getEnableDenylistWrites())
if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistWritesEnabled())
{
for (final IMutation mutation : mutations)
{
@ -1788,7 +1788,7 @@ public class StorageProxy implements StorageProxyMBean
throw exception;
}
if (DatabaseDescriptor.getEnablePartitionDenylist() && DatabaseDescriptor.getEnableDenylistReads())
if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistReadsEnabled())
{
for (SinglePartitionReadCommand command : group.queries)
{
@ -2143,7 +2143,7 @@ public class StorageProxy implements StorageProxyMBean
ConsistencyLevel consistencyLevel,
long queryStartNanoTime)
{
if (DatabaseDescriptor.getEnablePartitionDenylist() && DatabaseDescriptor.getEnableDenylistRangeReads())
if (DatabaseDescriptor.getPartitionDenylistEnabled() && DatabaseDescriptor.getDenylistRangeReadsEnabled())
{
final int denylisted = partitionDenylist.getDeniedKeysInRangeCount(command.metadata().id, command.dataRange().keyRange());
if (denylisted > 0)
@ -2877,25 +2877,25 @@ public class StorageProxy implements StorageProxyMBean
@Override
public void setEnablePartitionDenylist(boolean enabled)
{
DatabaseDescriptor.setEnablePartitionDenylist(enabled);
DatabaseDescriptor.setPartitionDenylistEnabled(enabled);
}
@Override
public void setEnableDenylistWrites(boolean enabled)
{
DatabaseDescriptor.setEnableDenylistWrites(enabled);
DatabaseDescriptor.setDenylistWritesEnabled(enabled);
}
@Override
public void setEnableDenylistReads(boolean enabled)
{
DatabaseDescriptor.setEnableDenylistReads(enabled);
DatabaseDescriptor.setDenylistReadsEnabled(enabled);
}
@Override
public void setEnableDenylistRangeReads(boolean enabled)
{
DatabaseDescriptor.setEnableDenylistRangeReads(enabled);
DatabaseDescriptor.setDenylistRangeReadsEnabled(enabled);
}
@Override

View File

@ -65,8 +65,8 @@ public class PartitionDenylistTest extends TestBaseImpl
.withConfig(config -> config
.with(NETWORK)
.with(GOSSIP)
.set("enable_partition_denylist", true)
.set("denylist_initial_load_retry_seconds", 1))
.set("partition_denylist_enabled", true)
.set("denylist_initial_load_retry", "1s"))
.createWithoutStarting())
{
cluster.forEach(i -> {
@ -142,8 +142,8 @@ public class PartitionDenylistTest extends TestBaseImpl
.withConfig(config -> config
.with(NETWORK)
.with(GOSSIP)
.set("enable_partition_denylist", true)
.set("denylist_initial_load_retry_seconds", 1))
.set("partition_denylist_enabled", true)
.set("denylist_initial_load_retry", "1s"))
.createWithoutStarting())
{
// Starting without networking enabled in the hope it doesn't trigger

View File

@ -210,7 +210,12 @@ public class DatabaseDescriptorRefTest
"org.apache.cassandra.config.DataRateSpec$DataRateUnit",
"org.apache.cassandra.config.DataRateSpec$DataRateUnit$1",
"org.apache.cassandra.config.DataRateSpec$DataRateUnit$2",
"org.apache.cassandra.config.DataRateSpec$DataRateUnit$3"
"org.apache.cassandra.config.DataRateSpec$DataRateUnit$3",
"org.apache.cassandra.config.SmallestDurationMinutes",
"org.apache.cassandra.config.SmallestDurationSeconds",
"org.apache.cassandra.config.SmallestDurationMilliseconds",
"org.apache.cassandra.config.SmallestDataStorageKibibytes",
"org.apache.cassandra.config.SmallestDataStorageMebibytes"
};
static final Set<String> checkedClasses = new HashSet<>(Arrays.asList(validClasses));

View File

@ -599,8 +599,8 @@ public class DatabaseDescriptorTest
{
DatabaseDescriptor.loadConfig();
expectIllegalArgumentException(DatabaseDescriptor::setDenylistRefreshSeconds, 0, "denylist_refresh_seconds must be a positive integer.");
expectIllegalArgumentException(DatabaseDescriptor::setDenylistRefreshSeconds, -1, "denylist_refresh_seconds must be a positive integer.");
expectIllegalArgumentException(DatabaseDescriptor::setDenylistRefreshSeconds, 0, "denylist_refresh must be a positive integer.");
expectIllegalArgumentException(DatabaseDescriptor::setDenylistRefreshSeconds, -1, "denylist_refresh must be a positive integer.");
expectIllegalArgumentException(DatabaseDescriptor::setDenylistMaxKeysPerTable, 0, "denylist_max_keys_per_table must be a positive integer.");
expectIllegalArgumentException(DatabaseDescriptor::setDenylistMaxKeysPerTable, -1, "denylist_max_keys_per_table must be a positive integer.");
expectIllegalArgumentException(DatabaseDescriptor::setDenylistMaxKeysTotal, 0, "denylist_max_keys_total must be a positive integer.");

View File

@ -110,16 +110,16 @@ public class LoadOldYAMLBackwardCompatibilityTest
assertTrue(config.sasi_indexes_enabled);
assertTrue(config.drop_compact_storage_enabled);
assertTrue(config.user_defined_functions_threads_enabled);
/*assertEquals(DurationSpec.inMilliseconds(2000), config.permissions_validity);
assertEquals(DurationSpec.inMilliseconds(2000), config.permissions_validity);
assertEquals(DurationSpec.inMilliseconds(0), config.permissions_update_interval);
assertEquals(DurationSpec.inMilliseconds(2000), config.roles_validity);
assertEquals(DurationSpec.inMilliseconds(0), config.roles_update_interval);
assertEquals(DurationSpec.inMilliseconds(2000), config.credentials_validity);
assertEquals(DurationSpec.inMilliseconds(0), config.credentials_update_interval);
assertEquals(DurationSpec.inMinutes(60), config.index_summary_resize_interval);
//assertEquals(DurationSpec.inMinutes(60), config.index_summary_resize_interval);
//parameters which names have not changed with CASSANDRA-15234
assertEquals(DurationSpec.inSecondsString("14400"), config.key_cache_save_period);
/*assertEquals(DurationSpec.inSecondsString("14400"), config.key_cache_save_period);
assertEquals(DurationSpec.inHours(4), config.key_cache_save_period);
assertEquals(DurationSpec.inSecondsString("0"), config.row_cache_save_period);
assertEquals(DurationSpec.inSeconds(0), config.row_cache_save_period);

View File

@ -0,0 +1,42 @@
/*
* 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.config;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
public class SmallestDataStorageKibibytesTest
{
@Test
public void testInvalidUnits()
{
assertThatThrownBy(() -> new SmallestDataStorageKibibytes("10B")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid data storage: 10B");
}
@Test
public void testValidUnits()
{
assertEquals(10L, new SmallestDataStorageKibibytes("10KiB").toKibibytes());
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.config;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
public class SmallestDataStorageMebibytesTest
{
@Test
public void testInvalidUnits()
{
assertThatThrownBy(() -> new SmallestDataStorageMebibytes("10B")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid data storage: 10B");
}
@Test
public void testValidUnits()
{
assertEquals(10L, new SmallestDataStorageMebibytes("10MiB").toMebibytes());
}
}

View File

@ -0,0 +1,48 @@
/*
* 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.config;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
public class SmallestDurationMillisecondsTest
{
@Test
public void testInvalidUnits()
{
assertThatThrownBy(() -> new SmallestDurationMilliseconds("10ns")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10ns");
assertThatThrownBy(() -> new SmallestDurationMilliseconds("10us")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10us");
assertThatThrownBy(() -> new SmallestDurationMilliseconds("10µs")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10µs");
assertThatThrownBy(() -> new SmallestDurationMilliseconds("-10s")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: -10s");
}
@Test
public void testValidUnits()
{
assertEquals(10L, new SmallestDurationMilliseconds("10ms").toMilliseconds());
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.config;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
public class SmallestDurationMinutesTest
{
@Test
public void testInvalidUnits()
{
assertThatThrownBy(() -> new SmallestDurationMinutes("10s")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10s");
assertThatThrownBy(() -> new SmallestDurationMinutes("10ms")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10ms");
assertThatThrownBy(() -> new SmallestDurationMinutes("10ns")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10ns");
assertThatThrownBy(() -> new SmallestDurationMinutes("10us")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10us");
assertThatThrownBy(() -> new SmallestDurationMinutes("10µs")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10µs");
assertThatThrownBy(() -> new SmallestDurationMinutes("-10s")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: -10s");
}
@Test
public void testValidUnits()
{
assertEquals(10L, new SmallestDurationMinutes("10m").toMinutes());
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.config;
import org.junit.Test;
import org.apache.cassandra.exceptions.ConfigurationException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
public class SmallestDurationSecondsTest
{
@Test
public void testInvalidUnits()
{
assertThatThrownBy(() -> new SmallestDurationSeconds("10ms")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10ms");
assertThatThrownBy(() -> new SmallestDurationSeconds("10ns")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10ns");
assertThatThrownBy(() -> new SmallestDurationSeconds("10us")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10us");
assertThatThrownBy(() -> new SmallestDurationSeconds("10µs")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: 10µs");
assertThatThrownBy(() -> new SmallestDurationSeconds("-10s")).isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Invalid duration: -10s");
}
@Test
public void testValidUnits()
{
assertEquals(10L, new SmallestDurationSeconds("10s").toSeconds());
}
}

View File

@ -70,8 +70,8 @@ public class PartitionDenylistTest
+ "PRIMARY KEY((keyone, keytwo), keythree) ) ", ks_cql).build()
));
Schema.instance.load(schema);
DatabaseDescriptor.setEnablePartitionDenylist(true);
DatabaseDescriptor.setEnableDenylistRangeReads(true);
DatabaseDescriptor.setPartitionDenylistEnabled(true);
DatabaseDescriptor.setDenylistRangeReadsEnabled(true);
DatabaseDescriptor.setDenylistConsistencyLevel(ConsistencyLevel.ONE);
DatabaseDescriptor.setDenylistRefreshSeconds(1);
StorageService.instance.initServer(0);
@ -80,7 +80,7 @@ public class PartitionDenylistTest
@Before
public void setup()
{
DatabaseDescriptor.setEnablePartitionDenylist(true);
DatabaseDescriptor.setPartitionDenylistEnabled(true);
resetDenylist();
process("INSERT INTO " + ks_cql + ".table1 (keyone, keytwo, qux, quz, foo) VALUES ('aaa', 'bbb', 'ccc', 'ddd', 'v')", ConsistencyLevel.ONE);
@ -282,7 +282,7 @@ public class PartitionDenylistTest
process(String.format("TRUNCATE TABLE %s.table2", ks_cql), ConsistencyLevel.ONE);
process(String.format("TRUNCATE TABLE %s.table3", ks_cql), ConsistencyLevel.ONE);
denyAllKeys();
DatabaseDescriptor.setEnablePartitionDenylist(false);
DatabaseDescriptor.setPartitionDenylistEnabled(false);
process("INSERT INTO " + ks_cql + ".table1 (keyone, keytwo, qux, quz, foo) VALUES ('bbb', 'ccc', 'eee', 'fff', 'w')", ConsistencyLevel.ONE);
process("SELECT * FROM " + ks_cql + ".table1 WHERE keyone='bbb' and keytwo='ccc'", ConsistencyLevel.ONE);
process("SELECT * FROM " + ks_cql + ".table1", ConsistencyLevel.ONE);