Skip to content

[CELEBORN-2415] Add Celeborn Spark UI extension - #3793

Open
yew1eb wants to merge 2 commits into
apache:mainfrom
yew1eb:celeborn-ui-mvp
Open

[CELEBORN-2415] Add Celeborn Spark UI extension#3793
yew1eb wants to merge 2 commits into
apache:mainfrom
yew1eb:celeborn-ui-mvp

Conversation

@yew1eb

@yew1eb yew1eb commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

Add a new module celeborn-client-spark-3-ui — a Spark UI extension that renders a "Celeborn" tab in both the live Spark UI and the History Server (enabled via spark.plugins=org.apache.spark.shuffle.celeborn.CelebornPlugin, supports Spark 3.x and 4.x).

This is an initial version that lays the foundation; it intentionally starts minimal. The page shows:

  1. Summary — shuffle write/read bytes, time, speed, and shuffle-vs-task duration ratio, aggregated from Spark's built-in TaskMetrics
  2. Celeborn Properties — a collapsible table of spark.celeborn.* configs

The skeleton is: CelebornPlugin (live UI) / CelebornHistoryServerPlugin (SHS, via AppHistoryServerPlugin SPI) → listener aggregates into Spark KVStoreCelebornStatusStoreCelebornUITab renders; a javax/jakarta servlet bridge covers Spark 3.x and 4.x. This plugin/store/render pipeline is designed so that fine-grained Celeborn client metrics can be added as collapsible sections in follow-up PRs:

  • Shuffle Write Times — serialize / copy / queue wait / compress / queue stall / inflight wait / drain wait / max push RTT / slow push
  • Shuffle Read Times — chunk wait / decompress / retry wait / max chunk RTT / slow chunk
  • Shuffle Write Servers — per-worker push bytes, push count, RTT, soft/hard splits, congested counts, last failure reason
  • Shuffle Read Servers — per-worker chunk count, read bytes, total/max RTT
  • Shuffle Assignments — partition-to-worker slot assignments

Why are the changes needed?

Spark UI already shows stage/task-level shuffle metrics, but nothing Celeborn-specific: which workers served the data, push/chunk latencies, congestion and split events are invisible there, while Celeborn's own metrics sit in cluster-level Grafana dashboards that cannot be attributed back to a specific application. This tab closes that gap by putting per-application Celeborn shuffle information inside the Spark UI and History Server, alongside the job's stages and tasks.

Does this PR introduce any user-facing change?

Yes.
Setting spark.plugins=org.apache.spark.shuffle.celeborn.CelebornPlugin (with the extension jar on the classpath) adds a "Celeborn" tab to the Spark UI and History Server. Disabled by default; no effect otherwise.

Does this PR resolve a correctness bug?

No.

How was this patch tested?

Manual end-to-end verification:

image

@yew1eb
yew1eb marked this pull request as draft August 10, 2026 11:30
@yew1eb
yew1eb marked this pull request as ready for review August 10, 2026 13:38
@yew1eb
yew1eb force-pushed the celeborn-ui-mvp branch 2 times, most recently from 4f8bad3 to 68fc2f2 Compare August 10, 2026 13:40
@yew1eb
yew1eb marked this pull request as draft August 10, 2026 13:48
@yew1eb
yew1eb force-pushed the celeborn-ui-mvp branch 6 times, most recently from 7a9e389 to 47f3676 Compare August 10, 2026 16:27
@yew1eb yew1eb closed this Aug 10, 2026
@yew1eb yew1eb reopened this Aug 10, 2026
@yew1eb
yew1eb marked this pull request as ready for review August 10, 2026 17:30
@yew1eb

yew1eb commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@cxzl25
cxzl25 requested a lite review from Copilot August 18, 2026 10:34

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds a new Spark UI / History Server extension module that surfaces Celeborn-related shuffle summary metrics and spark.celeborn.* configuration in a dedicated “Celeborn” tab.

Changes:

  • Introduces new module client-spark/spark-3-ui with Spark plugin + History Server plugin, status-store access, and UI rendering.
  • Adds a Spark 3 vs Spark 4 servlet (javax vs jakarta) bridge via profile-selected source roots.
  • Wires the new UI module into root build and shaded Spark client artifacts.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
pom.xml Adds the new UI module to Spark build profiles and introduces servlet.source.dir to select Spark3/Spark4 servlet bridge sources.
client-spark/spark-3-ui/pom.xml New module POM with Spark dependency + servlet APIs and build-helper source-root selection.
client-spark/spark-4-shaded/pom.xml Includes the new UI artifact in the Spark 4 shaded assembly dependencies.
client-spark/spark-3-shaded/pom.xml Includes the new UI artifact in the Spark 3 shaded assembly dependencies.
client-spark/spark-3-ui/src/main/scala/org/apache/spark/shuffle/celeborn/CelebornPlugin.scala Adds SparkPlugin entrypoint for live UI, registers listener and attaches tab.
client-spark/spark-3-ui/src/main/scala/org/apache/spark/shuffle/celeborn/CelebornHistoryServerPlugin.scala Adds SHS plugin SPI implementation to replay metrics and attach the tab.
client-spark/spark-3-ui/src/main/scala/org/apache/spark/shuffle/celeborn/CelebornListener.scala Listener aggregates task metrics and persists rollups + properties into KVStore.
client-spark/spark-3-ui/src/main/scala/org/apache/spark/shuffle/celeborn/CelebornStatusStore.scala Status-store accessors for aggregated metrics and celeborn properties.
client-spark/spark-3-ui/src/main/scala/org/apache/spark/shuffle/celeborn/ui/CelebornUITab.scala Defines the “Celeborn” tab and registers the page.
client-spark/spark-3-ui/src/main/scala/org/apache/spark/shuffle/celeborn/ui/CelebornShufflePage.scala Renders summary metrics + collapsible properties table.
client-spark/spark-3-ui/src/main/scala-spark3/org/apache/spark/shuffle/celeborn/ui/SparkServletBridge.scala Spark 3 servlet type alias (javax.servlet).
client-spark/spark-3-ui/src/main/scala-spark4/org/apache/spark/shuffle/celeborn/ui/SparkServletBridge.scala Spark 4 servlet type alias (jakarta.servlet).
client-spark/spark-3-ui/src/main/resources/META-INF/services/org.apache.spark.status.AppHistoryServerPlugin Registers the SHS plugin via SPI.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +35 to +41
private[celeborn] class CelebornPropertiesUIData(
val info: Seq[(String, String)]) {

@JsonIgnore
@KVIndex
def id: String = classOf[CelebornPropertiesUIData].getName
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary. Verified end-to-end that the properties table renders correctly in both live UI and SHS replay. Spark's KVStore uses Jackson with the Scala

Comment on lines +39 to +41
if (statusStore.hasData()) {
new CelebornUITab(statusStore, ui)
}

@yew1eb yew1eb Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point on the live/SHS inconsistency. Fixed by attaching the tab unconditionally in SHS (matching the live UI, which always attaches once spark.plugins=CelebornPlugin is set) and removing the now-unused hasData().

private val totalTaskDurationMs = new AtomicLong(0L)

private val lastUpdateTimestamp = new AtomicLong(-1L)
private val updateIntervalMillis = 5000L

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable for later, but intentionally a fixed default for the MVP. We can expose spark.celeborn.ui.flushIntervalMs in a follow-up once we see real cadence
needs.

Comment on lines +79 to +85
val last = lastUpdateTimestamp.get()
if (!force && (last != -1L && (now - last) < updateIntervalMillis)) {
return
}
if (lastUpdateTimestamp.compareAndSet(last, now) || force) {
flushAggregations()
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Impact is negligible in practice: force=true is only reached from onApplicationEnd, which fires once at app end with no concurrent onTaskEnd. No extra flushes follow it. Leaving as-is.

Comment on lines +53 to +57
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional. A single module pom with ${servlet.source.dir} selecting the source root keeps the build simple; both servlet APIs are provided and only the
one matching the selected source root is ever referenced at compile time. Splitting into per-profile dependencies would duplicate the dependency block
across every Spark profile without real benefit.

Comment thread pom.xml
<module>client-spark/spark-3</module>
<module>client-spark/spark-3-columnar-common</module>
<module>client-spark/spark-3-columnar-shuffle</module>
<module>client-spark/spark-3-ui</module>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name follows the existing celeborn-client-spark-3 convention in this repo, which also serves Spark 4. Renaming would diverge from that. Keeping spark-3-ui for consistency.

Comment on lines +106 to +114
<span class="collapse-aggregated-celebornProperties collapse-table"
onClick="collapseTable('collapse-aggregated-celebornProperties',
'aggregated-celebornProperties')">
<h4>
<span class="collapse-table-arrow arrow-open"></span>
<a>Celeborn Properties</a>
</h4>
</span>
<div class="aggregated-celebornProperties collapsible-table">

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Following Spark's own UI convention here — Spark uses camelCase collapse identifiers (e.g. collapse-aggregated-allActiveStages). The collapseTable() JS
accepts arbitrary ids as long as the collapse-* prefix and the target class match, which they do. No change.

setupUI() no longer gates on hasData(); the Celeborn tab is now always
attached in the History Server, matching the live UI behavior where the
tab is attached unconditionally once spark.plugins=CelebornPlugin is set.
Removes the now-unused CelebornStatusStore.hasData().
@yew1eb yew1eb closed this Aug 18, 2026
@yew1eb yew1eb reopened this Aug 18, 2026
@zaynt4606

Copy link
Copy Markdown
Contributor

ping @Kalvin2077

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants