mirror of https://github.com/apache/cassandra
Add new custom types and unit tests for configuration
patch by Ekaterina Dimitrova; reviewed by Caleb Rackliffe, David Capwell, Michael Semb Wever and Benjamin Lerer for CASSANDRA-15234
This commit is contained in:
parent
da47849b50
commit
db9f7a67ec
|
|
@ -1,4 +1,5 @@
|
|||
4.1
|
||||
* Standardize storage configuration parameters' names. Support unit suffixes. (CASSANDRA-15234)
|
||||
* Remove support for Windows (CASSANDRA-16956)
|
||||
* Runtime-configurable YAML option to prohibit USE statements (CASSANDRA-17318)
|
||||
* When streaming sees a ClosedChannelException this triggers the disk failure policy (CASSANDRA-17116)
|
||||
|
|
|
|||
6
NEWS.txt
6
NEWS.txt
|
|
@ -81,6 +81,12 @@ New features
|
|||
|
||||
Upgrading
|
||||
---------
|
||||
- There is a new cassandra.yaml version 2. Units suffixes should be provided for all rates(B/s|MiB/s|KiB/s|MiB/s),
|
||||
memory (KiB|MiB|GiB|B) and duration(d|h|s|ms|us|µs|ns|m)
|
||||
parameters. (CASSANDRA-15234)
|
||||
Backward compatibility with the old cassandra.yaml file will be in place until at least the next major version.
|
||||
- Many cassandra.yaml parameters' names have been changed. Full list can be found on ...... (ADD LINK LATER WHEN PAGE
|
||||
IS CREATED) (CASSANDRA-15234)
|
||||
- Before you upgrade, if you are using `cassandra.auth_bcrypt_gensalt_log2_rounds` property,
|
||||
confirm it is set to value lower than 31 otherwise Cassandra will fail to start. See CASSANDRA-9384
|
||||
for further details. You also need to regenerate passwords for users for who the password
|
||||
|
|
|
|||
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* 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.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
|
||||
/**
|
||||
* Represents a data rate type used for cassandra configuration. It supports the opportunity for the users to be able to
|
||||
* add units to the confiuration parameter value. (CASSANDRA-15234)
|
||||
*/
|
||||
public final class DataRateSpec
|
||||
{
|
||||
/**
|
||||
* The Regexp used to parse the rate provided as String in cassandra.yaml.
|
||||
*/
|
||||
private static final Pattern BIT_RATE_UNITS_PATTERN = Pattern.compile("^(\\d+)(MiB/s|KiB/s|B/s)$");
|
||||
|
||||
private final double quantity;
|
||||
|
||||
private final DataRateUnit unit;
|
||||
|
||||
public DataRateSpec(String value)
|
||||
{
|
||||
//parse the string field value
|
||||
Matcher matcher = BIT_RATE_UNITS_PATTERN.matcher(value);
|
||||
|
||||
if (!matcher.find())
|
||||
throw new ConfigurationException("Invalid bit rate: " + value + " Accepted units: MiB/s, KiB/s, B/s where " +
|
||||
"case matters and " + "only non-negative values are valid");
|
||||
|
||||
quantity = Long.parseLong(matcher.group(1));
|
||||
unit = DataRateUnit.fromSymbol(matcher.group(2));
|
||||
}
|
||||
|
||||
DataRateSpec(double quantity, DataRateUnit unit)
|
||||
{
|
||||
if (quantity < 0)
|
||||
throw new ConfigurationException("Invalid bit rare: value must be non-negative");
|
||||
|
||||
if (quantity > Long.MAX_VALUE)
|
||||
throw new NumberFormatException("Invalid bit rate: value must be between 0 and Long.MAX_VALUE = 9223372036854775807");
|
||||
|
||||
this.quantity = quantity;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DataRateSpec} of the specified amount of bits per second.
|
||||
*
|
||||
* @param bytesPerSecond the amount of bytes per second
|
||||
* @return a {@code DataRateSpec}
|
||||
*/
|
||||
public static DataRateSpec inBytesPerSecond(long bytesPerSecond)
|
||||
{
|
||||
return new DataRateSpec(bytesPerSecond, DataRateUnit.BYTES_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DataRateSpec} of the specified amount of kibibytes per second.
|
||||
*
|
||||
* @param kibibytesPerSecond the amount of kibibytes per second
|
||||
* @return a {@code DataRateSpec}
|
||||
*/
|
||||
public static DataRateSpec inKibibytesPerSecond(long kibibytesPerSecond)
|
||||
{
|
||||
return new DataRateSpec(kibibytesPerSecond, DataRateUnit.KIBIBYTES_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DataRateSpec} of the specified amount of mebibytes per second.
|
||||
*
|
||||
* @param mebibytesPerSecond the amount of mebibytes per second
|
||||
* @return a {@code DataRateSpec}
|
||||
*/
|
||||
public static DataRateSpec inMebibytesPerSecond(long mebibytesPerSecond)
|
||||
{
|
||||
return new DataRateSpec(mebibytesPerSecond, DataRateUnit.MEBIBYTES_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DataRateSpec} of the specified amount of mebibytes per second.
|
||||
*
|
||||
* @param megabitsPerSecond the amount of megabits per second
|
||||
* @return a {@code DataRateSpec}
|
||||
*/
|
||||
public static DataRateSpec megabitsPerSecondInMebibytesPerSecond(long megabitsPerSecond)
|
||||
{
|
||||
final double MEBIBYTES_PER_MEGABIT = 0.119209289550781;
|
||||
double mebibytesPerSecond = (double)megabitsPerSecond * MEBIBYTES_PER_MEGABIT;
|
||||
|
||||
return new DataRateSpec(mebibytesPerSecond, DataRateUnit.MEBIBYTES_PER_SECOND);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data rate unit assigned.
|
||||
*/
|
||||
public DataRateUnit getUnit()
|
||||
{
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data rate in bytes per second
|
||||
*/
|
||||
public double toBytesPerSecond()
|
||||
{
|
||||
return unit.toBytesPerSecond(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data rate in bytes per second as an {@code int}
|
||||
*
|
||||
* @return the data rate in bytes per second or {@code Integer.MAX_VALUE} if the rate is too large.
|
||||
*/
|
||||
public int toBytesPerSecondAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(Math.round(toBytesPerSecond()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data rate in kibibytes per second
|
||||
*/
|
||||
public double toKibibytesPerSecond()
|
||||
{
|
||||
return unit.toKibibytesPerSecond(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data rate in kibibytes per second as an {@code int}
|
||||
*
|
||||
* @return the data rate in kibibytes per second or {@code Integer.MAX_VALUE} if the number of kibibytes is too large.
|
||||
*/
|
||||
public int toKibibytesPerSecondAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(Math.round(toKibibytesPerSecond()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data rate in mebibytes per second
|
||||
*/
|
||||
public double toMebibytesPerSecond()
|
||||
{
|
||||
return unit.toMebibytesPerSecond(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data rate in mebibytes per second as an {@code int}
|
||||
*
|
||||
* @return the data rate in mebibytes per second or {@code Integer.MAX_VALUE} if the number of mebibytes is too large.
|
||||
*/
|
||||
public int toMebibytesPerSecondAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(Math.round(toMebibytesPerSecond()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required in order to support backward compatibility with the old unit used for a few Data Rate
|
||||
* parameters before CASSANDRA-15234
|
||||
*
|
||||
* @return the data rate in megabits per second.
|
||||
*/
|
||||
public double toMegabitsPerSecond()
|
||||
{
|
||||
return unit.toMegabitsPerSecond(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data rate in megabits per second as an {@code int}. This method is required in order to support
|
||||
* backward compatibility with the old unit used for a few Data Rate parameters before CASSANDRA-15234
|
||||
*
|
||||
* @return the data rate in mebibytes per second or {@code Integer.MAX_VALUE} if the number of mebibytes is too large.
|
||||
*/
|
||||
public int toMegabitsPerSecondAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(Math.round(toMegabitsPerSecond()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(unit.toKibibytesPerSecond(quantity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (!(obj instanceof DataRateSpec))
|
||||
return false;
|
||||
|
||||
DataRateSpec other = (DataRateSpec) obj;
|
||||
if (unit == other.unit)
|
||||
return quantity == other.quantity;
|
||||
|
||||
// Due to overflows we can only guarantee that the 2 data rates are equal if we get the same results
|
||||
// doing the conversion in both directions.
|
||||
return unit.convert(other.quantity, other.unit) == quantity && other.unit.convert(quantity, unit) == other.quantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return Math.round(quantity) + unit.symbol;
|
||||
}
|
||||
|
||||
public enum DataRateUnit
|
||||
{
|
||||
BYTES_PER_SECOND("B/s")
|
||||
{
|
||||
public double toBytesPerSecond(double d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
public double toKibibytesPerSecond(double d)
|
||||
{
|
||||
return d / 1024.0;
|
||||
}
|
||||
|
||||
public double toMebibytesPerSecond(double d)
|
||||
{
|
||||
return d / (1024.0 * 1024.0);
|
||||
}
|
||||
|
||||
public double toMegabitsPerSecond(double d) { return (d / 125000.0); }
|
||||
|
||||
public double convert(double source, DataRateUnit sourceUnit)
|
||||
{
|
||||
return sourceUnit.toBytesPerSecond(source);
|
||||
}
|
||||
},
|
||||
KIBIBYTES_PER_SECOND("KiB/s")
|
||||
{
|
||||
public double toBytesPerSecond(double d)
|
||||
{
|
||||
return x(d, 1024.0, (MAX / 1024.0));
|
||||
}
|
||||
|
||||
public double toKibibytesPerSecond(double d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
public double toMebibytesPerSecond(double d)
|
||||
{
|
||||
return d / 1024.0;
|
||||
}
|
||||
|
||||
public double toMegabitsPerSecond(double d)
|
||||
{
|
||||
return d / 122.0;
|
||||
}
|
||||
|
||||
public double convert(double source, DataRateUnit sourceUnit)
|
||||
{
|
||||
return sourceUnit.toKibibytesPerSecond(source);
|
||||
}
|
||||
},
|
||||
MEBIBYTES_PER_SECOND("MiB/s")
|
||||
{
|
||||
public double toBytesPerSecond(double d)
|
||||
{
|
||||
return x(d, (1024.0 * 1024.0), (MAX / (1024.0 * 1024.0)));
|
||||
}
|
||||
|
||||
public double toKibibytesPerSecond(double d)
|
||||
{
|
||||
return x(d, 1024.0, (MAX / 1024.0));
|
||||
}
|
||||
|
||||
public double toMebibytesPerSecond(double d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
public double toMegabitsPerSecond(double d)
|
||||
{
|
||||
if (d > MAX / (MEGABITS_PER_MEBIBYTE))
|
||||
return MAX;
|
||||
return Math.round(d * MEGABITS_PER_MEBIBYTE);
|
||||
}
|
||||
|
||||
public double convert(double source, DataRateUnit sourceUnit)
|
||||
{
|
||||
return sourceUnit.toMebibytesPerSecond(source);
|
||||
}
|
||||
};
|
||||
|
||||
static final double MAX = Long.MAX_VALUE;
|
||||
static final double MEGABITS_PER_MEBIBYTE = 8.388608;
|
||||
|
||||
/**
|
||||
* Scale d by m, checking for overflow. This has a short name to make above code more readable.
|
||||
*/
|
||||
static double x(double d, double m, double over)
|
||||
{
|
||||
assert (over > 0.0) && (over < (MAX - 1)) && (over == (MAX / m));
|
||||
|
||||
if (d > over)
|
||||
return MAX;
|
||||
return d * m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param symbol the unit symbol
|
||||
* @return the rate unit corresponding to the given symbol
|
||||
*/
|
||||
public static DataRateUnit fromSymbol(String symbol)
|
||||
{
|
||||
for (DataRateUnit value : values())
|
||||
{
|
||||
if (value.symbol.equalsIgnoreCase(symbol))
|
||||
return value;
|
||||
}
|
||||
throw new ConfigurationException(String.format("Unsupported data rate unit: %s. Supported units are: %s",
|
||||
symbol, Arrays.stream(values())
|
||||
.map(u -> u.symbol)
|
||||
.collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
/**
|
||||
* The unit symbol
|
||||
*/
|
||||
private final String symbol;
|
||||
|
||||
DataRateUnit(String symbol)
|
||||
{
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
public double toBytesPerSecond(double d)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public double toKibibytesPerSecond(double d)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public double toMebibytesPerSecond(double d)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public double toMegabitsPerSecond(double d) { throw new AbstractMethodError(); }
|
||||
|
||||
public double convert(double source, DataRateUnit sourceUnit)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* 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.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* The Regexp used to parse the storage provided as String.
|
||||
*/
|
||||
private static final Pattern STORAGE_UNITS_PATTERN = Pattern.compile("^(\\d+)(GiB|MiB|KiB|B)$");
|
||||
|
||||
private final long quantity;
|
||||
|
||||
private final DataStorageUnit unit;
|
||||
|
||||
public DataStorageSpec(String value)
|
||||
{
|
||||
if (value == null || value.equals("null"))
|
||||
{
|
||||
quantity = 0;
|
||||
unit = DataStorageUnit.MEBIBYTES; // the unit doesn't really matter as 0 is 0 in all units
|
||||
return;
|
||||
}
|
||||
|
||||
//parse the string field value
|
||||
Matcher matcher = STORAGE_UNITS_PATTERN.matcher(value);
|
||||
|
||||
if (!matcher.find())
|
||||
{
|
||||
throw new ConfigurationException("Invalid data storage: " + value + " Accepted units: B, KiB, MiB, GiB" +
|
||||
" where case matters and only non-negative values are accepted");
|
||||
}
|
||||
|
||||
quantity = Long.parseLong(matcher.group(1));
|
||||
unit = DataStorageUnit.fromSymbol(matcher.group(2));
|
||||
}
|
||||
|
||||
DataStorageSpec(long quantity, DataStorageUnit unit)
|
||||
{
|
||||
if (quantity < 0)
|
||||
throw new ConfigurationException("Invalid data storage: value must be positive, but was " + quantity);
|
||||
|
||||
this.quantity = quantity;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DataStorageSpec} of the specified amount of bytes.
|
||||
*
|
||||
* @param bytes the amount of bytes
|
||||
* @return a {@code DataStorageSpec}
|
||||
*/
|
||||
public static DataStorageSpec inBytes(long bytes)
|
||||
{
|
||||
return new DataStorageSpec(bytes, DataStorageUnit.BYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DataStorageSpec} of the specified amount of kibibytes.
|
||||
*
|
||||
* @param kibibytes the amount of kibibytes
|
||||
* @return a {@code DataStorageSpec}
|
||||
*/
|
||||
public static DataStorageSpec inKibibytes(long kibibytes)
|
||||
{
|
||||
return new DataStorageSpec(kibibytes, DataStorageUnit.KIBIBYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DataStorageSpec} of the specified amount of mebibytes.
|
||||
*
|
||||
* @param mebibytes the amount of mebibytes
|
||||
* @return a {@code DataStorageSpec}
|
||||
*/
|
||||
public static DataStorageSpec inMebibytes(long mebibytes)
|
||||
{
|
||||
return new DataStorageSpec(mebibytes, DataStorageUnit.MEBIBYTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the data storage unit.
|
||||
*/
|
||||
public DataStorageUnit getUnit()
|
||||
{
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of data storage in bytes
|
||||
*/
|
||||
public long toBytes()
|
||||
{
|
||||
return unit.toBytes(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of data storage in bytes as an {@code int}
|
||||
*
|
||||
* @return the amount of data storage in bytes or {@code Integer.MAX_VALUE} if the number of bytes is too large.
|
||||
*/
|
||||
public int toBytesAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toBytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of data storage in kibibytes
|
||||
*/
|
||||
public long toKibibytes()
|
||||
{
|
||||
return unit.toKibibytes(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of data storage in kibibytes as an {@code int}
|
||||
*
|
||||
* @return the amount of data storage in kibibytes or {@code Integer.MAX_VALUE} if the number of kibibytes is too large.
|
||||
*/
|
||||
public int toKibibytesAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toKibibytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of data storage in mebibytes
|
||||
*/
|
||||
public long toMebibytes()
|
||||
{
|
||||
return unit.toMebibytes(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of data storage in mebibytes as an {@code int}
|
||||
*
|
||||
* @return the amount of data storage in mebibytes or {@code Integer.MAX_VALUE} if the number of mebibytes is too large.
|
||||
*/
|
||||
public int toMebibytesAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toMebibytes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of data storage in gibibytes
|
||||
*/
|
||||
public long toGibibytes()
|
||||
{
|
||||
return unit.toGibibytes(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of data storage in gibibytes as an {@code int}
|
||||
*
|
||||
* @return the amount of data storage in gibibytes or {@code Integer.MAX_VALUE} if the number of gibibytes is too large.
|
||||
*/
|
||||
public int toGibibytesAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toGibibytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(unit.toKibibytes(quantity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (!(obj instanceof DataStorageSpec))
|
||||
return false;
|
||||
|
||||
DataStorageSpec other = (DataStorageSpec) obj;
|
||||
if (unit == other.unit)
|
||||
return quantity == other.quantity;
|
||||
|
||||
// Due to overflows we can only guarantee that the 2 storages are equal if we get the same results
|
||||
// doing the convertion in both directions.
|
||||
return unit.convert(other.quantity, other.unit) == quantity && other.unit.convert(quantity, unit) == other.quantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return quantity + unit.symbol;
|
||||
}
|
||||
|
||||
public enum DataStorageUnit
|
||||
{
|
||||
BYTES("B")
|
||||
{
|
||||
public long toBytes(long d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
public long toKibibytes(long d)
|
||||
{
|
||||
return (d / 1024L);
|
||||
}
|
||||
|
||||
public long toMebibytes(long d)
|
||||
{
|
||||
return (d / (1024L * 1024));
|
||||
}
|
||||
|
||||
public long toGibibytes(long d)
|
||||
{
|
||||
return (d / (1024L * 1024 * 1024));
|
||||
}
|
||||
|
||||
public long convert(long source, DataStorageUnit sourceUnit)
|
||||
{
|
||||
return sourceUnit.toBytes(source);
|
||||
}
|
||||
},
|
||||
KIBIBYTES("KiB")
|
||||
{
|
||||
public long toBytes(long d)
|
||||
{
|
||||
return x(d, 1024L, (MAX / 1024L));
|
||||
}
|
||||
|
||||
public long toKibibytes(long d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
public long toMebibytes(long d)
|
||||
{
|
||||
return (d / 1024L);
|
||||
}
|
||||
|
||||
public long toGibibytes(long d)
|
||||
{
|
||||
return (d / (1024L * 1024));
|
||||
}
|
||||
|
||||
public long convert(long source, DataStorageUnit sourceUnit)
|
||||
{
|
||||
return sourceUnit.toKibibytes(source);
|
||||
}
|
||||
},
|
||||
MEBIBYTES("MiB")
|
||||
{
|
||||
public long toBytes(long d)
|
||||
{
|
||||
return x(d, (1024L * 1024), MAX / (1024L * 1024));
|
||||
}
|
||||
|
||||
public long toKibibytes(long d)
|
||||
{
|
||||
return x(d, 1024L, (MAX / 1024L));
|
||||
}
|
||||
|
||||
public long toMebibytes(long d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
public long toGibibytes(long d)
|
||||
{
|
||||
return (d / 1024L);
|
||||
}
|
||||
|
||||
public long convert(long source, DataStorageUnit sourceUnit)
|
||||
{
|
||||
return sourceUnit.toMebibytes(source);
|
||||
}
|
||||
},
|
||||
GIBIBYTES("GiB")
|
||||
{
|
||||
public long toBytes(long d)
|
||||
{
|
||||
return x(d, (1024L * 1024 * 1024), MAX / (1024L * 1024 * 1024));
|
||||
}
|
||||
|
||||
public long toKibibytes(long d)
|
||||
{
|
||||
return x(d, (1024L * 1024), MAX / (1024L * 1024));
|
||||
}
|
||||
|
||||
public long toMebibytes(long d)
|
||||
{
|
||||
return x(d, 1024L, (MAX / 1024L));
|
||||
}
|
||||
|
||||
public long toGibibytes(long d)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
|
||||
public long convert(long source, DataStorageUnit sourceUnit)
|
||||
{
|
||||
return sourceUnit.toGibibytes(source);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scale d by m, checking for overflow. This has a short name to make above code more readable.
|
||||
*/
|
||||
static long x(long d, long m, long over)
|
||||
{
|
||||
assert (over > 0) && (over < (MAX-1L)) && (over == (MAX / m));
|
||||
|
||||
if (d > over)
|
||||
return Long.MAX_VALUE;
|
||||
return Math.multiplyExact(d, m);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param symbol the unit symbol
|
||||
* @return the memory unit corresponding to the given symbol
|
||||
*/
|
||||
public static DataStorageUnit fromSymbol(String symbol)
|
||||
{
|
||||
for (DataStorageUnit value : values())
|
||||
{
|
||||
if (value.symbol.equalsIgnoreCase(symbol))
|
||||
return value;
|
||||
}
|
||||
throw new ConfigurationException(String.format("Unsupported data storage unit: %s. Supported units are: %s",
|
||||
symbol, Arrays.stream(values())
|
||||
.map(u -> u.symbol)
|
||||
.collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
static final long MAX = Long.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* The unit symbol
|
||||
*/
|
||||
private final String symbol;
|
||||
|
||||
DataStorageUnit(String symbol)
|
||||
{
|
||||
this.symbol = symbol;
|
||||
}
|
||||
|
||||
public long toBytes(long d)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public long toKibibytes(long d)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public long toMebibytes(long d)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public long toGibibytes(long d)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
|
||||
public long convert(long source, DataStorageUnit sourceUnit)
|
||||
{
|
||||
throw new AbstractMethodError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
* 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.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* 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 VALUES_PATTERN = Pattern.compile(("\\d+"));
|
||||
|
||||
public final long quantity;
|
||||
|
||||
private final TimeUnit unit;
|
||||
|
||||
public DurationSpec(String value)
|
||||
{
|
||||
if (value == null || value.equals("null") || value.toLowerCase(Locale.ROOT).equals("nan"))
|
||||
{
|
||||
quantity = 0;
|
||||
unit = TimeUnit.MILLISECONDS;
|
||||
return;
|
||||
}
|
||||
|
||||
//parse the string field value
|
||||
Matcher matcher = TIME_UNITS_PATTERN.matcher(value);
|
||||
|
||||
if(matcher.find())
|
||||
{
|
||||
quantity = Long.parseLong(matcher.group(1));
|
||||
unit = fromSymbol(matcher.group(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ConfigurationException("Invalid duration: " + value + " Accepted units: d, h, m, s, ms, us, µs," +
|
||||
" ns where case matters and " + "only non-negative values");
|
||||
}
|
||||
}
|
||||
|
||||
DurationSpec(long quantity, TimeUnit unit)
|
||||
{
|
||||
if (quantity < 0)
|
||||
throw new ConfigurationException("Invalid duration: value must be positive");
|
||||
|
||||
this.quantity = quantity;
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
private DurationSpec(double quantity, TimeUnit unit)
|
||||
{
|
||||
this(Math.round(quantity), unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DurationSpec} of the specified amount of milliseconds.
|
||||
*
|
||||
* @param milliseconds the amount of milliseconds
|
||||
* @return a duration
|
||||
*/
|
||||
public static DurationSpec inMilliseconds(long milliseconds)
|
||||
{
|
||||
return new DurationSpec(milliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public static DurationSpec inDoubleMilliseconds(double milliseconds)
|
||||
{
|
||||
return new DurationSpec(milliseconds, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DurationSpec} of the specified amount of seconds.
|
||||
*
|
||||
* @param seconds the amount of seconds
|
||||
* @return a duration
|
||||
*/
|
||||
public static DurationSpec inSeconds(long seconds)
|
||||
{
|
||||
return new DurationSpec(seconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DurationSpec} of the specified amount of minutes.
|
||||
*
|
||||
* @param minutes the amount of minutes
|
||||
* @return a duration
|
||||
*/
|
||||
public static DurationSpec inMinutes(long minutes)
|
||||
{
|
||||
return new DurationSpec(minutes, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DurationSpec} of the specified amount of hours.
|
||||
*
|
||||
* @param hours the amount of hours
|
||||
* @return a duration
|
||||
*/
|
||||
public static DurationSpec inHours(long hours)
|
||||
{
|
||||
return new DurationSpec(hours, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code DurationSpec} of the specified amount of seconds. Custom method for special cases.
|
||||
*
|
||||
* @param value which can be in the old form only presenting the quantity or the post CASSANDRA-15234 form - a
|
||||
* value consisting of quantity and unit. This method is necessary for three parameters which didn't change their
|
||||
* names but only their value format. (key_cache_save_period, row_cache_save_period, counter_cache_save_period)
|
||||
* @return a duration
|
||||
*/
|
||||
public static DurationSpec inSecondsString(String value)
|
||||
{
|
||||
//parse the string field value
|
||||
Matcher matcher = VALUES_PATTERN.matcher(value);
|
||||
|
||||
long seconds;
|
||||
//if the provided string value is just a number, then we create a Duration Spec value in seconds
|
||||
if (matcher.matches())
|
||||
{
|
||||
seconds = Long.parseLong(value);
|
||||
return new DurationSpec(seconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
//otherwise we just use the standard constructors
|
||||
return new DurationSpec(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param symbol the time unit symbol
|
||||
* @return the time unit associated to the specified symbol
|
||||
*/
|
||||
static TimeUnit fromSymbol(String symbol)
|
||||
{
|
||||
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 "us":
|
||||
case "µs": return TimeUnit.MICROSECONDS;
|
||||
case "ns": return TimeUnit.NANOSECONDS;
|
||||
}
|
||||
throw new ConfigurationException(String.format("Unsupported time unit: %s. Supported units are: %s",
|
||||
symbol, Arrays.stream(TimeUnit.values())
|
||||
.map(DurationSpec::getSymbol)
|
||||
.collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param targetUnit the time unit
|
||||
* @return this duration in the specified time unit
|
||||
*/
|
||||
public long to(TimeUnit targetUnit)
|
||||
{
|
||||
return targetUnit.convert(quantity, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this duration in number of hours
|
||||
*/
|
||||
public long toHours()
|
||||
{
|
||||
return unit.toHours(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this duration in number of minutes as an {@code int}
|
||||
*
|
||||
* @return this duration in number of minutes or {@code Integer.MAX_VALUE} if the number of minutes is too large.
|
||||
*/
|
||||
public int toHoursAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toHours());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this duration in number of minutes
|
||||
*/
|
||||
public long toMinutes()
|
||||
{
|
||||
return unit.toMinutes(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this duration in number of minutes as an {@code int}
|
||||
*
|
||||
* @return this duration in number of minutes or {@code Integer.MAX_VALUE} if the number of minutes is too large.
|
||||
*/
|
||||
public int toMinutesAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toMinutes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this duration in number of seconds
|
||||
*/
|
||||
public long toSeconds()
|
||||
{
|
||||
return unit.toSeconds(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this duration in number of seconds as an {@code int}
|
||||
*
|
||||
* @return this duration in number of seconds or {@code Integer.MAX_VALUE} if the number of seconds is too large.
|
||||
*/
|
||||
public int toSecondsAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toSeconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this duration in number of nanoseconds
|
||||
*/
|
||||
public long toNanoseconds()
|
||||
{
|
||||
return unit.toNanos(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this duration in number of nanoseconds as an {@code int}
|
||||
*
|
||||
* @return this duration in number of nanoseconds or {@code Integer.MAX_VALUE} if the number of nanoseconds is too large.
|
||||
*/
|
||||
public int toNanosecondsAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toNanoseconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this duration in number of milliseconds
|
||||
*/
|
||||
public long toMilliseconds()
|
||||
{
|
||||
return unit.toMillis(quantity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the duration value in milliseconds
|
||||
*/
|
||||
public static long toMilliseconds(DurationSpec quantity)
|
||||
{
|
||||
return quantity.toMilliseconds();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this duration in number of milliseconds as an {@code int}
|
||||
*
|
||||
* @return this duration in number of milliseconds or {@code Integer.MAX_VALUE} if the number of milliseconds is too large.
|
||||
*/
|
||||
public int toMillisecondsAsInt()
|
||||
{
|
||||
return Ints.saturatedCast(toMilliseconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
// Milliseconds seems to be a reasonable tradeoff
|
||||
return Objects.hash(unit.toMillis(quantity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
|
||||
if (!(obj instanceof DurationSpec))
|
||||
return false;
|
||||
|
||||
DurationSpec other = (DurationSpec) obj;
|
||||
if (unit == other.unit)
|
||||
return quantity == other.quantity;
|
||||
|
||||
// Due to overflows we can only guarantee that the 2 durations are equal if we get the same results
|
||||
// doing the conversion in both directions.
|
||||
return unit.convert(other.quantity, other.unit) == quantity && other.unit.convert(quantity, unit) == other.quantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return quantity + getSymbol(unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the symbol associated to the specified unit
|
||||
*
|
||||
* @param unit the time unit
|
||||
* @return the time unit symbol
|
||||
*/
|
||||
static String getSymbol(TimeUnit unit)
|
||||
{
|
||||
switch (unit)
|
||||
{
|
||||
case DAYS: return "d";
|
||||
case HOURS: return "h";
|
||||
case MINUTES: return "m";
|
||||
case SECONDS: return "s";
|
||||
case MILLISECONDS: return "ms";
|
||||
case MICROSECONDS: return "us";
|
||||
case NANOSECONDS: return "ns";
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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 org.quicktheories.core.Gen;
|
||||
import org.quicktheories.generators.SourceDSL;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.quicktheories.QuickTheory.qt;
|
||||
|
||||
public class DataRateSpecTest
|
||||
{
|
||||
@Test
|
||||
public void testConversions()
|
||||
{
|
||||
assertEquals(10, new DataRateSpec("10B/s").toBytesPerSecond(), 0);
|
||||
assertEquals(10240, new DataRateSpec("10KiB/s").toBytesPerSecond(), 0);
|
||||
assertEquals(0, new DataRateSpec("10KiB/s").toMebibytesPerSecond(), 0.1);
|
||||
assertEquals(10240, new DataRateSpec("10MiB/s").toKibibytesPerSecond(), 0);
|
||||
assertEquals(10485760, new DataRateSpec("10MiB/s").toBytesPerSecond(), 0);
|
||||
assertEquals(10485760, new DataRateSpec("10MiB/s").toBytesPerSecond(), 0);
|
||||
assertEquals(new DataRateSpec("24MiB/s").toString(), DataRateSpec.megabitsPerSecondInMebibytesPerSecond(200L).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverflowingDuringConversion()
|
||||
{
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807B/s").toBytesPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807B/s").toBytesPerSecondAsInt(), 0);
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toBytesPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toBytesPerSecondAsInt(), 0);
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecondAsInt(), 0);
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toBytesPerSecondAsInt(), 0);
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMegabitsPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMegabitsPerSecondAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toKibibytesPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807KiB/s").toKibibytesPerSecondAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toKibibytesPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toKibibytesPerSecondAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMebibytesPerSecond(), 0);
|
||||
assertEquals(Integer.MAX_VALUE, new DataRateSpec("9223372036854775807MiB/s").toMebibytesPerSecondAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSymbol()
|
||||
{
|
||||
assertEquals(DataRateSpec.DataRateUnit.fromSymbol("B/s"), DataRateSpec.DataRateUnit.BYTES_PER_SECOND);
|
||||
assertEquals(DataRateSpec.DataRateUnit.fromSymbol("KiB/s"), DataRateSpec.DataRateUnit.KIBIBYTES_PER_SECOND);
|
||||
assertEquals(DataRateSpec.DataRateUnit.fromSymbol("MiB/s"), DataRateSpec.DataRateUnit.MEBIBYTES_PER_SECOND);
|
||||
assertThatThrownBy(() -> DataRateSpec.DataRateUnit.fromSymbol("n"))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Unsupported data rate unit: n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidInputs()
|
||||
{
|
||||
assertThatThrownBy(() -> new DataRateSpec("10")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid bit rate: 10");
|
||||
assertThatThrownBy(() -> new DataRateSpec("-10b/s")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid bit rate: -10b/s");
|
||||
assertThatThrownBy(() -> new DataRateSpec("10xb/s")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid bit rate: 10xb/s");
|
||||
assertThatThrownBy(() -> new DataRateSpec("9223372036854775809B/s")
|
||||
.toBytesPerSecond()).isInstanceOf(NumberFormatException.class)
|
||||
.hasMessageContaining("For input string: \"9223372036854775809\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals()
|
||||
{
|
||||
assertEquals(new DataRateSpec("10B/s"), new DataRateSpec("10B/s"));
|
||||
assertEquals(new DataRateSpec("10KiB/s"), new DataRateSpec("10240B/s"));
|
||||
assertEquals(new DataRateSpec("10240B/s"), new DataRateSpec("10KiB/s"));
|
||||
assertEquals(DataRateSpec.inMebibytesPerSecond(Long.MAX_VALUE), DataRateSpec.inMebibytesPerSecond(Long.MAX_VALUE));
|
||||
assertNotEquals(DataRateSpec.inMebibytesPerSecond(Long.MAX_VALUE), DataRateSpec.inBytesPerSecond(Long.MAX_VALUE));
|
||||
assertNotEquals(new DataRateSpec("0KiB/s"), new DataRateSpec("10MiB/s"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thereAndBack()
|
||||
{
|
||||
Gen<DataRateSpec.DataRateUnit> unitGen = SourceDSL.arbitrary().enumValues(DataRateSpec.DataRateUnit.class);
|
||||
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
|
||||
qt().forAll(valueGen, unitGen).check((value, unit) -> {
|
||||
DataRateSpec there = new DataRateSpec(value, unit);
|
||||
DataRateSpec back = new DataRateSpec(there.toString());
|
||||
DataRateSpec BACK = new DataRateSpec(there.toString());
|
||||
return there.equals(back) && there.equals(BACK);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq()
|
||||
{
|
||||
qt().forAll(gen(), gen()).check((a, b) -> a.equals(b) == b.equals(a));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eqAndHash()
|
||||
{
|
||||
qt().forAll(gen(), gen()).check((a, b) -> !a.equals(b) || a.hashCode() == b.hashCode());
|
||||
}
|
||||
|
||||
private static Gen<DataRateSpec> gen()
|
||||
{
|
||||
Gen<DataRateSpec.DataRateUnit> unitGen = SourceDSL.arbitrary().enumValues(DataRateSpec.DataRateUnit.class);
|
||||
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
|
||||
Gen<DataRateSpec> gen = rs -> new DataRateSpec(valueGen.generate(rs), unitGen.generate(rs));
|
||||
return gen.describedAs(DataRateSpec::toString);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* 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.Locale;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.quicktheories.core.Gen;
|
||||
import org.quicktheories.generators.SourceDSL;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.quicktheories.QuickTheory.qt;
|
||||
|
||||
public class DataStorageSpecTest
|
||||
{
|
||||
@Test
|
||||
public void testConversions()
|
||||
{
|
||||
assertEquals(10, new DataStorageSpec("10B").toBytes());
|
||||
assertEquals(10240, new DataStorageSpec("10KiB").toBytes());
|
||||
assertEquals(0, new DataStorageSpec("10KiB").toMebibytes());
|
||||
assertEquals(10240, new DataStorageSpec("10MiB").toKibibytes());
|
||||
assertEquals(10485760, new DataStorageSpec("10MiB").toBytes());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverflowingDuringConversion()
|
||||
{
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807B").toBytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807B").toBytesAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toBytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toBytesAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toBytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toBytesAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toBytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toBytesAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toKibibytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807KiB").toKibibytesAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toKibibytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toKibibytesAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toKibibytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toKibibytesAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toMebibytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807MiB").toMebibytesAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toMebibytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toMebibytesAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toGibibytes());
|
||||
assertEquals(Integer.MAX_VALUE, new DataStorageSpec("9223372036854775807GiB").toGibibytesAsInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSymbol()
|
||||
{
|
||||
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("B"), DataStorageSpec.DataStorageUnit.BYTES);
|
||||
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("KiB"), DataStorageSpec.DataStorageUnit.KIBIBYTES);
|
||||
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("MiB"), DataStorageSpec.DataStorageUnit.MEBIBYTES);
|
||||
assertEquals(DataStorageSpec.DataStorageUnit.fromSymbol("GiB"), DataStorageSpec.DataStorageUnit.GIBIBYTES);
|
||||
assertThatThrownBy(() -> DataStorageSpec.DataStorageUnit.fromSymbol("n"))
|
||||
.isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Unsupported data storage unit: n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidInputs()
|
||||
{
|
||||
assertThatThrownBy(() -> new DataStorageSpec("10")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid data storage: 10");
|
||||
assertThatThrownBy(() -> new DataStorageSpec("-10bps")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid data storage: -10bps");
|
||||
assertThatThrownBy(() -> new DataStorageSpec("-10b")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid data storage: -10b");
|
||||
assertThatThrownBy(() -> new DataStorageSpec("10HG")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid data storage: 10HG");
|
||||
assertThatThrownBy(() -> new DataStorageSpec("9223372036854775809B")
|
||||
.toBytes()).isInstanceOf(NumberFormatException.class)
|
||||
.hasMessageContaining("For input string: \"9223372036854775809\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals()
|
||||
{
|
||||
assertEquals(new DataStorageSpec("10B"), new DataStorageSpec("10B"));
|
||||
assertEquals(new DataStorageSpec("10KiB"), new DataStorageSpec("10240B"));
|
||||
assertEquals(new DataStorageSpec("10240B"), new DataStorageSpec("10KiB"));
|
||||
assertEquals(DataStorageSpec.inMebibytes(Long.MAX_VALUE), DataStorageSpec.inMebibytes(Long.MAX_VALUE));
|
||||
assertNotEquals(DataStorageSpec.inMebibytes(Long.MAX_VALUE), DataStorageSpec.inKibibytes(Long.MAX_VALUE));
|
||||
assertNotEquals(DataStorageSpec.inMebibytes(Long.MAX_VALUE), DataStorageSpec.inBytes(Long.MAX_VALUE));
|
||||
assertNotEquals(new DataStorageSpec("0MiB"), new DataStorageSpec("10KiB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thereAndBack()
|
||||
{
|
||||
qt().forAll(gen()).check(there -> {
|
||||
DataStorageSpec back = new DataStorageSpec(there.toString());
|
||||
DataStorageSpec BACK = new DataStorageSpec(there.toString().toUpperCase(Locale.ROOT).replace("I", "i"));
|
||||
return there.equals(back) && there.equals(BACK);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eq()
|
||||
{
|
||||
qt().forAll(gen(), gen()).check((a, b) -> a.equals(b) == b.equals(a));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eqAndHash()
|
||||
{
|
||||
qt().forAll(gen(), gen()).check((a, b) -> !a.equals(b) || a.hashCode() == b.hashCode());
|
||||
}
|
||||
|
||||
private static Gen<DataStorageSpec> gen()
|
||||
{
|
||||
Gen<DataStorageSpec.DataStorageUnit> unitGen = SourceDSL.arbitrary().enumValues(DataStorageSpec.DataStorageUnit.class);
|
||||
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
|
||||
Gen<DataStorageSpec> gen = rs -> new DataStorageSpec(valueGen.generate(rs), unitGen.generate(rs));
|
||||
return gen.describedAs(DataStorageSpec::toString);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.quicktheories.core.Gen;
|
||||
import org.quicktheories.generators.SourceDSL;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.quicktheories.QuickTheory.qt;
|
||||
|
||||
public class DurationSpecTest
|
||||
{
|
||||
@Test
|
||||
public void testConversions()
|
||||
{
|
||||
assertEquals(10L, new DurationSpec("10s").toSeconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toSecondsAsInt());
|
||||
assertEquals(10000, new DurationSpec("10s").toMilliseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toMillisecondsAsInt());
|
||||
assertEquals(0, new DurationSpec("10s").toMinutes());
|
||||
assertEquals(10, new DurationSpec("10m").toMinutes());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toMinutesAsInt());
|
||||
assertEquals(600000, new DurationSpec("10m").toMilliseconds());
|
||||
assertEquals(600, new DurationSpec("10m").toSeconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toSecondsAsInt());
|
||||
assertEquals(DurationSpec.inDoubleMilliseconds(0.7), new DurationSpec("1ms"));
|
||||
assertEquals(DurationSpec.inDoubleMilliseconds(0.33), new DurationSpec("0ms"));
|
||||
assertEquals(DurationSpec.inDoubleMilliseconds(0.333), new DurationSpec("0ms"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromSymbol()
|
||||
{
|
||||
assertEquals(DurationSpec.fromSymbol("ms"), TimeUnit.MILLISECONDS);
|
||||
assertEquals(DurationSpec.fromSymbol("d"), TimeUnit.DAYS);
|
||||
assertEquals(DurationSpec.fromSymbol("h"), TimeUnit.HOURS);
|
||||
assertEquals(DurationSpec.fromSymbol("m"), TimeUnit.MINUTES);
|
||||
assertEquals(DurationSpec.fromSymbol("s"), TimeUnit.SECONDS);
|
||||
assertEquals(DurationSpec.fromSymbol("us"), TimeUnit.MICROSECONDS);
|
||||
assertEquals(DurationSpec.fromSymbol("µs"), TimeUnit.MICROSECONDS);
|
||||
assertEquals(DurationSpec.fromSymbol("ns"), TimeUnit.NANOSECONDS);
|
||||
assertThatThrownBy(() -> DurationSpec.fromSymbol("n")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Unsupported time unit: n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSymbol()
|
||||
{
|
||||
assertEquals(DurationSpec.getSymbol(TimeUnit.MILLISECONDS), "ms");
|
||||
assertEquals(DurationSpec.getSymbol(TimeUnit.DAYS), "d");
|
||||
assertEquals(DurationSpec.getSymbol(TimeUnit.HOURS), "h");
|
||||
assertEquals(DurationSpec.getSymbol(TimeUnit.MINUTES), "m");
|
||||
assertEquals(DurationSpec.getSymbol(TimeUnit.SECONDS), "s");
|
||||
assertEquals(DurationSpec.getSymbol(TimeUnit.MICROSECONDS), "us");
|
||||
assertEquals(DurationSpec.getSymbol(TimeUnit.NANOSECONDS), "ns");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidInputs()
|
||||
{
|
||||
assertThatThrownBy(() -> new DurationSpec("10")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid duration: 10");
|
||||
assertThatThrownBy(() -> new DurationSpec("-10s")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid duration: -10s");
|
||||
assertThatThrownBy(() -> new DurationSpec("10xd")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid duration: 10xd");
|
||||
assertThatThrownBy(() -> new DurationSpec("0.333555555ms")).isInstanceOf(ConfigurationException.class)
|
||||
.hasMessageContaining("Invalid duration: 0.333555555ms");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals()
|
||||
{
|
||||
assertEquals(new DurationSpec("10s"), new DurationSpec("10s"));
|
||||
assertEquals(new DurationSpec("10s"), new DurationSpec("10000ms"));
|
||||
assertEquals(new DurationSpec("10000ms"), new DurationSpec("10s"));
|
||||
assertEquals(DurationSpec.inMinutes(Long.MAX_VALUE), DurationSpec.inMinutes(Long.MAX_VALUE));
|
||||
assertEquals(new DurationSpec("4h"), new DurationSpec("14400s"));
|
||||
assertEquals(DurationSpec.inSecondsString("14400"), new DurationSpec("14400s"));
|
||||
assertEquals(DurationSpec.inSecondsString("4h"), new DurationSpec("14400s"));
|
||||
assertEquals(DurationSpec.inSecondsString("14400s"), new DurationSpec("14400s"));
|
||||
assertEquals(DurationSpec.inHours(Long.MAX_VALUE),DurationSpec.inHours(Long.MAX_VALUE));
|
||||
assertNotEquals(DurationSpec.inMinutes(Long.MAX_VALUE), DurationSpec.inMilliseconds(Long.MAX_VALUE));
|
||||
assertNotEquals(new DurationSpec("0m"), new DurationSpec("10ms"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thereAndBack()
|
||||
{
|
||||
Gen<TimeUnit> unitGen = SourceDSL.arbitrary().enumValues(TimeUnit.class);
|
||||
Gen<Long> valueGen = SourceDSL.longs().between(0, Long.MAX_VALUE);
|
||||
qt().forAll(valueGen, unitGen).check((value, unit) -> {
|
||||
DurationSpec there = new DurationSpec(value, unit);
|
||||
DurationSpec back = new DurationSpec(there.toString());
|
||||
return there.equals(back);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverflowingDuringConversion()
|
||||
{
|
||||
// we are heavily dependent on the Java TimeUnit for our configuration of type duration. We want to be sure
|
||||
// that any regression in handlining overflow will be caught quickly on our end
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807ns").toNanoseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807ns").toNanosecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807ms").toNanoseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807ms").toNanosecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807s").toNanoseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toNanosecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toNanoseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toNanosecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toNanoseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toNanosecondsAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807ms").toMilliseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807ms").toMillisecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807s").toMilliseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toMillisecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toMilliseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toMillisecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toMilliseconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toMillisecondsAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807s").toSeconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807s").toSecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toSeconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toSecondsAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toSeconds());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toSecondsAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807m").toMinutes());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807m").toMinutesAsInt());
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toMinutes());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toMinutesAsInt());
|
||||
|
||||
assertEquals(Long.MAX_VALUE, new DurationSpec("9223372036854775807h").toHours());
|
||||
assertEquals(Integer.MAX_VALUE, new DurationSpec("9223372036854775807h").toHoursAsInt());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue