This is an automated email from the ASF dual-hosted git repository.
kturner pushed a commit to branch 3.1
in repository https://gitbox.apache.org/repos/asf/accumulo.git
The following commit(s) were added to refs/heads/3.1 by this push:
new 9f8c06979f caches duration computation in accumulo configuration
(#4890)
9f8c06979f is described below
commit 9f8c06979fe62b882c08b9fc0a7b24a75a77d693
Author: Keith Turner <[email protected]>
AuthorDate: Tue Sep 17 12:11:52 2024 -0400
caches duration computation in accumulo configuration (#4890)
---
.../accumulo/core/conf/AccumuloConfiguration.java | 39 +++++++-
.../core/conf/AccumuloConfigurationTest.java | 103 +++++++++++++++++++++
2 files changed, 139 insertions(+), 3 deletions(-)
diff --git
a/core/src/main/java/org/apache/accumulo/core/conf/AccumuloConfiguration.java
b/core/src/main/java/org/apache/accumulo/core/conf/AccumuloConfiguration.java
index 5dc3a29c2c..724dab45b9 100644
---
a/core/src/main/java/org/apache/accumulo/core/conf/AccumuloConfiguration.java
+++
b/core/src/main/java/org/apache/accumulo/core/conf/AccumuloConfiguration.java
@@ -22,7 +22,9 @@ import static
org.apache.accumulo.core.conf.Property.GENERAL_ARBITRARY_PROP_PREF
import static org.apache.accumulo.core.conf.Property.INSTANCE_CRYPTO_PREFIX;
import static org.apache.accumulo.core.conf.Property.TABLE_CRYPTO_PREFIX;
+import java.time.Duration;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
@@ -71,6 +73,27 @@ public abstract class AccumuloConfiguration implements
Iterable<Entry<String,Str
private static final Logger log =
LoggerFactory.getLogger(AccumuloConfiguration.class);
+ private static final List<Property> DURATION_PROPS =
Arrays.stream(Property.values())
+ .filter(property -> property.getType() == PropertyType.TIMEDURATION)
+ .collect(Collectors.toUnmodifiableList());
+
+ private final Deriver<EnumMap<Property,Duration>> durationDeriver =
newDeriver(conf -> {
+ EnumMap<Property,Duration> durations = new EnumMap<>(Property.class);
+ for (Property prop : DURATION_PROPS) {
+ var value = conf.get(prop);
+ if (value != null) {
+ try {
+ var durationMillis = ConfigurationTypeHelper.getTimeInMillis(value);
+ durations.put(prop, Duration.ofMillis(durationMillis));
+ } catch (RuntimeException e) {
+ log.error("Failed to parse duration for {}={}", prop, value, e);
+ }
+ }
+ }
+ log.trace("recomputed durations {}", durations);
+ return durations;
+ });
+
/**
* Gets a property value from this configuration.
*
@@ -259,13 +282,23 @@ public abstract class AccumuloConfiguration implements
Iterable<Entry<String,Str
* Gets a property of type {@link PropertyType#TIMEDURATION}, interpreting
the value properly.
*
* @param property property to get
- * @return property value
* @throws IllegalArgumentException if the property is of the wrong type
*/
- public long getTimeInMillis(Property property) {
+ public Duration getDuration(Property property) {
checkType(property, PropertyType.TIMEDURATION);
+ return Objects.requireNonNull(durationDeriver.derive().get(property), ()
-> "Property "
+ + property.getKey() + " is not set or its value did not parse
correctly.");
+ }
- return ConfigurationTypeHelper.getTimeInMillis(get(property));
+ /**
+ * Gets a property of type {@link PropertyType#TIMEDURATION}, interpreting
the value properly.
+ *
+ * @param property property to get
+ * @return property value
+ * @throws IllegalArgumentException if the property is of the wrong type
+ */
+ public long getTimeInMillis(Property property) {
+ return getDuration(property).toMillis();
}
/**
diff --git
a/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
b/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
index 51c4930a95..cfcf240306 100644
---
a/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
+++
b/core/src/test/java/org/apache/accumulo/core/conf/AccumuloConfigurationTest.java
@@ -27,6 +27,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Field;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
@@ -445,4 +447,105 @@ public class AccumuloConfigurationTest {
}
}
+ @Test
+ public void testDefaultDurations() {
+ var conf = DefaultConfiguration.getInstance();
+ for (Property prop : Property.values()) {
+ if (prop.getType() == PropertyType.TIMEDURATION) {
+ var expectedDuration =
+
Duration.ofMillis(ConfigurationTypeHelper.getTimeInMillis(prop.getDefaultValue()));
+ assertEquals(expectedDuration, conf.getDuration(prop));
+ assertEquals(expectedDuration.toMillis(), conf.getTimeInMillis(prop));
+ }
+ }
+ }
+
+ /*
+ * AccumuloConfiguration will cache the results of computing durations, this
test ensures it
+ * recomputes them on change.
+ */
+ @Test
+ public void testDurationUpdate() {
+ var conf = new ConfigurationCopy(DefaultConfiguration.getInstance());
+
+ for (Property prop : Property.values()) {
+ if (prop.getType() == PropertyType.TIMEDURATION) {
+ var expectedDuration =
+
Duration.ofMillis(ConfigurationTypeHelper.getTimeInMillis(prop.getDefaultValue()));
+ assertEquals(expectedDuration, conf.getDuration(prop));
+ assertEquals(expectedDuration.toMillis(), conf.getTimeInMillis(prop));
+ }
+ }
+
+ for (int toAdd = 1; toAdd <= 4; toAdd++) {
+ for (Property prop : Property.values()) {
+ if (prop.getType() == PropertyType.TIMEDURATION) {
+ var defaultDuration =
+
Duration.ofMillis(ConfigurationTypeHelper.getTimeInMillis(prop.getDefaultValue()));
+ var expectedDuration = defaultDuration.plusMinutes(toAdd);
+ conf.set(prop.getKey(), expectedDuration.toMillis() + "ms");
+ }
+ }
+
+ for (Property prop : Property.values()) {
+ if (prop.getType() == PropertyType.TIMEDURATION) {
+ var defaultDuration =
+
Duration.ofMillis(ConfigurationTypeHelper.getTimeInMillis(prop.getDefaultValue()));
+ var expectedDuration = defaultDuration.plus(toAdd,
ChronoUnit.MINUTES);
+ assertEquals(expectedDuration, conf.getDuration(prop));
+ assertEquals(expectedDuration.toMillis(),
conf.getTimeInMillis(prop));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testMalformedDuration() {
+ assertEquals(PropertyType.TIMEDURATION,
Property.TSERV_WAL_MAX_AGE.getType());
+ assertEquals(PropertyType.TIMEDURATION,
Property.GENERAL_RPC_TIMEOUT.getType());
+
+ var conf = new ConfigurationCopy(DefaultConfiguration.getInstance());
+ conf.getDuration(Property.TSERV_WAL_MAX_AGE);
+ var expectedDuration = Duration.ofMillis(
+
ConfigurationTypeHelper.getTimeInMillis(Property.TSERV_WAL_MAX_AGE.getDefaultValue()));
+ var rpcTimeout = Duration.ofMillis(
+
ConfigurationTypeHelper.getTimeInMillis(Property.GENERAL_RPC_TIMEOUT.getDefaultValue()));
+ assertEquals(expectedDuration,
conf.getDuration(Property.TSERV_WAL_MAX_AGE));
+ assertEquals(rpcTimeout, conf.getDuration(Property.GENERAL_RPC_TIMEOUT));
+ conf.set(Property.TSERV_WAL_MAX_AGE.getKey(), "abc");
+ conf.set(Property.GENERAL_RPC_TIMEOUT, "2h");
+ var npe = assertThrows(NullPointerException.class,
+ () -> conf.getDuration(Property.TSERV_WAL_MAX_AGE));
+ assertTrue(npe.getMessage().contains(Property.TSERV_WAL_MAX_AGE.getKey()));
+ assertEquals(Duration.ofHours(2),
conf.getDuration(Property.GENERAL_RPC_TIMEOUT));
+ conf.set(Property.TSERV_WAL_MAX_AGE.getKey(), "1h");
+ assertEquals(Duration.ofHours(1),
conf.getDuration(Property.TSERV_WAL_MAX_AGE));
+ assertEquals(Duration.ofHours(2),
conf.getDuration(Property.GENERAL_RPC_TIMEOUT));
+ }
+
+ @Test
+ public void testDurationSubset() {
+ // test only having some of the duration properties actually set in the
configuration
+ assertEquals(PropertyType.TIMEDURATION,
Property.TSERV_WAL_MAX_AGE.getType());
+ assertEquals(PropertyType.TIMEDURATION,
Property.GENERAL_RPC_TIMEOUT.getType());
+
+ var conf = new ConfigurationCopy();
+ var npe = assertThrows(NullPointerException.class,
+ () -> conf.getDuration(Property.TSERV_WAL_MAX_AGE));
+ assertTrue(npe.getMessage().contains(Property.TSERV_WAL_MAX_AGE.getKey()));
+ npe = assertThrows(NullPointerException.class,
+ () -> conf.getTimeInMillis(Property.TSERV_WAL_MAX_AGE));
+ assertTrue(npe.getMessage().contains(Property.TSERV_WAL_MAX_AGE.getKey()));
+
+ conf.set(Property.TSERV_WAL_MAX_AGE.getKey(), "1h");
+ assertEquals(Duration.ofHours(1),
conf.getDuration(Property.TSERV_WAL_MAX_AGE));
+ assertEquals(Duration.ofHours(1).toMillis(),
conf.getTimeInMillis(Property.TSERV_WAL_MAX_AGE));
+
+ assertThrows(NullPointerException.class, () ->
conf.getDuration(Property.GENERAL_RPC_TIMEOUT));
+ assertThrows(NullPointerException.class,
+ () -> conf.getTimeInMillis(Property.GENERAL_RPC_TIMEOUT));
+
+ assertEquals(Duration.ofHours(1),
conf.getDuration(Property.TSERV_WAL_MAX_AGE));
+ assertEquals(Duration.ofHours(1).toMillis(),
conf.getTimeInMillis(Property.TSERV_WAL_MAX_AGE));
+ }
}