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

szetszwo 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 0ded78250e7 HDDS-15275. DiskBalancer getIdealUsage handles zero 
capacity incorrectly. (#10267)
0ded78250e7 is described below

commit 0ded78250e770d96ad8ab0907b8cbefe5c77765d
Author: slfan1989 <[email protected]>
AuthorDate: Fri May 15 13:59:12 2026 +0800

    HDDS-15275. DiskBalancer getIdealUsage handles zero capacity incorrectly. 
(#10267)
---
 .../DiskBalancerVolumeCalculation.java             |  35 +++++-
 .../TestDiskBalancerVolumeCalculation.java         | 123 +++++++++++++++++++++
 2 files changed, 153 insertions(+), 5 deletions(-)

diff --git 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerVolumeCalculation.java
 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerVolumeCalculation.java
index 45ac5c4658f..89b00f073e7 100644
--- 
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerVolumeCalculation.java
+++ 
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerVolumeCalculation.java
@@ -67,16 +67,41 @@ public static List<VolumeFixedUsage> 
getVolumeUsages(MutableVolumeSet volumeSet,
    * @param volumes Immutable list of volumes
    * from each source volume during container moves
    * @return Ideal usage as a ratio (used space / total capacity)
-   * @throws IllegalArgumentException if total capacity is zero
    */
   public static double getIdealUsage(List<VolumeFixedUsage> volumes) {
+    if (volumes == null || volumes.isEmpty()) {
+      return 0.0;
+    }
+
     long totalCapacity = 0L, totalEffectiveUsed = 0L;
-    
+
     for (VolumeFixedUsage volumeUsage : volumes) {
-      totalCapacity += volumeUsage.getUsage().getCapacity();
-      totalEffectiveUsed += volumeUsage.getEffectiveUsed();
+      final long capacity = volumeUsage.getUsage().getCapacity();
+      if (capacity < 0) {
+        throw new IllegalArgumentException(
+            "Negative capacity = " + capacity + ": " + 
volumeUsage.getVolume());
+      }
+
+      final long effectiveUsed = volumeUsage.getEffectiveUsed();
+      if (effectiveUsed < 0) {
+        throw new IllegalArgumentException(
+            "Negative effective used = " + effectiveUsed + ": "
+                + volumeUsage.getVolume());
+      }
+      if (effectiveUsed > capacity) {
+        throw new IllegalArgumentException(
+            "Effective used = " + effectiveUsed + " > capacity = "
+                + capacity + ": " + volumeUsage.getVolume());
+      }
+
+      totalCapacity += capacity;
+      totalEffectiveUsed += effectiveUsed;
     }
-    
+
+    if (totalCapacity == 0) {
+      return 0.0;
+    }
+
     return ((double) (totalEffectiveUsed)) / totalCapacity;
   }
   
diff --git 
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerVolumeCalculation.java
 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerVolumeCalculation.java
new file mode 100644
index 00000000000..9ea13503b05
--- /dev/null
+++ 
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerVolumeCalculation.java
@@ -0,0 +1,123 @@
+/*
+ * 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.diskbalancer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.Collections;
+import org.apache.hadoop.hdds.conf.OzoneConfiguration;
+import org.apache.hadoop.hdds.fs.MockSpaceUsageCheckFactory;
+import org.apache.hadoop.hdds.fs.MockSpaceUsageSource;
+import org.apache.hadoop.hdds.fs.SpaceUsageCheckFactory;
+import org.apache.hadoop.hdds.fs.SpaceUsagePersistence;
+import org.apache.hadoop.hdds.fs.SpaceUsageSource;
+import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+/**
+ * Tests for disk balancer volume calculations.
+ */
+class TestDiskBalancerVolumeCalculation {
+
+  @TempDir
+  private Path tempDir;
+
+  @Test
+  void getIdealUsageReturnsZeroForEmptyVolumeList() {
+    assertEquals(0.0, DiskBalancerVolumeCalculation.getIdealUsage(
+        Collections.emptyList()));
+  }
+
+  @Test
+  void getIdealUsageReturnsZeroForZeroTotalCapacity() throws IOException {
+    HddsVolume zeroCapacityVolume = createVolume("zero-capacity", 0, 0);
+
+    assertEquals(0.0, DiskBalancerVolumeCalculation.getIdealUsage(
+        Collections.singletonList(
+            DiskBalancerVolumeCalculation.newVolumeFixedUsage(
+                zeroCapacityVolume, null))));
+  }
+
+  @Test
+  void getIdealUsageRejectsNegativeCapacity() throws IOException {
+    HddsVolume negativeCapacityVolume = createVolume(
+        "negative-capacity", -1, 0);
+
+    IllegalArgumentException exception = assertThrows(
+        IllegalArgumentException.class,
+        () -> DiskBalancerVolumeCalculation.getIdealUsage(
+            Collections.singletonList(
+                DiskBalancerVolumeCalculation.newVolumeFixedUsage(
+                    negativeCapacityVolume, null))));
+
+    assertEquals("Negative capacity = -1: " + negativeCapacityVolume,
+        exception.getMessage());
+  }
+
+  @Test
+  void getIdealUsageRejectsNegativeEffectiveUsed() throws IOException {
+    HddsVolume volume = createVolume("negative-effective-used", 100, 100);
+    DiskBalancerVolumeCalculation.VolumeFixedUsage volumeUsage =
+        DiskBalancerVolumeCalculation.newVolumeFixedUsage(
+            volume, Collections.singletonMap(volume, -1L));
+
+    IllegalArgumentException exception = assertThrows(
+        IllegalArgumentException.class,
+        () -> DiskBalancerVolumeCalculation.getIdealUsage(
+            Collections.singletonList(volumeUsage)));
+
+    assertEquals("Negative effective used = " + volumeUsage.getEffectiveUsed()
+        + ": " + volume, exception.getMessage());
+  }
+
+  @Test
+  void getIdealUsageRejectsEffectiveUsedGreaterThanCapacity()
+      throws IOException {
+    HddsVolume volume = createVolume("effective-used-exceeds-capacity", 100, 
0);
+    DiskBalancerVolumeCalculation.VolumeFixedUsage volumeUsage =
+        DiskBalancerVolumeCalculation.newVolumeFixedUsage(
+            volume, Collections.singletonMap(volume, 1L));
+
+    IllegalArgumentException exception = assertThrows(
+        IllegalArgumentException.class,
+        () -> DiskBalancerVolumeCalculation.getIdealUsage(
+            Collections.singletonList(volumeUsage)));
+
+    assertEquals("Effective used = " + volumeUsage.getEffectiveUsed()
+        + " > capacity = " + volumeUsage.getUsage().getCapacity() + ": "
+        + volume, exception.getMessage());
+  }
+
+  private HddsVolume createVolume(String name, long capacity, long available)
+      throws IOException {
+    OzoneConfiguration conf = new OzoneConfiguration();
+    SpaceUsageSource source = MockSpaceUsageSource.fixed(capacity, available);
+    SpaceUsageCheckFactory factory = MockSpaceUsageCheckFactory.of(
+        source, Duration.ZERO, SpaceUsagePersistence.None.INSTANCE);
+
+    return new HddsVolume.Builder(tempDir.resolve(name).toString())
+        .conf(conf)
+        .usageCheckFactory(factory)
+        .build();
+  }
+}


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

Reply via email to