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 <ktur...@apache.org>
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));
+  }
 }

Reply via email to