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

zaynt4606 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 5099daad77 [CELEBORN-2490] Make memory file storage configurable for 
replica partitions
5099daad77 is described below

commit 5099daad77b0c8aed6f5a744a96eef4aa70a4f70
Author: Venkata Krishnan Sowrirajan <[email protected]>
AuthorDate: Thu Oct 22 10:19:34 2026 +0800

    [CELEBORN-2490] Make memory file storage configurable for replica partitions
    
    ### What changes were proposed in this pull request?
    
    Add a worker config `celeborn.worker.memoryFileStorage.replica.enabled` 
(default `true`) to preserve existing behavior. When explicitly disabled, 
`StoragePolicy` skips `MEMORY` for replica partitions and falls through to the 
next eligible storage type in 
`celeborn.worker.storage.storagePolicy.createFilePolicy`. The setting also 
applies when a custom `celeborn.worker.storage.storagePolicy.evictPolicy` 
targets MEMORY for replicas. Primary partitions are unaffected.
    
    JIRA: [CELEBORN-2490](https://issues.apache.org/jira/browse/CELEBORN-2490)
    
    ### Why are the changes needed?
    
    When memory file storage and replication are enabled, both primary and 
replica partitions can consume the memory file storage budget, which also 
contributes to push-pause thresholds. Replica data is normally read only when 
fetching from the primary fails. Allowing operators to keep replicas out of 
memory leaves more memory available for primary data and can reduce eviction 
and push-pause pressure, at the cost of additional non-memory storage I/O and 
potentially higher replica-fetch la [...]
    
    ### Does this PR resolve a correctness bug?
    
    - [ ] Yes
    
    ### Does this PR introduce _any_ user-facing change?
    
    - [x] Yes
    
    Adds `celeborn.worker.memoryFileStorage.replica.enabled`, defaulting to 
`true` for backward compatibility. Existing clusters retain their current 
storage-placement behavior after upgrade. Set it to `false` to opt out of 
memory file storage for replica partitions, including eviction policies that 
target MEMORY.
    
    ### How was this patch tested?
    
    - `StoragePolicyReplicaSuite` uses explicit memory/local-disk availability 
and covers primary behavior, replicas with the default and explicit true/false 
settings, and custom eviction policies targeting MEMORY.
    - `build/mvn -B -q test -pl worker -am 
-DwildcardSuites=org.apache.celeborn.service.deploy.worker.storage.storagePolicy,org.apache.celeborn.ConfigurationSuite
 -Dtest=none` (JDK 17): passed, 13 storage-policy tests and 8 
configuration-documentation tests.
    - `build/mvn -B -q spotless:check -pl common,worker`: passed.
    
    Closes #3859 from venkata91/CELEBORN-replica-skip-memory-tier.
    
    Authored-by: Venkata Krishnan Sowrirajan <[email protected]>
    Signed-off-by: zhengtao <[email protected]>
    
    Co-authored-by: Copilot <[email protected]>
    Co-Authored-By: Codex <[email protected]>
    AI-Model: gpt-6
    AI-Contributed/Feature: 0/27
    AI-Contributed/UT: 0/137
---
 .../org/apache/celeborn/common/CelebornConf.scala  |  16 +++
 docs/configuration/worker.md                       |   1 +
 .../deploy/worker/storage/StoragePolicy.scala      |  10 +-
 .../storagePolicy/StoragePolicyReplicaSuite.scala  | 137 +++++++++++++++++++++
 4 files changed, 162 insertions(+), 2 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 32f120d4a1..5f535fb344 100644
--- a/common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala
+++ b/common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala
@@ -1477,6 +1477,8 @@ class CelebornConf(loadDefaults: Boolean) extends 
Cloneable with Logging with Se
     get(WORKER_MEMORY_FILE_STORAGE_EVICT_AGGRESSIVE_MODE_ENABLED)
   def workerMemoryFileStorageEvictRatio: Double =
     get(WORKER_MEMORY_FILE_STORAGE_EVICT_RATIO)
+  def workerMemoryFileStorageReplicaEnabled: Boolean =
+    get(WORKER_MEMORY_FILE_STORAGE_REPLICA_ENABLED)
   def workerPushDataMergeBufferEnabled: Boolean = 
get(WORKER_PUSH_DATA_MERGE_BUFFER_ENABLED)
   def workerDirectMemoryRatioToMergeBuffer: Double = 
get(WORKER_DIRECT_MEMORY_RATIO_TO_MERGE_BUFFER)
 
@@ -4442,6 +4444,20 @@ object CelebornConf extends Logging {
       .doubleConf
       .createWithDefault(0.5)
 
+  val WORKER_MEMORY_FILE_STORAGE_REPLICA_ENABLED: ConfigEntry[Boolean] =
+    buildConf("celeborn.worker.memoryFileStorage.replica.enabled")
+      .categories("worker")
+      .doc("Whether replica partitions can be stored in memory file storage. 
Enabled by default " +
+        "to preserve existing behavior. When disabled, replica partitions skip 
MEMORY and " +
+        "fall through to the next eligible storage type in " +
+        "`celeborn.worker.storage.storagePolicy.createFilePolicy`, leaving 
memory for " +
+        "primary partitions. This also applies when " +
+        "`celeborn.worker.storage.storagePolicy.evictPolicy` targets MEMORY 
for replicas. " +
+        "Primary partitions are unaffected.")
+      .version("1.0.0")
+      .booleanConf
+      .createWithDefault(true)
+
   val WORKER_PUSH_DATA_MERGE_BUFFER_ENABLED: ConfigEntry[Boolean] =
     buildConf("celeborn.worker.pushdata.mergeBuffer.enabled")
       .categories("worker")
diff --git a/docs/configuration/worker.md b/docs/configuration/worker.md
index dd0aa15a55..51bd1bdfe7 100644
--- a/docs/configuration/worker.md
+++ b/docs/configuration/worker.md
@@ -149,6 +149,7 @@ license: |
 | celeborn.worker.memoryFileStorage.evict.aggressiveMode.enabled | false | 
false | If this set to true, memory shuffle files will be evicted when worker 
is in PAUSED state. If the worker's offheap memory is not ample, set this to 
true and decrease `celeborn.worker.directMemoryRatioForMemoryFileStorage` will 
be helpful. | 0.5.1 |  | 
 | celeborn.worker.memoryFileStorage.evict.ratio | 0.5 | false | If memory 
shuffle storage usage rate is above this config, the memory storage shuffle 
files will evict to free memory. | 0.5.1 |  | 
 | celeborn.worker.memoryFileStorage.maxFileSize | 8MB | false | Max size for a 
memory storage file. It must be less than 2GB. | 0.5.0 |  | 
+| celeborn.worker.memoryFileStorage.replica.enabled | true | false | Whether 
replica partitions can be stored in memory file storage. Enabled by default to 
preserve existing behavior. When disabled, replica partitions skip MEMORY and 
fall through to the next eligible storage type in 
`celeborn.worker.storage.storagePolicy.createFilePolicy`, leaving memory for 
primary partitions. This also applies when 
`celeborn.worker.storage.storagePolicy.evictPolicy` targets MEMORY for 
replicas. Primary [...]
 | celeborn.worker.monitor.disk.check.interval | 30s | false | Intervals 
between device monitor to check disk. | 0.3.0 | 
celeborn.worker.monitor.disk.checkInterval | 
 | celeborn.worker.monitor.disk.check.timeout | 30s | false | Timeout time for 
worker check device status. | 0.3.0 | celeborn.worker.disk.check.timeout | 
 | celeborn.worker.monitor.disk.checklist | readwrite,diskusage | false | 
Monitor type for disk, available items are: iohang, readwrite and diskusage. | 
0.2.0 |  | 
diff --git 
a/worker/src/main/scala/org/apache/celeborn/service/deploy/worker/storage/StoragePolicy.scala
 
b/worker/src/main/scala/org/apache/celeborn/service/deploy/worker/storage/StoragePolicy.scala
index c7d8b4d807..7dbcee46ee 100644
--- 
a/worker/src/main/scala/org/apache/celeborn/service/deploy/worker/storage/StoragePolicy.scala
+++ 
b/worker/src/main/scala/org/apache/celeborn/service/deploy/worker/storage/StoragePolicy.scala
@@ -24,7 +24,7 @@ import 
org.apache.celeborn.common.exception.CelebornIOException
 import org.apache.celeborn.common.internal.Logging
 import org.apache.celeborn.common.meta.{DiskFileInfo, FileInfo}
 import org.apache.celeborn.common.metrics.source.AbstractSource
-import org.apache.celeborn.common.protocol.{PartitionType, StorageInfo}
+import org.apache.celeborn.common.protocol.{PartitionLocation, PartitionType, 
StorageInfo}
 import org.apache.celeborn.service.deploy.worker.memory.MemoryManager
 
 class StoragePolicy(conf: CelebornConf, storageManager: StorageManager, 
source: AbstractSource)
@@ -32,6 +32,8 @@ class StoragePolicy(conf: CelebornConf, storageManager: 
StorageManager, source:
   private val createFileOrder: Option[List[String]] = 
conf.workerStoragePolicyCreateFilePolicy
   private val evictFileOrder: Option[Map[String, List[String]]] =
     conf.workerStoragePolicyEvictFilePolicy
+  private val memoryFileStorageReplicaEnabled: Boolean =
+    conf.workerMemoryFileStorageReplicaEnabled
 
   def getEvictedFileWriter(
       celebornFile: TierWriterBase,
@@ -102,7 +104,11 @@ class StoragePolicy(conf: CelebornConf, storageManager: 
StorageManager, source:
       try {
         storageInfoType match {
           case StorageInfo.Type.MEMORY =>
-            if (location.getStorageInfo.memoryAvailable() && 
MemoryManager.instance().memoryFileStorageAvailable()) {
+            if (!memoryFileStorageReplicaEnabled
+              && location.getMode == PartitionLocation.Mode.REPLICA) {
+              logDebug(s"Skip memory file for replica 
${partitionDataWriterContext.getShuffleKey} 
${partitionDataWriterContext.getPartitionLocation.getFileName}")
+              null
+            } else if (location.getStorageInfo.memoryAvailable() && 
MemoryManager.instance().memoryFileStorageAvailable()) {
               logDebug(s"Create memory file for 
${partitionDataWriterContext.getShuffleKey} 
${partitionDataWriterContext.getPartitionLocation.getFileName}")
               val memoryFileInfo = storageManager.createMemoryFileInfo(
                 partitionDataWriterContext.getAppId,
diff --git 
a/worker/src/test/scala/org/apache/celeborn/service/deploy/worker/storage/storagePolicy/StoragePolicyReplicaSuite.scala
 
b/worker/src/test/scala/org/apache/celeborn/service/deploy/worker/storage/storagePolicy/StoragePolicyReplicaSuite.scala
new file mode 100644
index 0000000000..77bb47524c
--- /dev/null
+++ 
b/worker/src/test/scala/org/apache/celeborn/service/deploy/worker/storage/storagePolicy/StoragePolicyReplicaSuite.scala
@@ -0,0 +1,137 @@
+/*
+ * 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.service.deploy.worker.storage.storagePolicy
+
+import java.io.File
+import java.util.concurrent.atomic.AtomicInteger
+
+import io.netty.buffer.UnpooledByteBufAllocator
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito
+import org.mockito.MockitoSugar.{mock, when}
+
+import org.apache.celeborn.CelebornFunSuite
+import org.apache.celeborn.common.CelebornConf
+import org.apache.celeborn.common.meta.{DiskFileInfo, MemoryFileInfo}
+import org.apache.celeborn.common.metrics.source.AbstractSource
+import org.apache.celeborn.common.protocol.{PartitionLocation, PartitionType, 
StorageInfo}
+import org.apache.celeborn.service.deploy.worker.memory.MemoryManager
+import org.apache.celeborn.service.deploy.worker.storage._
+
+class StoragePolicyReplicaSuite extends CelebornFunSuite {
+  val mockedStorageManager: StorageManager = mock[StorageManager]
+  val mockedSource: AbstractSource = mock[AbstractSource]
+
+  when(
+    mockedStorageManager.createMemoryFileInfo(any(), any(), any(), any(), 
any(), any())).thenAnswer(
+    mock[MemoryFileInfo])
+  
when(mockedStorageManager.storageBufferAllocator).thenAnswer(UnpooledByteBufAllocator.DEFAULT)
+  when(mockedStorageManager.localOrDfsStorageAvailable).thenAnswer(true)
+
+  val mockedDiskFile: DiskFileInfo = mock[DiskFileInfo]
+  when(mockedDiskFile.getStorageType).thenAnswer(StorageInfo.Type.SSD)
+  when(
+    mockedStorageManager.createDiskFile(
+      any(),
+      any(),
+      any(),
+      any(),
+      any(),
+      any(),
+      any(),
+      any())).thenAnswer((mock[LocalFlusher], mockedDiskFile, mock[File]))
+
+  private def createWriter(
+      mode: PartitionLocation.Mode,
+      replicaEnabled: Option[Boolean],
+      evict: Boolean = false): TierWriterBase = {
+    val context = mock[PartitionDataWriterContext]
+    val location = new PartitionLocation(1, 1, "h1", 1, 2, 3, 4, mode)
+    location.setStorageInfo(new StorageInfo(
+      "mountpoint",
+      StorageInfo.Type.SSD,
+      StorageInfo.MEMORY_MASK | StorageInfo.LOCAL_DISK_MASK))
+    when(context.getPartitionLocation).thenAnswer(location)
+    when(context.getPartitionType).thenAnswer(PartitionType.REDUCE)
+    val conf = new CelebornConf()
+    conf.set("celeborn.worker.storage.storagePolicy.createFilePolicy", 
"MEMORY,SSD,HDD,HDFS,OSS,S3")
+    conf.set(CelebornConf.WORKER_STORAGE_EVICT_POLICY.key, "SSD,MEMORY,HDD")
+    replicaEnabled.foreach(v =>
+      conf.set(CelebornConf.WORKER_MEMORY_FILE_STORAGE_REPLICA_ENABLED.key, 
v.toString))
+    val storagePolicy = new StoragePolicy(conf, mockedStorageManager, 
mockedSource)
+    val memoryManager = mock[MemoryManager]
+    when(memoryManager.memoryFileStorageAvailable()).thenReturn(true)
+    val memoryManagerStatic = Mockito.mockStatic(classOf[MemoryManager])
+    try {
+      memoryManagerStatic.when[MemoryManager](() => 
MemoryManager.instance()).thenReturn(
+        memoryManager)
+      if (evict) {
+        val writer = mock[LocalTierWriter]
+        when(writer.storageType).thenAnswer(StorageInfo.Type.SSD)
+        storagePolicy.getEvictedFileWriter(
+          writer,
+          context,
+          new AtomicInteger(),
+          new FlushNotifier)
+      } else {
+        storagePolicy.createFileWriter(context, new AtomicInteger(), new 
FlushNotifier)
+      }
+    } finally {
+      memoryManagerStatic.close()
+    }
+  }
+
+  test("primary partition uses memory file storage") {
+    assert(createWriter(PartitionLocation.Mode.PRIMARY, 
None).isInstanceOf[MemoryTierWriter])
+  }
+
+  test("primary partition uses memory file storage when replica memory storage 
is disabled") {
+    assert(createWriter(PartitionLocation.Mode.PRIMARY, 
Some(false)).isInstanceOf[MemoryTierWriter])
+  }
+
+  test("replica partition uses memory file storage by default") {
+    assert(createWriter(PartitionLocation.Mode.REPLICA, 
None).isInstanceOf[MemoryTierWriter])
+  }
+
+  test("replica partition skips memory file storage when disabled") {
+    assert(createWriter(PartitionLocation.Mode.REPLICA, 
Some(false)).isInstanceOf[LocalTierWriter])
+  }
+
+  test("replica partition uses memory file storage when enabled") {
+    assert(
+      createWriter(PartitionLocation.Mode.REPLICA, 
Some(true)).isInstanceOf[MemoryTierWriter])
+  }
+
+  test("replica eviction can target memory file storage by default") {
+    assert(
+      createWriter(PartitionLocation.Mode.REPLICA, None, evict = true)
+        .isInstanceOf[MemoryTierWriter])
+  }
+
+  test("replica eviction skips memory file storage when disabled") {
+    assert(
+      createWriter(PartitionLocation.Mode.REPLICA, Some(false), evict = true)
+        .isInstanceOf[LocalTierWriter])
+  }
+
+  test("primary eviction can target memory when replica memory storage is 
disabled") {
+    assert(
+      createWriter(PartitionLocation.Mode.PRIMARY, Some(false), evict = true)
+        .isInstanceOf[MemoryTierWriter])
+  }
+}

Reply via email to