This is an automated email from the ASF dual-hosted git repository.
sammichen pushed a commit to branch HDDS-5713
in repository https://gitbox.apache.org/repos/asf/ozone.git
The following commit(s) were added to refs/heads/HDDS-5713 by this push:
new 2abd75118ed HDDS-14175. DiskBalancer should not call getCurrentUsage()
multiple times. (#9505)
2abd75118ed is described below
commit 2abd75118ed0232d9c96f5fa482760ae7e8a1696
Author: Tsz-Wo Nicholas Sze <[email protected]>
AuthorDate: Thu Dec 18 19:15:01 2025 -0800
HDDS-14175. DiskBalancer should not call getCurrentUsage() multiple times.
(#9505)
---
.../ozone/container/common/volume/VolumeUsage.java | 5 +-
.../diskbalancer/DiskBalancerService.java | 38 ++++----
.../DiskBalancerVolumeCalculation.java | 106 ++++++++++++++-------
.../policy/ContainerChoosingPolicy.java | 4 +-
.../policy/DefaultContainerChoosingPolicy.java | 65 ++++---------
.../policy/DefaultVolumeChoosingPolicy.java | 84 ++++++----------
.../policy/DiskBalancerVolumeChoosingPolicy.java | 4 +-
.../diskbalancer/TestDiskBalancerService.java | 11 ++-
.../scm/node/TestContainerChoosingPolicy.java | 1 -
.../ozone/scm/node/TestVolumeChoosingPolicy.java | 2 +-
10 files changed, 154 insertions(+), 166 deletions(-)
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 907ec611245..15275fcd6c5 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
@@ -175,9 +175,8 @@ public long getReservedInBytes() {
return reservedInBytes;
}
- private static long getUsableSpace(
- long available, long committed, long minFreeSpace) {
- return available - committed - minFreeSpace;
+ public static long getUsableSpace(long available, long committed, long
spared) {
+ return available - committed - spared;
}
public static long getUsableSpace(StorageReportProto report) {
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java
index 37984848e9f..7490c5bb3ea 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java
@@ -18,16 +18,19 @@
package org.apache.hadoop.ozone.container.diskbalancer;
import static
org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.CONTAINER_ALREADY_EXISTS;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.calculateVolumeDataDensity;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getIdealUsage;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getVolumeUsages;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -65,6 +68,7 @@
import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
import org.apache.hadoop.ozone.container.common.volume.StorageVolume;
import
org.apache.hadoop.ozone.container.common.volume.VolumeChoosingPolicyFactory;
+import
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.VolumeFixedUsage;
import
org.apache.hadoop.ozone.container.diskbalancer.policy.ContainerChoosingPolicy;
import
org.apache.hadoop.ozone.container.diskbalancer.policy.DiskBalancerVolumeChoosingPolicy;
import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainerData;
@@ -632,54 +636,46 @@ private boolean tryCleanupOnePendingDeletionContainer() {
}
public DiskBalancerInfo getDiskBalancerInfo() {
- ImmutableList<HddsVolume> immutableVolumeSet =
DiskBalancerVolumeCalculation.getImmutableVolumeSet(volumeSet);
+ final List<VolumeFixedUsage> volumeUsages = getVolumeUsages(volumeSet,
deltaSizes);
// Calculate volumeDataDensity
- double volumeDatadensity = 0.0;
- volumeDatadensity =
DiskBalancerVolumeCalculation.calculateVolumeDataDensity(immutableVolumeSet,
deltaSizes);
+ final double volumeDataDensity = calculateVolumeDataDensity(volumeUsages);
long bytesToMove = 0;
if (this.operationalState == DiskBalancerRunningStatus.RUNNING) {
// this calculates live changes in bytesToMove
// calculate bytes to move if the balancer is in a running state, else 0.
- bytesToMove = calculateBytesToMove(immutableVolumeSet);
+ bytesToMove = calculateBytesToMove(volumeUsages);
}
return new DiskBalancerInfo(operationalState, threshold, bandwidthInMB,
parallelThread, stopAfterDiskEven, version, metrics.getSuccessCount(),
- metrics.getFailureCount(), bytesToMove, metrics.getSuccessBytes(),
volumeDatadensity);
+ metrics.getFailureCount(), bytesToMove, metrics.getSuccessBytes(),
volumeDataDensity);
}
- public long calculateBytesToMove(ImmutableList<HddsVolume> inputVolumeSet) {
+ public long calculateBytesToMove(List<VolumeFixedUsage> inputVolumeSet) {
// If there are no available volumes or only one volume, return 0 bytes to
move
if (inputVolumeSet.isEmpty() || inputVolumeSet.size() < 2) {
return 0;
}
- // Calculate ideal usage
- double idealUsage =
DiskBalancerVolumeCalculation.getIdealUsage(inputVolumeSet, deltaSizes);
- double normalizedThreshold = threshold / 100.0;
+ // Calculate actual threshold
+ final double actualThreshold = getIdealUsage(inputVolumeSet) + threshold /
100.0;
long totalBytesToMove = 0;
// Calculate excess data in overused volumes
- for (HddsVolume volume : inputVolumeSet) {
- SpaceUsageSource usage = volume.getCurrentUsage();
-
+ for (VolumeFixedUsage volumeUsage : inputVolumeSet) {
+ final SpaceUsageSource.Fixed usage = volumeUsage.getUsage();
if (usage.getCapacity() == 0) {
continue;
}
- long deltaSize = deltaSizes.getOrDefault(volume, 0L);
- double currentUsage = (double)((usage.getCapacity() -
usage.getAvailable())
- + deltaSize + volume.getCommittedBytes()) / usage.getCapacity();
-
- double volumeUtilisation = currentUsage - idealUsage;
-
+ final double excess = volumeUsage.getUtilization() - actualThreshold;
// Only consider volumes that exceed the threshold (source volumes)
- if (volumeUtilisation >= normalizedThreshold) {
+ if (excess > 0) {
// Calculate excess bytes that need to be moved from this volume
- long excessBytes = (long) ((volumeUtilisation - normalizedThreshold) *
usage.getCapacity());
+ final long excessBytes = (long) (excess * usage.getCapacity());
totalBytesToMove += Math.max(0, excessBytes);
}
}
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 442cdcface4..45ac5c4658f 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
@@ -17,14 +17,18 @@
package org.apache.hadoop.ozone.container.diskbalancer;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
+import static org.apache.ratis.util.Preconditions.assertInstanceOf;
+import static org.apache.ratis.util.Preconditions.assertTrue;
+
import java.util.List;
import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
import org.apache.hadoop.hdds.fs.SpaceUsageSource;
-import org.apache.hadoop.ozone.container.common.utils.StorageVolumeUtil;
import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
+import org.apache.hadoop.ozone.container.common.volume.StorageVolume;
+import org.apache.hadoop.ozone.container.common.volume.VolumeUsage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,39 +51,32 @@ private DiskBalancerVolumeCalculation() {
/**
* Get an immutable snapshot of volumes from a MutableVolumeSet.
- *
+ *
* @param volumeSet The MutableVolumeSet to create a snapshot from
- * @return Immutable list of HddsVolume objects
+ * @return a list of volumes and usages
*/
- public static ImmutableList<HddsVolume>
getImmutableVolumeSet(MutableVolumeSet volumeSet) {
- // Create an immutable copy of the volume list at this point in time
- List<HddsVolume> volumes =
StorageVolumeUtil.getHddsVolumesList(volumeSet.getVolumesList());
- return ImmutableList.copyOf(volumes);
+ public static List<VolumeFixedUsage> getVolumeUsages(MutableVolumeSet
volumeSet, Map<HddsVolume, Long> deltas) {
+ return volumeSet.getVolumesList().stream()
+ .map(v -> newVolumeFixedUsage(v, deltas))
+ .collect(Collectors.toList());
}
/**
* Get ideal usage from an immutable list of volumes.
*
* @param volumes Immutable list of volumes
- * @param deltaMap A map that tracks the total bytes which will be freed
* 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(ImmutableList<HddsVolume> volumes,
- Map<HddsVolume, Long> deltaMap) {
+ public static double getIdealUsage(List<VolumeFixedUsage> volumes) {
long totalCapacity = 0L, totalEffectiveUsed = 0L;
- for (HddsVolume volume : volumes) {
- SpaceUsageSource usage = volume.getCurrentUsage();
- totalCapacity += usage.getCapacity();
- long currentUsed = usage.getCapacity() - usage.getAvailable();
- long delta = (deltaMap != null) ? deltaMap.getOrDefault(volume, 0L) : 0L;
- long committed = volume.getCommittedBytes();
- totalEffectiveUsed += (currentUsed + delta + committed);
+ for (VolumeFixedUsage volumeUsage : volumes) {
+ totalCapacity += volumeUsage.getUsage().getCapacity();
+ totalEffectiveUsed += volumeUsage.getEffectiveUsed();
}
- Preconditions.checkArgument(totalCapacity != 0);
return ((double) (totalEffectiveUsed)) / totalCapacity;
}
@@ -87,11 +84,9 @@ public static double getIdealUsage(ImmutableList<HddsVolume>
volumes,
* Calculate VolumeDataDensity.
*
* @param volumeSet The MutableVolumeSet containing all volumes
- * @param deltaMap Map of volume to delta sizes (ongoing operations), can be
null
* @return VolumeDataDensity sum across all volumes
*/
- public static double calculateVolumeDataDensity(ImmutableList<HddsVolume>
volumeSet,
- Map<HddsVolume, Long> deltaMap) {
+ public static double calculateVolumeDataDensity(List<VolumeFixedUsage>
volumeSet) {
if (volumeSet == null) {
LOG.warn("VolumeSet is null, returning 0.0 for VolumeDataDensity");
return 0.0;
@@ -104,18 +99,13 @@ public static double
calculateVolumeDataDensity(ImmutableList<HddsVolume> volume
}
// Calculate ideal usage using the same immutable volume snapshot
- double idealUsage = getIdealUsage(volumeSet, deltaMap);
+ final double idealUsage = getIdealUsage(volumeSet);
double volumeDensitySum = 0.0;
// Calculate density for each volume using the same snapshot
- for (HddsVolume volume : volumeSet) {
- SpaceUsageSource usage = volume.getCurrentUsage();
- Preconditions.checkArgument(usage.getCapacity() != 0);
-
- long deltaSize = (deltaMap != null) ? deltaMap.getOrDefault(volume,
0L) : 0L;
- double currentUsage = (double)((usage.getCapacity() -
usage.getAvailable())
- + deltaSize + volume.getCommittedBytes()) / usage.getCapacity();
-
+ for (VolumeFixedUsage volumeUsage : volumeSet) {
+ final double currentUsage = volumeUsage.getUtilization();
+
// Calculate density as absolute difference from ideal usage
double volumeDensity = Math.abs(currentUsage - idealUsage);
volumeDensitySum += volumeDensity;
@@ -126,4 +116,56 @@ public static double
calculateVolumeDataDensity(ImmutableList<HddsVolume> volume
return -1.0;
}
}
+
+ public static double computeUtilization(SpaceUsageSource.Fixed usage, long
committed, long required) {
+ final long capacity = usage.getCapacity();
+ assertTrue(capacity > 0, () -> "capacity = " + capacity + " <= 0");
+ return computeEffectiveUsage(usage, committed, required) / (double)
capacity;
+ }
+
+ private static long computeEffectiveUsage(SpaceUsageSource.Fixed usage, long
committed, long required) {
+ return usage.getCapacity() - usage.getAvailable() + committed + required;
+ }
+
+ public static VolumeFixedUsage newVolumeFixedUsage(StorageVolume volume,
Map<HddsVolume, Long> deltaMap) {
+ final HddsVolume v = assertInstanceOf(volume, HddsVolume.class);
+ final long delta = deltaMap == null ? 0 : deltaMap.getOrDefault(v, 0L);
+ return new VolumeFixedUsage(v, delta);
+ }
+
+ /** {@link HddsVolume} with a {@link SpaceUsageSource.Fixed} usage. */
+ public static final class VolumeFixedUsage {
+ private final HddsVolume volume;
+ private final SpaceUsageSource.Fixed usage;
+ private final long effectiveUsed;
+ private final Double utilization;
+
+ private VolumeFixedUsage(HddsVolume volume, long delta) {
+ this.volume = volume;
+ this.usage = volume.getCurrentUsage();
+ this.effectiveUsed = computeEffectiveUsage(usage,
volume.getCommittedBytes(), delta);
+ this.utilization = usage.getCapacity() > 0 ? computeUtilization(usage,
volume.getCommittedBytes(), delta) : null;
+ }
+
+ public HddsVolume getVolume() {
+ return volume;
+ }
+
+ public SpaceUsageSource.Fixed getUsage() {
+ return usage;
+ }
+
+ public long getEffectiveUsed() {
+ return effectiveUsed;
+ }
+
+ public double getUtilization() {
+ return Objects.requireNonNull(utilization, "utilization == null");
+ }
+
+ public long computeUsableSpace() {
+ final long spared = volume.getFreeSpaceToSpare(usage.getCapacity());
+ return VolumeUsage.getUsableSpace(usage.getAvailable(),
volume.getCommittedBytes(), spared);
+ }
+ }
}
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/ContainerChoosingPolicy.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/ContainerChoosingPolicy.java
index da52f4fcd53..c6bcc1ff50f 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/ContainerChoosingPolicy.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/ContainerChoosingPolicy.java
@@ -36,7 +36,7 @@ public interface ContainerChoosingPolicy {
* @param destVolume the destination volume to which container is being
moved.
* @param inProgressContainerIDs containerIDs present in this set should be
- avoided as these containers are already under move by diskBalancer.
- * @param threshold the threshold value
+ * @param thresholdPercentage the threshold percentage in range [0, 100]
* @param volumeSet the volumeSet instance
* @param deltaMap the deltaMap instance of source volume
* @return a Container
@@ -44,6 +44,6 @@ public interface ContainerChoosingPolicy {
ContainerData chooseContainer(OzoneContainer ozoneContainer,
HddsVolume srcVolume, HddsVolume destVolume,
Set<ContainerID> inProgressContainerIDs,
- Double threshold, MutableVolumeSet volumeSet,
+ double thresholdPercentage, MutableVolumeSet volumeSet,
Map<HddsVolume, Long> deltaMap);
}
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultContainerChoosingPolicy.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultContainerChoosingPolicy.java
index c759271f881..11728beb943 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultContainerChoosingPolicy.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultContainerChoosingPolicy.java
@@ -18,12 +18,15 @@
package org.apache.hadoop.ozone.container.diskbalancer.policy;
import static java.util.concurrent.TimeUnit.HOURS;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.computeUtilization;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getIdealUsage;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getVolumeUsages;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
-import com.google.common.collect.ImmutableList;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -33,7 +36,7 @@
import org.apache.hadoop.ozone.container.common.interfaces.Container;
import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
-import
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation;
+import
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.VolumeFixedUsage;
import org.apache.hadoop.ozone.container.ozoneimpl.OzoneContainer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,72 +57,42 @@ public class DefaultContainerChoosingPolicy implements
ContainerChoosingPolicy {
@Override
public ContainerData chooseContainer(OzoneContainer ozoneContainer,
- HddsVolume srcVolume, HddsVolume destVolume,
+ HddsVolume src, HddsVolume dst,
Set<ContainerID> inProgressContainerIDs,
- Double threshold, MutableVolumeSet volumeSet,
+ double thresholdPercentage, MutableVolumeSet volumeSet,
Map<HddsVolume, Long> deltaMap) {
- Iterator<Container<?>> itr;
+ final Iterator<Container<?>> itr;
try {
- itr = CACHE.get().get(srcVolume,
- () -> ozoneContainer.getController().getContainers(srcVolume));
+ itr = CACHE.get().get(src, () ->
ozoneContainer.getController().getContainers(src));
} catch (ExecutionException e) {
- LOG.warn("Failed to get container iterator for volume {}", srcVolume, e);
+ LOG.warn("Failed to get container iterator for volume {}", src, e);
return null;
}
- // Calculate maxAllowedUtilization
- ImmutableList<HddsVolume> immutableVolumeSet =
DiskBalancerVolumeCalculation.getImmutableVolumeSet(volumeSet);
- double idealUsage =
DiskBalancerVolumeCalculation.getIdealUsage(immutableVolumeSet, deltaMap);
- double maxAllowedUtilization = idealUsage + (threshold / 100.0);
+ // Calculate the actual threshold
+ final List<VolumeFixedUsage> volumeUsages = getVolumeUsages(volumeSet,
deltaMap);
+ final double actualThreshold = getIdealUsage(volumeUsages) +
thresholdPercentage / 100.0;
+ // Find container
+ final SpaceUsageSource.Fixed dstUsage = dst.getCurrentUsage();
+ final long dstCommittedBytes = dst.getCommittedBytes();
while (itr.hasNext()) {
ContainerData containerData = itr.next().getContainerData();
if (!inProgressContainerIDs.contains(
ContainerID.valueOf(containerData.getContainerID())) &&
(containerData.isClosed() || (test &&
containerData.isQuasiClosed()))) {
- // This is a candidate container. Now, check if moving it would be
productive.
- if (isMoveProductive(containerData, destVolume,
maxAllowedUtilization)) {
+ // Check if dst can accept the candidate container.
+ if (computeUtilization(dstUsage, dstCommittedBytes,
containerData.getBytesUsed()) < actualThreshold) {
return containerData;
}
}
}
- if (!itr.hasNext()) {
- CACHE.get().invalidate(srcVolume);
- }
+ CACHE.get().invalidate(src);
return null;
}
- /**
- * Checks if moving the given container from source to destination would
- * result in the destination's utilization being less than or equal to the
- * averageUtilization + threshold. This prevents "thrashing" where a move
- * immediately makes the destination a candidate for a source volume.
- *
- * @param containerData The container to be moved.
- * @param destVolume The destination volume.
- * @param maxAllowedUtilization The maximum allowed utilization
- * for the destination volume.
- * @return true if the move is productive, false otherwise.
- */
- private boolean isMoveProductive(ContainerData containerData, HddsVolume
destVolume,
- Double maxAllowedUtilization) {
- long containerSize = containerData.getBytesUsed();
- SpaceUsageSource usage = destVolume.getCurrentUsage();
-
- double newDestUtilization =
- (double) ((usage.getCapacity() - usage.getAvailable()) +
destVolume.getCommittedBytes() + containerSize)
- / usage.getCapacity();
-
- if (newDestUtilization <= maxAllowedUtilization) {
- // The move is productive.
- return true;
- }
-
- return false;
- }
-
@VisibleForTesting
public static void setTest(boolean isTest) {
test = isTest;
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultVolumeChoosingPolicy.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultVolumeChoosingPolicy.java
index 1dee4b57d43..c994af442cd 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultVolumeChoosingPolicy.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DefaultVolumeChoosingPolicy.java
@@ -17,17 +17,19 @@
package org.apache.hadoop.ozone.container.diskbalancer.policy;
-import com.google.common.collect.ImmutableList;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getIdealUsage;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.newVolumeFixedUsage;
+
+import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.hadoop.hdds.fs.SpaceUsageSource;
-import org.apache.hadoop.ozone.container.common.volume.AvailableSpaceFilter;
import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
-import
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation;
+import org.apache.hadoop.ozone.container.common.volume.StorageVolume;
+import
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.VolumeFixedUsage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -50,67 +52,43 @@ public DefaultVolumeChoosingPolicy(ReentrantLock
globalLock) {
@Override
public Pair<HddsVolume, HddsVolume> chooseVolume(MutableVolumeSet volumeSet,
- double threshold, Map<HddsVolume, Long> deltaMap, long containerSize) {
+ double thresholdPercentage, Map<HddsVolume, Long> deltaMap, long
containerSize) {
lock.lock();
try {
// Create truly immutable snapshot of volumes to ensure consistency
- ImmutableList<HddsVolume> allVolumes =
DiskBalancerVolumeCalculation.getImmutableVolumeSet(volumeSet);
-
+ final List<StorageVolume> allVolumes = volumeSet.getVolumesList();
if (allVolumes.size() < 2) {
return null; // Can't balance with less than 2 volumes.
}
-
- // Calculate ideal usage using the same immutable volume
- double idealUsage =
DiskBalancerVolumeCalculation.getIdealUsage(allVolumes, deltaMap);
- // Threshold is given as a percentage
- double normalizedThreshold = threshold / 100;
- List<HddsVolume> volumes = allVolumes
- .stream()
- .filter(volume -> {
- SpaceUsageSource usage = volume.getCurrentUsage();
+ // Calculate usages and sort in ascending order of utilization
+ final List<VolumeFixedUsage> volumeUsages = allVolumes.stream()
+ .map(v -> newVolumeFixedUsage(v, deltaMap))
+ .sorted(Comparator.comparingDouble(VolumeFixedUsage::getUtilization))
+ .collect(Collectors.toList());
- return Math.abs(
- ((double)((usage.getCapacity() - usage.getAvailable())
- + deltaMap.getOrDefault(volume, 0L) +
volume.getCommittedBytes()))
- / usage.getCapacity() - idealUsage) >=
normalizedThreshold;
-
- }).sorted((v1, v2) -> {
- SpaceUsageSource usage1 = v1.getCurrentUsage();
- SpaceUsageSource usage2 = v2.getCurrentUsage();
+ // Calculate the actual threshold and check src
+ final double actualThreshold = getIdealUsage(volumeUsages) +
thresholdPercentage / 100;
+ final VolumeFixedUsage src = volumeUsages.get(volumeUsages.size() - 1);
+ if (src.getUtilization() < actualThreshold) {
+ return null; // all volumes are under the threshold
+ }
- return Double.compare(
- (double) ((usage2.getCapacity() - usage2.getAvailable())
- + deltaMap.getOrDefault(v2, 0L) +
v2.getCommittedBytes()) /
- usage2.getCapacity(),
- (double) ((usage1.getCapacity() - usage1.getAvailable())
- + deltaMap.getOrDefault(v1, 0L) +
v1.getCommittedBytes()) /
- usage1.getCapacity());
- }).collect(Collectors.toList());
+ // Find dst
+ for (int i = 0; i < volumeUsages.size() - 1; i++) {
+ final VolumeFixedUsage dstUsage = volumeUsages.get(i);
+ final HddsVolume dst = dstUsage.getVolume();
- // Can not generate DiskBalancerTask if we have less than 2 results
- if (volumes.size() <= 1) {
- LOG.debug("Can not find appropriate Source volume and Dest Volume.");
- return null;
- }
- AvailableSpaceFilter filter = new AvailableSpaceFilter(containerSize);
- HddsVolume srcVolume = volumes.get(0);
- HddsVolume destVolume = volumes.get(volumes.size() - 1);
- while (!filter.test(destVolume)) {
- // If the destination volume does not have enough space, try the next
- // one in the list.
- LOG.debug("Destination volume {} does not have enough space, trying
next volume.",
- destVolume.getStorageID());
- volumes.remove(destVolume);
- if (volumes.size() <= 1) {
- LOG.debug("Can not find appropriate Source volume and Dest Volume.");
- return null;
+ if (containerSize < dstUsage.computeUsableSpace()) {
+ // Found dst, reserve space and return
+ dst.incCommittedBytes(containerSize);
+ return Pair.of(src.getVolume(), dst);
}
- destVolume = volumes.get(volumes.size() - 1);
+ LOG.debug("Destination volume {} does not have enough space, trying
next volume.",
+ dst.getStorageID());
}
- // reserve space for the dest volume
- destVolume.incCommittedBytes(containerSize);
- return Pair.of(srcVolume, destVolume);
+ LOG.debug("Failed to find appropriate destination volume.");
+ return null;
} finally {
lock.unlock();
}
diff --git
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DiskBalancerVolumeChoosingPolicy.java
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DiskBalancerVolumeChoosingPolicy.java
index 01520fea37b..043aa83f5f9 100644
---
a/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DiskBalancerVolumeChoosingPolicy.java
+++
b/hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/DiskBalancerVolumeChoosingPolicy.java
@@ -30,11 +30,11 @@ public interface DiskBalancerVolumeChoosingPolicy {
* Choose a pair of volumes for balancing.
*
* @param volumeSet - volumes to choose from.
- * @param threshold - the threshold to choose source and dest volumes.
+ * @param thresholdPercentage the threshold percentage in range [0, 100] to
choose the source volume.
* @param deltaSizes - the sizes changes of inProgress balancing jobs.
* @param containerSize - the estimated size of container to be moved.
* @return Source volume and Dest volume.
*/
Pair<HddsVolume, HddsVolume> chooseVolume(MutableVolumeSet volumeSet,
- double threshold, Map<HddsVolume, Long> deltaSizes, long containerSize);
+ double thresholdPercentage, Map<HddsVolume, Long> deltaSizes, long
containerSize);
}
diff --git
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerService.java
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerService.java
index 5bd84c2086f..ca5dc710856 100644
---
a/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerService.java
+++
b/hadoop-hdds/container-service/src/test/java/org/apache/hadoop/ozone/container/diskbalancer/TestDiskBalancerService.java
@@ -18,6 +18,7 @@
package org.apache.hadoop.ozone.container.diskbalancer;
import static
org.apache.hadoop.ozone.container.common.ContainerTestUtils.createDbInstancesForTestIfNeeded;
+import static
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getVolumeUsages;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -27,7 +28,6 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import com.google.common.collect.ImmutableList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
@@ -51,6 +51,7 @@
import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
import org.apache.hadoop.ozone.container.common.volume.StorageVolume;
+import
org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.VolumeFixedUsage;
import
org.apache.hadoop.ozone.container.diskbalancer.policy.ContainerChoosingPolicy;
import
org.apache.hadoop.ozone.container.diskbalancer.policy.DefaultContainerChoosingPolicy;
import
org.apache.hadoop.ozone.container.diskbalancer.policy.DefaultVolumeChoosingPolicy;
@@ -252,10 +253,9 @@ public void testCalculateBytesToMove(int volumeCount, int
deltaUsagePercent,
long expectedBytesToMove = (long) Math.ceil(
(totalCapacity * expectedBytesToMovePercent) / 100.0 *
totalOverUtilisedVolumes);
- ImmutableList<HddsVolume> immutableVolumes =
DiskBalancerVolumeCalculation.getImmutableVolumeSet(volumeSet);
-
+ final List<VolumeFixedUsage> volumeUsages = getVolumeUsages(volumeSet,
null);
// data precision loss due to double data involved in calculation
- assertTrue(Math.abs(expectedBytesToMove -
svc.calculateBytesToMove(immutableVolumes)) <= 1);
+ assertTrue(Math.abs(expectedBytesToMove -
svc.calculateBytesToMove(volumeUsages)) <= 1);
}
@Test
@@ -293,7 +293,8 @@ public void testConcurrentTasksNotExceedThreadLimit()
throws Exception {
when(containerData.getBytesUsed()).thenReturn(100L);
when(volumePolicy.chooseVolume(any(), anyDouble(), any(),
anyLong())).thenReturn(Pair.of(source, dest));
- when(containerPolicy.chooseContainer(any(), any(), any(), any(), any(),
any(), any())).thenReturn(containerData);
+ when(containerPolicy.chooseContainer(any(), any(), any(), any(),
anyDouble(), any(), any()))
+ .thenReturn(containerData);
// Test when no tasks are in progress, it should schedule up to the limit
BackgroundTaskQueue queue = svc.getTasks();
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestContainerChoosingPolicy.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestContainerChoosingPolicy.java
index efebf035253..15c39626564 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestContainerChoosingPolicy.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestContainerChoosingPolicy.java
@@ -279,7 +279,6 @@ public void createContainers() {
containerSet.addContainer(container); // Add container to ContainerSet
} catch (Exception e) {
Assertions.fail(e.getMessage());
- throw new RuntimeException("Failed to add container to ContainerSet",
e);
}
// Collect IDs of closed containers
diff --git
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestVolumeChoosingPolicy.java
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestVolumeChoosingPolicy.java
index b65cfa637c8..1bb20c1ce71 100644
---
a/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestVolumeChoosingPolicy.java
+++
b/hadoop-ozone/integration-test/src/test/java/org/apache/hadoop/ozone/scm/node/TestVolumeChoosingPolicy.java
@@ -262,7 +262,7 @@ private void createVolumes() throws IOException {
}
// Initialize the volumeSet with the new volume map
- volumeSet.setVolumeMap(newVolumeMap);
+ volumeSet.setVolumeMapForTesting(newVolumeMap);
System.out.println("Created " + NUM_VOLUMES + " volumes in " +
(System.currentTimeMillis() - startTime) + " ms");
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]