Skip to content

Commit 2abd751

Browse files
authored
HDDS-14175. DiskBalancer should not call getCurrentUsage() multiple times. (#9505)
1 parent 388084e commit 2abd751

10 files changed

Lines changed: 154 additions & 166 deletions

File tree

hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/common/volume/VolumeUsage.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,8 @@ public long getReservedInBytes() {
175175
return reservedInBytes;
176176
}
177177

178-
private static long getUsableSpace(
179-
long available, long committed, long minFreeSpace) {
180-
return available - committed - minFreeSpace;
178+
public static long getUsableSpace(long available, long committed, long spared) {
179+
return available - committed - spared;
181180
}
182181

183182
public static long getUsableSpace(StorageReportProto report) {

hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerService.java

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,19 @@
1818
package org.apache.hadoop.ozone.container.diskbalancer;
1919

2020
import static org.apache.hadoop.hdds.protocol.datanode.proto.ContainerProtos.Result.CONTAINER_ALREADY_EXISTS;
21+
import static org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.calculateVolumeDataDensity;
22+
import static org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getIdealUsage;
23+
import static org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.getVolumeUsages;
2124

2225
import com.google.common.annotations.VisibleForTesting;
2326
import com.google.common.base.Strings;
24-
import com.google.common.collect.ImmutableList;
2527
import java.io.File;
2628
import java.io.IOException;
2729
import java.nio.file.Files;
2830
import java.nio.file.Path;
2931
import java.nio.file.Paths;
3032
import java.nio.file.StandardCopyOption;
33+
import java.util.List;
3134
import java.util.Map;
3235
import java.util.Objects;
3336
import java.util.Set;
@@ -65,6 +68,7 @@
6568
import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
6669
import org.apache.hadoop.ozone.container.common.volume.StorageVolume;
6770
import org.apache.hadoop.ozone.container.common.volume.VolumeChoosingPolicyFactory;
71+
import org.apache.hadoop.ozone.container.diskbalancer.DiskBalancerVolumeCalculation.VolumeFixedUsage;
6872
import org.apache.hadoop.ozone.container.diskbalancer.policy.ContainerChoosingPolicy;
6973
import org.apache.hadoop.ozone.container.diskbalancer.policy.DiskBalancerVolumeChoosingPolicy;
7074
import org.apache.hadoop.ozone.container.keyvalue.KeyValueContainerData;
@@ -632,54 +636,46 @@ private boolean tryCleanupOnePendingDeletionContainer() {
632636
}
633637

634638
public DiskBalancerInfo getDiskBalancerInfo() {
635-
ImmutableList<HddsVolume> immutableVolumeSet = DiskBalancerVolumeCalculation.getImmutableVolumeSet(volumeSet);
639+
final List<VolumeFixedUsage> volumeUsages = getVolumeUsages(volumeSet, deltaSizes);
636640

637641
// Calculate volumeDataDensity
638-
double volumeDatadensity = 0.0;
639-
volumeDatadensity = DiskBalancerVolumeCalculation.calculateVolumeDataDensity(immutableVolumeSet, deltaSizes);
642+
final double volumeDataDensity = calculateVolumeDataDensity(volumeUsages);
640643

641644
long bytesToMove = 0;
642645
if (this.operationalState == DiskBalancerRunningStatus.RUNNING) {
643646
// this calculates live changes in bytesToMove
644647
// calculate bytes to move if the balancer is in a running state, else 0.
645-
bytesToMove = calculateBytesToMove(immutableVolumeSet);
648+
bytesToMove = calculateBytesToMove(volumeUsages);
646649
}
647650

648651
return new DiskBalancerInfo(operationalState, threshold, bandwidthInMB,
649652
parallelThread, stopAfterDiskEven, version, metrics.getSuccessCount(),
650-
metrics.getFailureCount(), bytesToMove, metrics.getSuccessBytes(), volumeDatadensity);
653+
metrics.getFailureCount(), bytesToMove, metrics.getSuccessBytes(), volumeDataDensity);
651654
}
652655

653-
public long calculateBytesToMove(ImmutableList<HddsVolume> inputVolumeSet) {
656+
public long calculateBytesToMove(List<VolumeFixedUsage> inputVolumeSet) {
654657
// If there are no available volumes or only one volume, return 0 bytes to move
655658
if (inputVolumeSet.isEmpty() || inputVolumeSet.size() < 2) {
656659
return 0;
657660
}
658661

659-
// Calculate ideal usage
660-
double idealUsage = DiskBalancerVolumeCalculation.getIdealUsage(inputVolumeSet, deltaSizes);
661-
double normalizedThreshold = threshold / 100.0;
662+
// Calculate actual threshold
663+
final double actualThreshold = getIdealUsage(inputVolumeSet) + threshold / 100.0;
662664

663665
long totalBytesToMove = 0;
664666

665667
// Calculate excess data in overused volumes
666-
for (HddsVolume volume : inputVolumeSet) {
667-
SpaceUsageSource usage = volume.getCurrentUsage();
668-
668+
for (VolumeFixedUsage volumeUsage : inputVolumeSet) {
669+
final SpaceUsageSource.Fixed usage = volumeUsage.getUsage();
669670
if (usage.getCapacity() == 0) {
670671
continue;
671672
}
672673

673-
long deltaSize = deltaSizes.getOrDefault(volume, 0L);
674-
double currentUsage = (double)((usage.getCapacity() - usage.getAvailable())
675-
+ deltaSize + volume.getCommittedBytes()) / usage.getCapacity();
676-
677-
double volumeUtilisation = currentUsage - idealUsage;
678-
674+
final double excess = volumeUsage.getUtilization() - actualThreshold;
679675
// Only consider volumes that exceed the threshold (source volumes)
680-
if (volumeUtilisation >= normalizedThreshold) {
676+
if (excess > 0) {
681677
// Calculate excess bytes that need to be moved from this volume
682-
long excessBytes = (long) ((volumeUtilisation - normalizedThreshold) * usage.getCapacity());
678+
final long excessBytes = (long) (excess * usage.getCapacity());
683679
totalBytesToMove += Math.max(0, excessBytes);
684680
}
685681
}

hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/DiskBalancerVolumeCalculation.java

Lines changed: 74 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@
1717

1818
package org.apache.hadoop.ozone.container.diskbalancer;
1919

20-
import com.google.common.base.Preconditions;
21-
import com.google.common.collect.ImmutableList;
20+
import static org.apache.ratis.util.Preconditions.assertInstanceOf;
21+
import static org.apache.ratis.util.Preconditions.assertTrue;
22+
2223
import java.util.List;
2324
import java.util.Map;
25+
import java.util.Objects;
26+
import java.util.stream.Collectors;
2427
import org.apache.hadoop.hdds.fs.SpaceUsageSource;
25-
import org.apache.hadoop.ozone.container.common.utils.StorageVolumeUtil;
2628
import org.apache.hadoop.ozone.container.common.volume.HddsVolume;
2729
import org.apache.hadoop.ozone.container.common.volume.MutableVolumeSet;
30+
import org.apache.hadoop.ozone.container.common.volume.StorageVolume;
31+
import org.apache.hadoop.ozone.container.common.volume.VolumeUsage;
2832
import org.slf4j.Logger;
2933
import org.slf4j.LoggerFactory;
3034

@@ -47,51 +51,42 @@ private DiskBalancerVolumeCalculation() {
4751

4852
/**
4953
* Get an immutable snapshot of volumes from a MutableVolumeSet.
50-
*
54+
*
5155
* @param volumeSet The MutableVolumeSet to create a snapshot from
52-
* @return Immutable list of HddsVolume objects
56+
* @return a list of volumes and usages
5357
*/
54-
public static ImmutableList<HddsVolume> getImmutableVolumeSet(MutableVolumeSet volumeSet) {
55-
// Create an immutable copy of the volume list at this point in time
56-
List<HddsVolume> volumes = StorageVolumeUtil.getHddsVolumesList(volumeSet.getVolumesList());
57-
return ImmutableList.copyOf(volumes);
58+
public static List<VolumeFixedUsage> getVolumeUsages(MutableVolumeSet volumeSet, Map<HddsVolume, Long> deltas) {
59+
return volumeSet.getVolumesList().stream()
60+
.map(v -> newVolumeFixedUsage(v, deltas))
61+
.collect(Collectors.toList());
5862
}
5963

6064
/**
6165
* Get ideal usage from an immutable list of volumes.
6266
*
6367
* @param volumes Immutable list of volumes
64-
* @param deltaMap A map that tracks the total bytes which will be freed
6568
* from each source volume during container moves
6669
* @return Ideal usage as a ratio (used space / total capacity)
6770
* @throws IllegalArgumentException if total capacity is zero
6871
*/
69-
public static double getIdealUsage(ImmutableList<HddsVolume> volumes,
70-
Map<HddsVolume, Long> deltaMap) {
72+
public static double getIdealUsage(List<VolumeFixedUsage> volumes) {
7173
long totalCapacity = 0L, totalEffectiveUsed = 0L;
7274

73-
for (HddsVolume volume : volumes) {
74-
SpaceUsageSource usage = volume.getCurrentUsage();
75-
totalCapacity += usage.getCapacity();
76-
long currentUsed = usage.getCapacity() - usage.getAvailable();
77-
long delta = (deltaMap != null) ? deltaMap.getOrDefault(volume, 0L) : 0L;
78-
long committed = volume.getCommittedBytes();
79-
totalEffectiveUsed += (currentUsed + delta + committed);
75+
for (VolumeFixedUsage volumeUsage : volumes) {
76+
totalCapacity += volumeUsage.getUsage().getCapacity();
77+
totalEffectiveUsed += volumeUsage.getEffectiveUsed();
8078
}
8179

82-
Preconditions.checkArgument(totalCapacity != 0);
8380
return ((double) (totalEffectiveUsed)) / totalCapacity;
8481
}
8582

8683
/**
8784
* Calculate VolumeDataDensity.
8885
*
8986
* @param volumeSet The MutableVolumeSet containing all volumes
90-
* @param deltaMap Map of volume to delta sizes (ongoing operations), can be null
9187
* @return VolumeDataDensity sum across all volumes
9288
*/
93-
public static double calculateVolumeDataDensity(ImmutableList<HddsVolume> volumeSet,
94-
Map<HddsVolume, Long> deltaMap) {
89+
public static double calculateVolumeDataDensity(List<VolumeFixedUsage> volumeSet) {
9590
if (volumeSet == null) {
9691
LOG.warn("VolumeSet is null, returning 0.0 for VolumeDataDensity");
9792
return 0.0;
@@ -104,18 +99,13 @@ public static double calculateVolumeDataDensity(ImmutableList<HddsVolume> volume
10499
}
105100

106101
// Calculate ideal usage using the same immutable volume snapshot
107-
double idealUsage = getIdealUsage(volumeSet, deltaMap);
102+
final double idealUsage = getIdealUsage(volumeSet);
108103
double volumeDensitySum = 0.0;
109104

110105
// Calculate density for each volume using the same snapshot
111-
for (HddsVolume volume : volumeSet) {
112-
SpaceUsageSource usage = volume.getCurrentUsage();
113-
Preconditions.checkArgument(usage.getCapacity() != 0);
114-
115-
long deltaSize = (deltaMap != null) ? deltaMap.getOrDefault(volume, 0L) : 0L;
116-
double currentUsage = (double)((usage.getCapacity() - usage.getAvailable())
117-
+ deltaSize + volume.getCommittedBytes()) / usage.getCapacity();
118-
106+
for (VolumeFixedUsage volumeUsage : volumeSet) {
107+
final double currentUsage = volumeUsage.getUtilization();
108+
119109
// Calculate density as absolute difference from ideal usage
120110
double volumeDensity = Math.abs(currentUsage - idealUsage);
121111
volumeDensitySum += volumeDensity;
@@ -126,4 +116,56 @@ public static double calculateVolumeDataDensity(ImmutableList<HddsVolume> volume
126116
return -1.0;
127117
}
128118
}
119+
120+
public static double computeUtilization(SpaceUsageSource.Fixed usage, long committed, long required) {
121+
final long capacity = usage.getCapacity();
122+
assertTrue(capacity > 0, () -> "capacity = " + capacity + " <= 0");
123+
return computeEffectiveUsage(usage, committed, required) / (double) capacity;
124+
}
125+
126+
private static long computeEffectiveUsage(SpaceUsageSource.Fixed usage, long committed, long required) {
127+
return usage.getCapacity() - usage.getAvailable() + committed + required;
128+
}
129+
130+
public static VolumeFixedUsage newVolumeFixedUsage(StorageVolume volume, Map<HddsVolume, Long> deltaMap) {
131+
final HddsVolume v = assertInstanceOf(volume, HddsVolume.class);
132+
final long delta = deltaMap == null ? 0 : deltaMap.getOrDefault(v, 0L);
133+
return new VolumeFixedUsage(v, delta);
134+
}
135+
136+
/** {@link HddsVolume} with a {@link SpaceUsageSource.Fixed} usage. */
137+
public static final class VolumeFixedUsage {
138+
private final HddsVolume volume;
139+
private final SpaceUsageSource.Fixed usage;
140+
private final long effectiveUsed;
141+
private final Double utilization;
142+
143+
private VolumeFixedUsage(HddsVolume volume, long delta) {
144+
this.volume = volume;
145+
this.usage = volume.getCurrentUsage();
146+
this.effectiveUsed = computeEffectiveUsage(usage, volume.getCommittedBytes(), delta);
147+
this.utilization = usage.getCapacity() > 0 ? computeUtilization(usage, volume.getCommittedBytes(), delta) : null;
148+
}
149+
150+
public HddsVolume getVolume() {
151+
return volume;
152+
}
153+
154+
public SpaceUsageSource.Fixed getUsage() {
155+
return usage;
156+
}
157+
158+
public long getEffectiveUsed() {
159+
return effectiveUsed;
160+
}
161+
162+
public double getUtilization() {
163+
return Objects.requireNonNull(utilization, "utilization == null");
164+
}
165+
166+
public long computeUsableSpace() {
167+
final long spared = volume.getFreeSpaceToSpare(usage.getCapacity());
168+
return VolumeUsage.getUsableSpace(usage.getAvailable(), volume.getCommittedBytes(), spared);
169+
}
170+
}
129171
}

hadoop-hdds/container-service/src/main/java/org/apache/hadoop/ozone/container/diskbalancer/policy/ContainerChoosingPolicy.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ public interface ContainerChoosingPolicy {
3636
* @param destVolume the destination volume to which container is being moved.
3737
* @param inProgressContainerIDs containerIDs present in this set should be
3838
- avoided as these containers are already under move by diskBalancer.
39-
* @param threshold the threshold value
39+
* @param thresholdPercentage the threshold percentage in range [0, 100]
4040
* @param volumeSet the volumeSet instance
4141
* @param deltaMap the deltaMap instance of source volume
4242
* @return a Container
4343
*/
4444
ContainerData chooseContainer(OzoneContainer ozoneContainer,
4545
HddsVolume srcVolume, HddsVolume destVolume,
4646
Set<ContainerID> inProgressContainerIDs,
47-
Double threshold, MutableVolumeSet volumeSet,
47+
double thresholdPercentage, MutableVolumeSet volumeSet,
4848
Map<HddsVolume, Long> deltaMap);
4949
}

0 commit comments

Comments
 (0)