This is an automated email from the ASF dual-hosted git repository.

ritesh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new 5d87d0eb75 HDDS-6901. Configure HDDS volume reserved as percentage of 
the volume space. (#3532)
5d87d0eb75 is described below

commit 5d87d0eb75c3e8b42993f7687244f8ec488878ee
Author: Aswin Shakil Balasubramanian <[email protected]>
AuthorDate: Mon Jul 11 11:33:59 2022 -0700

    HDDS-6901. Configure HDDS volume reserved as percentage of the volume 
space. (#3532)
    
    * HDDS-6901. Configure HDDS volume reserved as a percentage of the volume 
space.
    
    Co-authored-by: Aswin Shakil Balasubramanian <[email protected]>
---
 .../org/apache/hadoop/hdds/scm/ScmConfigKeys.java  |   3 +
 .../common/src/main/resources/ozone-default.xml    |   8 ++
 .../hadoop/hdds/conf/ConfigurationSource.java      |   8 ++
 .../ozone/container/common/volume/VolumeInfo.java  |  33 ++++-
 .../ozone/container/common/volume/VolumeUsage.java |   9 +-
 .../common/volume/TestReservedVolumeSpace.java     | 149 +++++++++++++++++++++
 6 files changed, 204 insertions(+), 6 deletions(-)

diff --git 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
index 3259b5b137..49789f4d70 100644
--- 
a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
+++ 
b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/ScmConfigKeys.java
@@ -214,6 +214,9 @@ public final class ScmConfigKeys {
   public static final String HDDS_DATANODE_DIR_KEY = "hdds.datanode.dir";
   public static final String HDDS_DATANODE_DIR_DU_RESERVED =
       "hdds.datanode.dir.du.reserved";
+  public static final String HDDS_DATANODE_DIR_DU_RESERVED_PERCENT =
+      "hdds.datanode.dir.du.reserved.percent";
+  public static final float HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT = 0;
   public static final String HDDS_REST_CSRF_ENABLED_KEY =
       "hdds.rest.rest-csrf.enabled";
   public static final boolean HDDS_REST_CSRF_ENABLED_DEFAULT = false;
diff --git a/hadoop-hdds/common/src/main/resources/ozone-default.xml 
b/hadoop-hdds/common/src/main/resources/ozone-default.xml
index d1ee50af21..9fa3dd3ab8 100644
--- a/hadoop-hdds/common/src/main/resources/ozone-default.xml
+++ b/hadoop-hdds/common/src/main/resources/ozone-default.xml
@@ -166,6 +166,14 @@
        Such as /dir1:100B, /dir2:200MB, means dir1 reserves 100 bytes and dir2 
reserves 200 MB.
     </description>
   </property>
+  <property>
+    <name>hdds.datanode.dir.du.reserved.percent</name>
+    <value/>
+    <tag>OZONE, CONTAINER, STORAGE, MANAGEMENT</tag>
+    <description>Percentage of volume that should be reserved. This space is 
left free for other usage.
+      The value should be between 0-1. Such as 0.1 which means 10% of volume 
space will be reserved.
+    </description>
+  </property>
   <property>
     <name>hdds.datanode.volume.choosing.policy</name>
     <value/>
diff --git 
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
 
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
index 22c74ff325..8c4ee7cd2f 100644
--- 
a/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
+++ 
b/hadoop-hdds/config/src/main/java/org/apache/hadoop/hdds/conf/ConfigurationSource.java
@@ -143,6 +143,14 @@ public interface ConfigurationSource {
     return configMap;
   }
 
+  /**
+   * Checks if the property <value> is set.
+   * @param key The property name.
+   * @return true if the value is set else false.
+   */
+  default boolean isConfigured(String key) {
+    return get(key) != null;
+  }
   /**
    * Create a Configuration object and inject the required configuration 
values.
    *
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeInfo.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeInfo.java
index 71063e23fc..6773b6ff64 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeInfo.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeInfo.java
@@ -33,6 +33,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED;
+import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT;
+import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT;
 
 /**
  * Stores information about a disk/volume.
@@ -134,6 +136,16 @@ public final class VolumeInfo {
   }
 
   private long getReserved(ConfigurationSource conf) {
+    if (conf.isConfigured(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT)
+        && conf.isConfigured(HDDS_DATANODE_DIR_DU_RESERVED)) {
+      LOG.error("Both {} and {} are set. Set either one, not both. If the " +
+          "volume matches with volume parameter in former config, it is set " +
+          "as reserved space. If not it fall backs to the latter config.",
+          HDDS_DATANODE_DIR_DU_RESERVED, 
HDDS_DATANODE_DIR_DU_RESERVED_PERCENT);
+    }
+
+    // 1. If hdds.datanode.dir.du.reserved is set for a volume then make it
+    // as the reserved bytes.
     Collection<String> reserveList = conf.getTrimmedStringCollection(
         HDDS_DATANODE_DIR_DU_RESERVED);
     for (String reserve : reserveList) {
@@ -149,12 +161,26 @@ public final class VolumeInfo {
           StorageSize size = StorageSize.parse(words[1].trim());
           return (long) size.getUnit().toBytes(size.getValue());
         } catch (Exception e) {
-          LOG.error("Failed to parse StorageSize:{}", words[1].trim(), e);
-          return 0;
+          LOG.error("Failed to parse StorageSize: {}", words[1].trim(), e);
+          break;
         }
       }
     }
 
+    // 2. If hdds.datanode.dir.du.reserved not set and
+    // hdds.datanode.dir.du.reserved.percent is set, fall back to this config.
+    if (conf.isConfigured(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT)) {
+      float percentage = conf.getFloat(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT,
+          HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT);
+      if (0 <= percentage && percentage <= 1) {
+        return (long) Math.ceil(this.usage.getCapacity() * percentage);
+      }
+      //If it comes here then the percentage is not between 0-1.
+      LOG.error("The value of {} should be between 0 to 1. Defaulting to 0.",
+          HDDS_DATANODE_DIR_DU_RESERVED_PERCENT);
+    }
+
+    //Both configs are not set, return 0.
     return 0;
   }
 
@@ -183,8 +209,9 @@ public final class VolumeInfo {
     SpaceUsageCheckParams checkParams =
         usageCheckFactory.paramsFor(root);
 
+    this.usage = new VolumeUsage(checkParams);
     this.reservedInBytes = getReserved(b.conf);
-    this.usage = new VolumeUsage(checkParams, reservedInBytes);
+    this.usage.setReserved(reservedInBytes);
   }
 
   public long getCapacity() {
diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeUsage.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeUsage.java
index fe12d4be9c..2d21c8f8ad 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeUsage.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeUsage.java
@@ -30,10 +30,9 @@ public class VolumeUsage implements SpaceUsageSource {
 
   private final CachingSpaceUsageSource source;
   private boolean shutdownComplete;
-  private final long reservedInBytes;
+  private long reservedInBytes;
 
-  VolumeUsage(SpaceUsageCheckParams checkParams, long reservedInBytes) {
-    this.reservedInBytes = reservedInBytes;
+  VolumeUsage(SpaceUsageCheckParams checkParams) {
     source = new CachingSpaceUsageSource(checkParams);
     start(); // TODO should start only on demand
   }
@@ -98,4 +97,8 @@ public class VolumeUsage implements SpaceUsageSource {
   public void refreshNow() {
     source.refreshNow();
   }
+
+  public void setReserved(long reserved) {
+    this.reservedInBytes = reserved;
+  }
 }
diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/volume/TestReservedVolumeSpace.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/volume/TestReservedVolumeSpace.java
new file mode 100644
index 0000000000..471a9a750f
--- /dev/null
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/common/volume/TestReservedVolumeSpace.java
@@ -0,0 +1,149 @@
+/**
+ * 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.hadoop.ozone.container.common.volume;
+
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.fs.MockSpaceUsageCheckFactory;
+import org.apache.hadoop.hdds.scm.ScmConfigKeys;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.UUID;
+
+import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT;
+import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT;
+
+/**
+ * To test the reserved volume space.
+ */
+public class TestReservedVolumeSpace {
+
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+  @Rule
+  public TemporaryFolder temp = new TemporaryFolder();
+  private static final String DATANODE_UUID = UUID.randomUUID().toString();
+  private HddsVolume.Builder volumeBuilder;
+
+  @Before
+  public void setup() throws Exception {
+    volumeBuilder = new HddsVolume.Builder(folder.getRoot().getPath())
+        .datanodeUuid(DATANODE_UUID)
+        .usageCheckFactory(MockSpaceUsageCheckFactory.NONE);
+  }
+
+  /**
+   * Test reserved capacity with respect to the percentage of actual capacity.
+   * @throws Exception
+   */
+  @Test
+  public void testVolumeCapacityAfterReserve() throws Exception {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "0.3");
+    HddsVolume hddsVolume = volumeBuilder.conf(conf).build();
+    //Reserving
+    float percentage = conf.getFloat(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT,
+        HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT);
+
+    long volumeCapacity = hddsVolume.getCapacity();
+    //Gets the actual total capacity
+    long totalCapacity = hddsVolume.getVolumeInfo()
+        .getUsageForTesting().getCapacity();
+    long reservedCapacity = hddsVolume.getVolumeInfo().getReservedInBytes();
+    //Volume Capacity with Reserved
+    long volumeCapacityReserved = totalCapacity - reservedCapacity;
+
+    long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
+    long reservedCalculated = (long) Math.ceil(totalCapacity * percentage);
+
+    Assert.assertEquals(reservedFromVolume, reservedCalculated);
+    Assert.assertEquals(volumeCapacity, volumeCapacityReserved);
+  }
+
+  /**
+   * When both configs are set, hdds.datanode.dir.du.reserved is set
+   * if the volume matches with volume in config parameter.
+   * @throws Exception
+   */
+  @Test
+  public void testReservedWhenBothConfigSet() throws Exception {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "0.3");
+    conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED,
+        folder.getRoot() + ":500B");
+    HddsVolume hddsVolume = volumeBuilder.conf(conf).build();
+
+    long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
+    Assert.assertEquals(reservedFromVolume, 500);
+  }
+
+  @Test
+  public void testReservedToZeroWhenBothConfigNotSet() throws Exception {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    HddsVolume hddsVolume = volumeBuilder.conf(conf).build();
+
+    long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
+    Assert.assertEquals(reservedFromVolume, 0);
+  }
+
+  @Test
+  public void testFallbackToPercentConfig() throws Exception {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    conf.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "0.3");
+    //Setting config for different volume, hence fallback happens
+    conf.set(ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED,
+        temp.getRoot() + ":500B");
+    HddsVolume hddsVolume = volumeBuilder.conf(conf).build();
+
+    long reservedFromVolume = hddsVolume.getVolumeInfo().getReservedInBytes();
+    Assert.assertNotEquals(reservedFromVolume, 0);
+
+    long totalCapacity = hddsVolume.getVolumeInfo()
+        .getUsageForTesting().getCapacity();
+    float percentage = conf.getFloat(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT,
+        HDDS_DATANODE_DIR_DU_RESERVED_PERCENT_DEFAULT);
+    long reservedCalculated = (long) Math.ceil(totalCapacity * percentage);
+    Assert.assertEquals(reservedFromVolume, reservedCalculated);
+  }
+
+  @Test
+  public void testInvalidConfig() throws Exception {
+    OzoneConfiguration conf1 = new OzoneConfiguration();
+
+    // 500C doesn't match with any Storage Unit
+    conf1.set(ScmConfigKeys.HDDS_DATANODE_DIR_DU_RESERVED,
+        folder.getRoot() + ":500C");
+    HddsVolume hddsVolume1 = volumeBuilder.conf(conf1).build();
+
+    long reservedFromVolume1 = 
hddsVolume1.getVolumeInfo().getReservedInBytes();
+    Assert.assertEquals(reservedFromVolume1, 0);
+
+    OzoneConfiguration conf2 = new OzoneConfiguration();
+
+    //Should be between 0-1.
+    conf2.set(HDDS_DATANODE_DIR_DU_RESERVED_PERCENT, "20");
+    HddsVolume hddsVolume2 = volumeBuilder.conf(conf2).build();
+
+    long reservedFromVolume2 = 
hddsVolume2.getVolumeInfo().getReservedInBytes();
+    Assert.assertEquals(reservedFromVolume2, 0);
+  }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to