[SPARK-58935][DOCS] Document executorManagement and shared queue metrics of LiveListenerBus - #58216
Conversation
### What changes were proposed in this pull request? `LiveListenerBus` delivers the same logical event to several independent, separately-capacity-limited queues. `ExecutorAllocationManager`'s listener is registered on the `executorManagement` queue, while the listener that drives the Spark UI is registered on a completely separate `appStatus` queue. Each queue independently drops events once its bounded capacity is exceeded, and a dropped event is never redelivered or resynced. If a `SparkListenerStageSubmitted` event is dropped specifically from the `executorManagement` queue, `ExecutorAllocationManager` never learns about that stage's tasks, and its "executors needed" calculation permanently omits them for the lifetime of the stage -- even though the Spark UI/REST API (backed by the unaffected `appStatus` queue) continues to show the stage as `RUNNING` normally. There is currently no way to observe this happening: the only existing signal is a generic, easy-to-miss `WARN` log line shared by every queue, logged at most once per 60 seconds, with no indication of which downstream component is affected. This PR adds `LiveListenerBus.numDroppedExecutorManagementEvents`, which exposes the `executorManagement` queue's dropped-event counter, and a corresponding `ExecutorAllocationManager` delegate method. It registers a new `numDroppedExecutorManagementEvents` gauge on `ExecutorAllocationManagerSource`, matching the existing pattern used by that source's other gauges (e.g. `numberMaxNeededExecutors`), so operators can alert on it and correlate "dynamic allocation stopped requesting executors" with "the executorManagement queue actually dropped an event." A full self-healing fix (e.g., periodically reconciling `ExecutorAllocationManager`'s bookkeeping against the ground-truth stage/task state already tracked by `AppStatusStore`) is a larger, more invasive change that needs broader design discussion. This PR is intentionally scoped to making the problem observable, not to fixing the underlying event-drop behavior. ### Why are the changes needed? Dynamic allocation can silently stop requesting new executors for an application that, from the UI/REST API and logs, looks completely healthy and busy, with no error, warning, or other signal indicating which component was affected. This was observed in production on a long-running Spark Connect driver, where `ExecutorAllocationManager`'s JMX metrics showed `numberMaxNeededExecutors = 0` and `numberTargetExecutors = 0` with pending tasks on an active stage, while the Spark UI simultaneously reported the job as `RUNNING`. The only workaround was restarting the driver process. SPARK-32597 previously identified that event drops in the async listener bus can cause general inconsistent application state, and proposed a more invasive `VariableLinkedBlockingQueue` approach (closed unmerged in 2020). This PR documents a specific, reproducible manifestation of that general class of problem, with a much narrower first fix. SPARK-58446 reports a similarly-surfacing symptom (dynamic allocation stuck at zero needed/target executors), but from a distinct mechanism: a late `TaskStart`/`SpeculativeTaskSubmitted` event arriving after `onStageCompleted` corrupts the pending-task count. That fix does not touch `onStageSubmitted` and would not prevent or fix the issue described here, where `stageAttemptToNumTasks` is never populated for the affected stage attempt in the first place because the `SparkListenerStageSubmitted` event never reaches the listener. ### Does this PR introduce _any_ user-facing change? Yes. A new `numDroppedExecutorManagementEvents` gauge metric is exposed under the `ExecutorAllocationManagerSource` metrics namespace. This PR does not change any existing behavior. ### How was this patch tested? * Added an `ExecutorAllocationManagerSuite` regression test that deterministically forces a `SparkListenerStageSubmitted` event to be dropped from the `executorManagement` queue (by setting its capacity to 1 and occupying its single dispatch thread with a blocking listener), and asserts that `numDroppedExecutorManagementEvents` reflects the drop and that `maxNumExecutorsNeededPerResourceProfile` is left at 0 for that stage, even though it has pending tasks. * Added a `SparkListenerSuite` test verifying, in both directions, that `numDroppedExecutorManagementEvents` tracks drops on the `executorManagement` queue only, and is unaffected by drops on the shared queue. * Ran the full `ExecutorAllocationManagerSuite` (38/38), `SparkListenerSuite` (24/24), and `ExecutorMonitorSuite` (16/16, regression check): all passed. * `scalastyle`: no violations. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Sonnet 5
|
Thank you @HwangDongJun! |
|
Thanks for the detailed writeup and for including a repro test. The failure mode you describe -- the That said, I don't think we need a new metric for it, because the per-queue dropped-event counter is already exported.
and that source ( So It is also strictly less available than the existing one: whereas the What I believe is the actual gap here is documentation. In https://github.com/apache/spark/blob/master/docs/monitoring.md?plain=1#L1327-L1338 If an operator could not find this signal, that omission seems the likely reason. Would you consider re-scoping this PR to fill in those missing lines? That makes exactly the signal you needed discoverable, with no new API surface and no duplicated metric. A few other notes, in case parts of the change are kept:
|
dongjoon-hyun
left a comment
There was a problem hiding this comment.
Please consider the above comment and double-check, @HwangDongJun .
Per @dongjoon-hyun's review, the previously proposed `numDroppedExecutorManagementEvents` gauge duplicated a metric that AsyncEventQueue already registers today (`queue.executorManagement.numDroppedEvents`), under the always-registered `LiveListenerBus` metrics source -- a broader condition than the removed gauge, which was only registered when `spark.dynamicAllocation.enabled=true`. Revert the ExecutorAllocationManager/LiveListenerBus additions and the two new regression tests, and instead fill in the missing `queue.executorManagement.numDroppedEvents.count`, `queue.executorManagement.size`, and `queue.shared.*` entries in `docs/monitoring.md`'s `namespace=LiveListenerBus` list, which is the actual gap: the existing metric was never documented. Generated-by: Claude Sonnet 5
|
Thanks so much for the detailed review, @dongjoon-hyun -- and you're right on every point. I re-scoped the PR per your suggestion: removed the Confirmed the diff against master is now docs-only (5 lines added, no source changes). Thanks again for catching that the signal was already exported -- much better outcome than adding a duplicate metric. |
|
Thank you for the additional review here @dongjoon-hyun, and @HwangDongJun for following up |
executorManagement and shared queue metrics of LiveListenerBus
…etrics of `LiveListenerBus` ### What changes were proposed in this pull request? This PR adds the missing `LiveListenerBus` queue metrics to the `namespace=LiveListenerBus` list in `docs/monitoring.md`: - `queue.executorManagement.numDroppedEvents.count` - `queue.executorManagement.size` - `queue.shared.listenerProcessingTime` (timer) - `queue.shared.numDroppedEvents.count` - `queue.shared.size` ### Why are the changes needed? `AsyncEventQueue` registers `queue.<name>.numDroppedEvents`, `queue.<name>.listenerProcessingTime`, and `queue.<name>.size` for every listener bus queue, and the `LiveListenerBus` metrics source is always registered. The documentation lists all three metrics for the `appStatus` and `eventLog` queues, but stops at `queue.executorManagement.listenerProcessingTime` and omits the `shared` queue entirely. As a result, an already-exported signal such as `<app-id>.driver.LiveListenerBus.queue.executorManagement.numDroppedEvents.count` is not discoverable from the docs, even though it is what an operator needs in order to tell whether the `executorManagement` queue (which drives `ExecutorAllocationManager`) has dropped events. ### Does this PR introduce _any_ user-facing change? No. This is a documentation-only change; no metric is added, removed, or renamed. ### How was this patch tested? Documentation-only change. The added entries were verified against the metric names registered in `AsyncEventQueue`, and follow the existing list's formatting and ordering. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Sonnet 5 Closes #58216 from HwangDongJun/fix/spark-58935-executormanagement-drop-metric. Authored-by: DongjunHwang <dongjun.hwang@hyundai.com> Signed-off-by: Dongjoon Hyun <dongjoon@apache.org> (cherry picked from commit 841f3a0) Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
…etrics of `LiveListenerBus` ### What changes were proposed in this pull request? This PR adds the missing `LiveListenerBus` queue metrics to the `namespace=LiveListenerBus` list in `docs/monitoring.md`: - `queue.executorManagement.numDroppedEvents.count` - `queue.executorManagement.size` - `queue.shared.listenerProcessingTime` (timer) - `queue.shared.numDroppedEvents.count` - `queue.shared.size` ### Why are the changes needed? `AsyncEventQueue` registers `queue.<name>.numDroppedEvents`, `queue.<name>.listenerProcessingTime`, and `queue.<name>.size` for every listener bus queue, and the `LiveListenerBus` metrics source is always registered. The documentation lists all three metrics for the `appStatus` and `eventLog` queues, but stops at `queue.executorManagement.listenerProcessingTime` and omits the `shared` queue entirely. As a result, an already-exported signal such as `<app-id>.driver.LiveListenerBus.queue.executorManagement.numDroppedEvents.count` is not discoverable from the docs, even though it is what an operator needs in order to tell whether the `executorManagement` queue (which drives `ExecutorAllocationManager`) has dropped events. ### Does this PR introduce _any_ user-facing change? No. This is a documentation-only change; no metric is added, removed, or renamed. ### How was this patch tested? Documentation-only change. The added entries were verified against the metric names registered in `AsyncEventQueue`, and follow the existing list's formatting and ordering. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Sonnet 5 Closes #58216 from HwangDongJun/fix/spark-58935-executormanagement-drop-metric. Authored-by: DongjunHwang <dongjun.hwang@hyundai.com> Signed-off-by: Dongjoon Hyun <dongjoon@apache.org> (cherry picked from commit 841f3a0) Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
|
Thank you, @HwangDongJun and @uros-b . |
What changes were proposed in this pull request?
This PR adds the missing
LiveListenerBusqueue metrics to thenamespace=LiveListenerBuslist indocs/monitoring.md:queue.executorManagement.numDroppedEvents.countqueue.executorManagement.sizequeue.shared.listenerProcessingTime(timer)queue.shared.numDroppedEvents.countqueue.shared.sizeWhy are the changes needed?
AsyncEventQueueregistersqueue.<name>.numDroppedEvents,queue.<name>.listenerProcessingTime, andqueue.<name>.sizefor every listener bus queue, and theLiveListenerBusmetrics source is always registered. The documentation lists all three metrics for theappStatusandeventLogqueues, but stops atqueue.executorManagement.listenerProcessingTimeand omits thesharedqueue entirely.As a result, an already-exported signal such as
<app-id>.driver.LiveListenerBus.queue.executorManagement.numDroppedEvents.countis not discoverable from the docs, even though it is what an operator needs in order to tell whether theexecutorManagementqueue (which drivesExecutorAllocationManager) has dropped events.Does this PR introduce any user-facing change?
No. This is a documentation-only change; no metric is added, removed, or renamed.
How was this patch tested?
Documentation-only change. The added entries were verified against the metric names registered in
AsyncEventQueue, and follow the existing list's formatting and ordering.Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Sonnet 5