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

SteNicholas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/celeborn.git


The following commit(s) were added to refs/heads/main by this push:
     new 56444d2ea [CELEBORN-2327] Add active-slot weight to load-aware 
placement
56444d2ea is described below

commit 56444d2ea58ed98b0e6bc8612e8c816fb2406e63
Author: Chao Sun <[email protected]>
AuthorDate: Tue May 19 02:41:10 2026 +0800

    [CELEBORN-2327] Add active-slot weight to load-aware placement
    
    ## Why are the changes needed?
    
    Celeborn load-aware slot placement currently orders candidate disks using 
flush and fetch timing only. That can still keep assigning new partitions onto 
disks that already carry a large amount of reserved active-slot pressure, which 
makes placement skew worse under overlapping shuffle-heavy workloads. 
CELEBORN-2327 tracks this gap.
    
    ## What changes were proposed in this PR?
    
    - Add an optional, default-off 
`celeborn.master.slot.assign.loadAware.activeSlotsWeight` config.
    - Include `activeSlots * activeSlotsWeight` in load-aware disk ordering.
    - Thread the new config through the master allocation path.
    - Document the new tuning knob and update the slot-allocation developer 
docs.
    - Add a regression test showing that, when configured, lower-active-slot 
disks are preferred over otherwise equivalent disks.
    
    ## How was this PR tested?
    
    - `UPDATE=1 build/mvn clean test -pl common -am -Dtest=none 
-DwildcardSuites=org.apache.celeborn.ConfigurationSuite`
    - `./build/mvn -pl master -am -Dtest=SlotsAllocatorSuiteJ 
-DwildcardSuites=org.apache.celeborn.NoSuchSuite -DfailIfNoTests=false test`
    - `./build/mvn -pl master -am -DskipTests test-compile`
    
    Closes #3685 from sunchao/dev/chao/codex/active-slots-placement-oss.
    
    Authored-by: Chao Sun <[email protected]>
    Signed-off-by: SteNicholas <[email protected]>
---
 .../org/apache/celeborn/common/CelebornConf.scala  | 11 +++++++
 docs/configuration/master.md                       |  1 +
 docs/developers/slotsallocation.md                 |  8 ++++-
 .../service/deploy/master/SlotsAllocator.java      | 12 +++++--
 .../celeborn/service/deploy/master/Master.scala    |  2 ++
 .../deploy/master/SlotsAllocatorSuiteJ.java        | 37 ++++++++++++++++++++++
 6 files changed, 67 insertions(+), 4 deletions(-)

diff --git 
a/common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala 
b/common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala
index ed7013ed5..40d06617f 100644
--- a/common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala
+++ b/common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala
@@ -679,6 +679,8 @@ class CelebornConf(loadDefaults: Boolean) extends Cloneable 
with Logging with Se
     get(MASTER_SLOT_ASSIGN_LOADAWARE_FLUSHTIME_WEIGHT)
   def masterSlotAssignLoadAwareFetchTimeWeight: Double =
     get(MASTER_SLOT_ASSIGN_LOADAWARE_FETCHTIME_WEIGHT)
+  def masterSlotAssignLoadAwareActiveSlotsWeight: Double =
+    get(MASTER_SLOT_ASSIGN_LOADAWARE_ACTIVE_SLOTS_WEIGHT)
   def masterSlotAssignExtraSlots: Int = get(MASTER_SLOT_ASSIGN_EXTRA_SLOTS)
   def masterSlotAssignMaxWorkers: Int = get(MASTER_SLOT_ASSIGN_MAX_WORKERS)
   def masterSlotAssignMinWorkers: Int = get(MASTER_SLOT_ASSIGN_MIN_WORKERS)
@@ -3154,6 +3156,15 @@ object CelebornConf extends Logging {
       .doubleConf
       .createWithDefault(1)
 
+  val MASTER_SLOT_ASSIGN_LOADAWARE_ACTIVE_SLOTS_WEIGHT: ConfigEntry[Double] =
+    buildConf("celeborn.master.slot.assign.loadAware.activeSlotsWeight")
+      .categories("master")
+      .doc(
+        "Weight of active slots when calculating ordering in load-aware 
assignment strategy")
+      .version("0.7.0")
+      .doubleConf
+      .createWithDefault(0)
+
   val MASTER_SLOT_ASSIGN_EXTRA_SLOTS: ConfigEntry[Int] =
     buildConf("celeborn.master.slot.assign.extraSlots")
       .withAlternative("celeborn.slots.assign.extraSlots")
diff --git a/docs/configuration/master.md b/docs/configuration/master.md
index 9ae889fe0..1f1c900c9 100644
--- a/docs/configuration/master.md
+++ b/docs/configuration/master.md
@@ -77,6 +77,7 @@ license: |
 | celeborn.master.slot.assign.extraSlots | 2 | false | Extra slots number when 
master assign slots. Provided enough workers are available. | 0.3.0 | 
celeborn.slots.assign.extraSlots | 
 | celeborn.master.slot.assign.interruptionAware | false | false | If this is 
set to true, Celeborn master will prioritize partition placement on workers 
that are not in scope for maintenance soon. | 0.7.0 |  | 
 | celeborn.master.slot.assign.interruptionAware.threshold | 50 | false | This 
controls what percentage of hosts would be selected for slot selection in the 
first iteration of creating partitions. Default is 50%. | 0.7.0 |  | 
+| celeborn.master.slot.assign.loadAware.activeSlotsWeight | 0.0 | false | 
Weight of active slots when calculating ordering in load-aware assignment 
strategy | 0.7.0 |  | 
 | celeborn.master.slot.assign.loadAware.diskGroupGradient | 0.1 | false | This 
value means how many more workload will be placed into a faster disk group than 
a slower group. | 0.3.0 | celeborn.slots.assign.loadAware.diskGroupGradient | 
 | celeborn.master.slot.assign.loadAware.fetchTimeWeight | 1.0 | false | Weight 
of average fetch time when calculating ordering in load-aware assignment 
strategy | 0.3.0 | celeborn.slots.assign.loadAware.fetchTimeWeight | 
 | celeborn.master.slot.assign.loadAware.flushTimeWeight | 0.0 | false | Weight 
of average flush time when calculating ordering in load-aware assignment 
strategy | 0.3.0 | celeborn.slots.assign.loadAware.flushTimeWeight | 
diff --git a/docs/developers/slotsallocation.md 
b/docs/developers/slotsallocation.md
index 71060bba1..f1dcb040e 100644
--- a/docs/developers/slotsallocation.md
+++ b/docs/developers/slotsallocation.md
@@ -33,6 +33,7 @@ celeborn.master.slot.assign.loadAware.numDiskGroups 5
 celeborn.master.slot.assign.loadAware.diskGroupGradient 0.1
 celeborn.master.slot.assign.loadAware.flushTimeWeight 0
 celeborn.master.slot.assign.loadAware.fetchTimeWeight 1
+celeborn.master.slot.assign.loadAware.activeSlotsWeight 0
 [spark.client.]celeborn.storage.availableTypes HDD,SSD
 ```
 ### Detail
@@ -44,7 +45,12 @@ Load-aware slots allocation will take following elements 
into consideration.
 - disk's used slot 
 
 Slots allocator will find out all worker involved in this allocation and sort 
their disks by 
-`disk's average flushtime * flush time weight + disk's average fetch time * 
fetch time weight`.
+`disk's average flushtime * flush time weight + disk's average fetch time * 
fetch time weight + disk's active slots * active slots weight`.
+The average flush/fetch times are measured in nanoseconds, while active slots 
is a slot count, so
+`activeSlotsWeight` is effectively a nanoseconds-per-slot conversion factor. 
For example, if the
+average fetch time is around `100 ms` (`10^8` ns) and a disk has about `1000` 
active slots,
+`activeSlotsWeight=10^5` makes the active-slot term contribute about `10^8`, 
comparable to the
+fetch-time term.
 After getting the sorted disks list, Celeborn will split the disks into
 `celeborn.master.slot.assign.loadAware.numDiskGroups` groups. The slots number 
to be placed into a disk group 
 is controlled by the `celeborn.master.slot.assign.loadAware.diskGroupGradient` 
which means that a group's 
diff --git 
a/master/src/main/java/org/apache/celeborn/service/deploy/master/SlotsAllocator.java
 
b/master/src/main/java/org/apache/celeborn/service/deploy/master/SlotsAllocator.java
index 07b817427..481b0aa10 100644
--- 
a/master/src/main/java/org/apache/celeborn/service/deploy/master/SlotsAllocator.java
+++ 
b/master/src/main/java/org/apache/celeborn/service/deploy/master/SlotsAllocator.java
@@ -111,6 +111,7 @@ public class SlotsAllocator {
           double diskGroupGradient,
           double flushTimeWeight,
           double fetchTimeWeight,
+          double activeSlotsWeight,
           int availableStorageTypes,
           boolean interruptionAware,
           int interruptionAwareThreshold) {
@@ -178,7 +179,8 @@ public class SlotsAllocator {
 
     Map<WorkerInfo, List<UsableDiskInfo>> slotsRestrictions =
         getSlotsRestrictionsByLoadAwareAlgorithm(
-            placeDisksToGroups(usableDisks, diskGroupCount, flushTimeWeight, 
fetchTimeWeight),
+            placeDisksToGroups(
+                usableDisks, diskGroupCount, flushTimeWeight, fetchTimeWeight, 
activeSlotsWeight),
             diskToWorkerMap,
             shouldReplicate ? partitionIds.size() * 2 : partitionIds.size());
     return locateSlots(
@@ -664,13 +666,17 @@ public class SlotsAllocator {
       List<DiskInfo> usableDisks,
       int diskGroupCount,
       double flushTimeWeight,
-      double fetchTimeWeight) {
+      double fetchTimeWeight,
+      double activeSlotsWeight) {
     List<List<DiskInfo>> diskGroups = new ArrayList<>();
     usableDisks.sort(
         (o1, o2) -> {
           double delta =
               (o1.avgFlushTime() * flushTimeWeight + o1.avgFetchTime() * 
fetchTimeWeight)
-                  - (o2.avgFlushTime() * flushTimeWeight + o2.avgFetchTime() * 
fetchTimeWeight);
+                  + o1.activeSlots() * activeSlotsWeight
+                  - (o2.avgFlushTime() * flushTimeWeight
+                      + o2.avgFetchTime() * fetchTimeWeight
+                      + o2.activeSlots() * activeSlotsWeight);
           return delta < 0 ? -1 : (delta > 0 ? 1 : 0);
         });
     int diskCount = usableDisks.size();
diff --git 
a/master/src/main/scala/org/apache/celeborn/service/deploy/master/Master.scala 
b/master/src/main/scala/org/apache/celeborn/service/deploy/master/Master.scala
index c87f53739..81e0e0076 100644
--- 
a/master/src/main/scala/org/apache/celeborn/service/deploy/master/Master.scala
+++ 
b/master/src/main/scala/org/apache/celeborn/service/deploy/master/Master.scala
@@ -212,6 +212,7 @@ private[celeborn] class Master(
     conf.masterSlotAssignLoadAwareDiskGroupGradient
   private val loadAwareFlushTimeWeight = 
conf.masterSlotAssignLoadAwareFlushTimeWeight
   private val loadAwareFetchTimeWeight = 
conf.masterSlotAssignLoadAwareFetchTimeWeight
+  private val loadAwareActiveSlotsWeight = 
conf.masterSlotAssignLoadAwareActiveSlotsWeight
 
   private val estimatedPartitionSizeUpdaterInitialDelay =
     conf.estimatedPartitionSizeUpdaterInitialDelay
@@ -981,6 +982,7 @@ private[celeborn] class Master(
               slotsAssignLoadAwareDiskGroupGradient,
               loadAwareFlushTimeWeight,
               loadAwareFetchTimeWeight,
+              loadAwareActiveSlotsWeight,
               requestSlots.availableStorageTypes,
               slotsAssignInterruptionAware,
               slotsAssignInterruptionAwareThreshold)
diff --git 
a/master/src/test/java/org/apache/celeborn/service/deploy/master/SlotsAllocatorSuiteJ.java
 
b/master/src/test/java/org/apache/celeborn/service/deploy/master/SlotsAllocatorSuiteJ.java
index b3b53f221..f7c70d26e 100644
--- 
a/master/src/test/java/org/apache/celeborn/service/deploy/master/SlotsAllocatorSuiteJ.java
+++ 
b/master/src/test/java/org/apache/celeborn/service/deploy/master/SlotsAllocatorSuiteJ.java
@@ -144,6 +144,41 @@ public class SlotsAllocatorSuiteJ {
     check(workers, partitionIds, shouldReplicate, true, true, false, 0);
   }
 
+  @Test
+  public void testLoadAwarePrefersLowerActiveSlotsWhenConfigured() {
+    final List<WorkerInfo> workers =
+        basePrepareWorkers(
+            2,
+            true,
+            ImmutableMap.of("/mnt/disk1", 100 * 1024 * 1024 * 1024L),
+            64 * 1024 * 1024L,
+            1,
+            false,
+            new Random(0));
+    final DiskInfo overloadedDisk = 
workers.get(0).diskInfos().get("/mnt/disk1");
+    final DiskInfo lightlyReservedDisk = 
workers.get(1).diskInfos().get("/mnt/disk1");
+    overloadedDisk.activeSlots_$eq(1000);
+    lightlyReservedDisk.activeSlots_$eq(0);
+
+    final Map<WorkerInfo, Tuple2<List<PartitionLocation>, 
List<PartitionLocation>>> slots =
+        SlotsAllocator.offerSlotsLoadAware(
+            workers,
+            Collections.singletonList(0),
+            false,
+            false,
+            2,
+            1,
+            0,
+            0,
+            1,
+            StorageInfo.ALL_TYPES_AVAILABLE_MASK,
+            false,
+            0);
+
+    assertTrue(slots.containsKey(workers.get(1)));
+    assertFalse(slots.containsKey(workers.get(0)));
+  }
+
   private void check(
       List<WorkerInfo> workers,
       List<Integer> partitionIds,
@@ -186,6 +221,7 @@ public class SlotsAllocatorSuiteJ {
               conf.masterSlotAssignLoadAwareDiskGroupGradient(),
               conf.masterSlotAssignLoadAwareFlushTimeWeight(),
               conf.masterSlotAssignLoadAwareFetchTimeWeight(),
+              conf.masterSlotAssignLoadAwareActiveSlotsWeight(),
               StorageInfo.ALL_TYPES_AVAILABLE_MASK,
               interruptionAware,
               interruptionAwareThreshold);
@@ -298,6 +334,7 @@ public class SlotsAllocatorSuiteJ {
               0.1,
               0,
               1,
+              0,
               StorageInfo.LOCAL_DISK_MASK | availableStorageTypes,
               false,
               0);

Reply via email to