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

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


The following commit(s) were added to refs/heads/branch-0.7 by this push:
     new 1272744e67 [CELEBORN-2489] Fix unknown worker lookup in shuffle data 
loss detection
1272744e67 is described below

commit 1272744e6726b895c7b2d095d802dc4517a71189
Author: zhengtao <[email protected]>
AuthorDate: Wed Oct 21 10:47:27 2026 +0800

    [CELEBORN-2489] Fix unknown worker lookup in shuffle data loss detection
    
    ### What changes were proposed in this pull request?
    
    Use `allocatedWorkers.containsKey(workerId.toUniqueId)` when checking 
whether an unknown worker belongs to a shuffle.
    
    Add focused tests for an allocated unknown worker, an unrelated unknown 
worker, disabled detection, enabled replication, a different failure status, 
and a shuffle without allocated workers.
    
    JIRA: https://issues.apache.org/jira/browse/CELEBORN-2489
    
    ### Why are the changes needed?
    
    `allocatedWorkers` is a `ConcurrentHashMap[String, 
ShufflePartitionLocationInfo]`. Its legacy `contains` method checks values, 
while the lookup argument is a `WorkerInfo`. The condition therefore remains 
false even when the lost worker is allocated to the shuffle.
    
    With `celeborn.client.shuffleDataLostOnUnknownWorker.enabled=true` and 
replication disabled, the client should report shuffle data loss as soon as an 
allocated worker becomes `WORKER_UNKNOWN`. The current check misses this and 
waits for a later failure path. This fix restores the existing early failure 
behavior and preserves the configuration and replication guards.
    
    ### Does this PR resolve a correctness bug?
    
    - [ ] Yes
    
    ### Does this PR introduce _any_ user-facing change?
    
    - [x] Yes
    
    Restores the configured early failure behavior for a lost allocated worker.
    
    ### How was this patch tested?
    
    - Before the fix, the new suite runs six tests: the allocated 
unknown-worker case fails and the other five pass.
    - After the fix, 19 tests pass across `ReducePartitionCommitHandlerSuite`, 
`WorkerStatusTrackerSuite`, and `MapPartitionCommitHandlerTest` with Java 17 / 
Scala 2.12.
    - `spotless:check` passes for the client and its upstream modules.
    
    Closes #3881 from zaynt4606/CELEBORN-2489.
    
    Authored-by: zhengtao <[email protected]>
    Signed-off-by: Nicholas Jiang <[email protected]>
    (cherry picked from commit 55474d41bd92fe818c0a67b463dc86da3f548a07)
    Signed-off-by: Nicholas Jiang <[email protected]>
---
 .../commit/ReducePartitionCommitHandler.scala      | 10 +--
 .../commit/ReducePartitionCommitHandlerSuite.scala | 96 ++++++++++++++++++++++
 2 files changed, 101 insertions(+), 5 deletions(-)

diff --git 
a/client/src/main/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandler.scala
 
b/client/src/main/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandler.scala
index febee90f8d..7477cb9dd3 100644
--- 
a/client/src/main/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandler.scala
+++ 
b/client/src/main/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandler.scala
@@ -152,11 +152,11 @@ class ReducePartitionCommitHandler(
     if (conf.clientShuffleDataLostOnUnknownWorkerEnabled && 
!conf.clientPushReplicateEnabled) {
       val allocatedWorkers = shuffleAllocatedWorkers.get(shuffleId)
       if (allocatedWorkers != null) {
-        return workerStatusTracker.excludedWorkers.asScala.collect {
-          case (workerId, (status, _))
-              if status == StatusCode.WORKER_UNKNOWN && 
allocatedWorkers.contains(workerId) =>
-            workerId
-        }.nonEmpty
+        return workerStatusTracker.excludedWorkers.asScala.exists {
+          case (workerId, (status, _)) =>
+            status == StatusCode.WORKER_UNKNOWN &&
+              allocatedWorkers.containsKey(workerId.toUniqueId)
+        }
       }
     }
     false
diff --git 
a/client/src/test/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandlerSuite.scala
 
b/client/src/test/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandlerSuite.scala
new file mode 100644
index 0000000000..bca906f341
--- /dev/null
+++ 
b/client/src/test/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandlerSuite.scala
@@ -0,0 +1,96 @@
+/*
+ * 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.celeborn.client.commit
+
+import java.util.concurrent.{ScheduledExecutorService, ThreadPoolExecutor}
+
+import org.mockito.Mockito.mock
+
+import org.apache.celeborn.CelebornFunSuite
+import org.apache.celeborn.client.{LifecycleManager, WorkerStatusTracker}
+import org.apache.celeborn.client.CommitManager.CommittedPartitionInfo
+import org.apache.celeborn.client.LifecycleManager.ShuffleAllocatedWorkers
+import org.apache.celeborn.common.CelebornConf
+import org.apache.celeborn.common.meta.{ShufflePartitionLocationInfo, 
WorkerInfo}
+import org.apache.celeborn.common.protocol.message.StatusCode
+import org.apache.celeborn.common.util.JavaUtils
+
+class ReducePartitionCommitHandlerSuite extends CelebornFunSuite {
+  private val shuffleId = 1
+
+  test("mark data lost when an allocated worker becomes unknown") {
+    // Heartbeats may report a different WorkerInfo instance for the same 
worker identity.
+    val handler = newHandler(worker("allocated"))
+    assert(handler.isStageDataLost(shuffleId))
+  }
+
+  test("do not mark data lost when an unrelated worker becomes unknown") {
+    val handler = newHandler(worker("unrelated"))
+    assert(!handler.isStageDataLost(shuffleId))
+  }
+
+  test("do not mark data lost when unknown-worker detection is disabled") {
+    val handler = newHandler(worker("allocated"), detectionEnabled = false)
+    assert(!handler.isStageDataLost(shuffleId))
+  }
+
+  test("do not mark data lost on an unknown worker when replication is 
enabled") {
+    val handler = newHandler(worker("allocated"), replicationEnabled = true)
+    assert(!handler.isStageDataLost(shuffleId))
+  }
+
+  test("do not mark data lost for an allocated worker with another failure 
status") {
+    val handler = newHandler(worker("allocated"), status = 
StatusCode.WORKER_UNRESPONSIVE)
+    assert(!handler.isStageDataLost(shuffleId))
+  }
+
+  test("do not mark data lost for a shuffle without allocated workers") {
+    val handler = newHandler(worker("allocated"))
+    assert(!handler.isStageDataLost(shuffleId + 1))
+  }
+
+  private def newHandler(
+      excludedWorker: WorkerInfo,
+      detectionEnabled: Boolean = true,
+      replicationEnabled: Boolean = false,
+      status: StatusCode = StatusCode.WORKER_UNKNOWN): 
ReducePartitionCommitHandler = {
+    val conf = new CelebornConf()
+      .set(CelebornConf.CLIENT_SHUFFLE_DATA_LOST_ON_UNKNOWN_WORKER_ENABLED, 
detectionEnabled)
+      .set(CelebornConf.CLIENT_PUSH_REPLICATE_ENABLED, replicationEnabled)
+    val allocatedWorker = worker("allocated")
+    val workers = JavaUtils.newConcurrentHashMap[String, 
ShufflePartitionLocationInfo]()
+    workers.put(allocatedWorker.toUniqueId, new 
ShufflePartitionLocationInfo(allocatedWorker))
+    val allocatedWorkers = new ShuffleAllocatedWorkers()
+    allocatedWorkers.put(shuffleId, workers)
+    val statusTracker = new WorkerStatusTracker(conf, null)
+    statusTracker.excludedWorkers.put(excludedWorker, (status, 
System.currentTimeMillis()))
+
+    new ReducePartitionCommitHandler(
+      "test-app",
+      conf,
+      allocatedWorkers,
+      new CommittedPartitionInfo(),
+      statusTracker,
+      mock(classOf[ThreadPoolExecutor]),
+      mock(classOf[ScheduledExecutorService]),
+      mock(classOf[LifecycleManager]))
+  }
+
+  private def worker(host: String): WorkerInfo =
+    new WorkerInfo(host, 1001, 1002, 1003, 1004)
+}

Reply via email to