[CELEBORN-2370] Scope reducer metadata by partition range - #3745
Conversation
|
I'll take a look at this soon. It's an interesting PR. |
There was a problem hiding this comment.
Pull request overview
This PR introduces partition-range scoping for GetReducerFileGroup so Spark executors request/cache only the reducer metadata needed for their [startPartition, endPartition) read range, reducing RPC payload size and executor-side memory/GC pressure on very large shuffles while keeping wire compatibility with legacy drivers/clients.
Changes:
- Extend
GetReducerFileGrouprequest/response (protobuf + transport serde) with optional partition-range fields and anomitMapAttemptsoptimization. - Add executor-side range cache/single-flight loading in
ShuffleClientImpland update Spark reader to request metadata by partition range (bypassing broadcast for scoped requests). - Add/adjust unit and integration tests, plus documentation updates clarifying broadcast applies to legacy full-shuffle responses only.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/spark-it/src/test/scala/org/apache/celeborn/tests/spark/CelebornSortSuite.scala | Updates Spark IT assertion to ensure scoped requests bypass broadcast. |
| tests/spark-it/src/test/scala/org/apache/celeborn/tests/spark/CelebornHashSuite.scala | Same as above for hash shuffle IT. |
| docs/configuration/client.md | Clarifies broadcast configs apply only to legacy shuffle-wide requests. |
| common/src/test/scala/org/apache/celeborn/common/util/UtilsSuite.scala | Adds round-trip serde tests for new request/response fields and interruption test. |
| common/src/main/scala/org/apache/celeborn/common/util/Utils.scala | Preserves interruption during retry backoff by restoring interrupt status and throwing InterruptedException. |
| common/src/main/scala/org/apache/celeborn/common/protocol/message/ControlMessages.scala | Adds transport (de)serialization for new partition-range fields. |
| common/src/main/scala/org/apache/celeborn/common/CelebornConf.scala | Updates config docs to note broadcast is legacy-only. |
| common/src/main/proto/TransportMessages.proto | Extends protobuf messages for range scoping and omitMapAttempts. |
| client/src/test/scala/org/apache/celeborn/client/commit/ReducerFileGroupFilterSuite.scala | Adds tests for reducer metadata filtering by partition range. |
| client/src/test/java/org/apache/celeborn/client/ShuffleClientSuiteJ.java | Adds extensive concurrency/range-cache tests for ShuffleClientImpl.updateFileGroup behavior. |
| client/src/main/scala/org/apache/celeborn/client/LifecycleManager.scala | Plumbs range fields through the driver endpoint and validates range inputs. |
| client/src/main/scala/org/apache/celeborn/client/CommitManager.scala | Passes new range parameters through to commit handlers. |
| client/src/main/scala/org/apache/celeborn/client/commit/ReducerFileGroupFilter.scala | Implements server-side filtering helpers for file groups/partition IDs/failed batches. |
| client/src/main/scala/org/apache/celeborn/client/commit/ReducePartitionCommitHandler.scala | Builds/scopes reducer metadata responses and bypasses broadcast for scoped requests. |
| client/src/main/scala/org/apache/celeborn/client/commit/MapPartitionCommitHandler.scala | Applies range filtering for mapper-side handler responses and supports omitting map attempts. |
| client/src/main/scala/org/apache/celeborn/client/commit/CommitHandler.scala | Extends handler API to accept range + omit flags. |
| client/src/main/java/org/apache/celeborn/client/ShuffleClientImpl.java | Adds executor-side range cache with in-flight sharing, cleanup safety, and old-driver fallback. |
| client/src/main/java/org/apache/celeborn/client/ShuffleClient.java | Adds a range overload (default implementation) for updateFileGroup. |
| client/src/main/java/org/apache/celeborn/client/DummyShuffleClient.java | Implements the new range overload as a no-op for dummy client. |
| client-spark/spark-3/src/main/scala/org/apache/spark/shuffle/celeborn/CelebornShuffleReader.scala | Switches Spark reader to request reducer metadata for the actual partition range. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Thanks @afterincomparableyum and @SteNicholas! We ran into this issue internally. In our case, a Spark job with an extremely large number of reducers (over 1 million) caused the Spark driver to become stuck for hours without making any progress. |
|
This PR is stale because it has been open 20 days with no activity. Remove stale label or comment or this will be closed in 10 days. |
…in updateFileGroup updateFileGroup loaded the file group via reduceFileGroupsMap.compute(), which holds ConcurrentHashMap's per-key bin lock for the whole remapping function. So the first reduce task of a shuffle held the bin lock while doing the blocking GetReducerFileGroup round-trip to the LifecycleManager, and every other reduce task for that shuffleId blocked on the same bin lock, even cache hits. On a large shuffle this convoyed the entire stage for minutes with no fetch activity. Move the RPC off the bin lock: a lock-free get() fast path for cache hits, and a per-shuffle monitor (double-checked) that serializes cold loads to a single RPC. Caching semantics are unchanged (a failed load caches null and reloads next call). Flink overrides updateFileGroup and is unaffected; the Spark client uses the base ShuffleClientImpl and was exposed. Standalone convoy fix extracted from apache#3687 (closed), independent of the broader partition-range metadata optimization in apache#3745 (CELEBORN-2370). Co-authored-by: Chao Sun <sunchao@apache.org>
|
@sunchao, please rebase the latest main branch for stable CI. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Suppressed comments (1)
client/src/main/scala/org/apache/celeborn/client/LifecycleManager.scala:996
- The driver-side range validation rejects
endPartition == startPartition(endPartition <= startPartition), but the client-side range API treats an empty[start, end)interval as valid and returns an empty result without loading metadata. Rejecting empty ranges makes the RPC contract less robust/inconsistent (e.g., if another client forwards an empty range to the driver) and turns a well-defined query intoREQUEST_FAILED.
Consider allowing empty ranges by treating only endPartition < startPartition as invalid (still rejecting negative starts). The commit handlers’ range filtering already naturally returns an empty map for an empty interval.
if (hasPartitionRange && (startPartition < 0 || endPartition <= startPartition)) {
logWarning(
s"Reject invalid reducer file group partition range [$startPartition, $endPartition) " +
s"for shuffle $shuffleId")
context.reply(
9bd64bc to
ec8b88d
Compare
|
Thanks @SteNicholas . Just did it. |
| hasPartitionRange, | ||
| omitMapAttempts, | ||
| serdeVersion) | ||
| } else { | ||
| getReducerFileGroupRequest.get(shuffleId).add(new MultiSerdeVersionRpcContext( | ||
| context, | ||
| serdeVersion)) | ||
| serdeVersion, | ||
| startPartition, | ||
| endPartition, | ||
| hasPartitionRange, | ||
| omitMapAttempts)) |
JIRA: CELEBORN-2370
Supersedes #3687.
Why are the changes needed?
Before a Spark reducer can read shuffle data, Celeborn calls
GetReducerFileGroupto obtain reducer file locations and related metadata.Today that response is shuffle-wide. For a shuffle with
Nreducers, it contains metadata for allNreducers, even though a Spark task normally reads only[startPartition, endPartition).For example:
[42, 43); butThe response is cached independently on every executor, so metadata transfer and executor memory grow with the total reducer count multiplied by the number of executors, rather than with the reducer ranges those executors actually read. On very large shuffles this can cause:
The driver still needs to retain complete shuffle commit metadata. This PR reduces the metadata transferred to and cached by each executor.
What changes were proposed in this pull request?
Add optional partition-range fields to
GetReducerFileGrouprequests and responses.When Spark reads a shuffle:
[startPartition, endPartition)range.The executor cache also:
Partition-scoped responses bypass the existing shuffle-wide RPC cache and Spark broadcast path. Legacy full-shuffle requests continue to use the existing cache and broadcast behavior.
This PR changes the Spark client and the Spark driver-side lifecycle/commit endpoint. It does not change the Celeborn Master or Worker.
Does this PR introduce any user-facing change?
No configuration or public API change is required.
When both the Spark client and driver contain this change, reducer metadata transfer and executor cache size become proportional to the partition ranges read by that executor instead of the total reducer count.
The wire protocol remains backward compatible:
Mixed-version deployments remain correct, but the optimization applies only when the driver supports scoped responses.
How was this patch tested?
ShuffleClientSuiteJ: 29 tests covering range caching, concurrent loads, cleanup races, interruption, and old-driver fallbackUtilsSuite: 28 tests, including V1 and V2 protocol round tripsReducerFileGroupFilterSuite: 2 range-filtering testsConfigurationSuite: 8 testsCelebornHashSuiteandCelebornSortSuite(4 tests)