[CDAP-21235] : Introduce Spark Task attempt based record count metrics, buffer and dedup while run and Emit only on task success - #16182
Open
sahusanket wants to merge 1 commit into
Open
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces buffering for pipeline stage metrics in Spark tasks, flushing raw metrics upon successful task completion and adding Spark partition and attempt tags to metric aggregations. The reviewer identified a potential memory leak in SparkRuntimeContext when TaskContext.get() is null (as the thread-local map is populated but never cleared), as well as performance overheads from stream allocations in bufferMetric and redundant getTaskMetrics() calls inside the loop in flushBufferedMetrics.
… buffer and dedup while run and Emit only on task success
sahusanket
force-pushed
the
CDAP-21235_record_count_fix
branch
from
July 2, 2026 12:00
c378dee to
3135494
Compare
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Deduplicate Spark Retry Metrics by Introducing Executor-Side Buffering
1. Context
During CDAP Spark pipeline executions, stages report processing metrics (e.g.
records.inandrecords.out) to the CDAP Master. To facilitate diagnostic monitoring, these are enriched with partition and attempt tags under a.rawsuffix (e.g.user.stage_name.records.out.raw).Currently, when Spark tasks fail and retry, both the failed and successful attempts write their counts immediately to the metrics database. Consequently, database queries using standard
SUMaggregates double-count records processed during failed attempts, causing audit mismatches.2. Approach
We introduce executor-side metrics buffering to separate real-time execution feedback from the final database records:
records.out) bypass the buffer and are sent immediately to preserve real-time UI tracking.ThreadLocalmaps.TaskFailureListenerandTaskCompletionListenerhooks on task startup:TaskContext.get() == nullfirst to prevent thread-local memory leaks on driver and helper threads.Metricsreference (getTaskMetrics()) outside the task flush loop to avoid redundant tag maps and child wrapper instantiations.3. Major Changes
cdap-spark-core-baseThreadLocalMap and Boolean registers to isolate memory buffers across concurrent worker threads..rawmetrics conditionally and clean up ThreadLocal allocations at thread reuse.4. Verification Example
For a partition processing 1,000 records that fails once (processing 5 records) and succeeds on the retry:
Metrics Query payload (
POST /v3/metrics/query){ "qid": { "tags": { "namespace": "default", "app": "fail_retry_records", "run": "d9faaa02-75e5-11f1-b343-d65e96f69bd1" }, "metrics": [ "user.JavaScript.records.out", "user.JavaScript.records.out.raw" ], "aggregate": true, "timeRange": {"startTime": 0, "endTime": "now"} } }Metrics Query Response
The legacy metric double-counts the failure, while the new
.rawmetric is successfully deduplicated:{ "qid": { "series": [ { "metricName": "user.JavaScript.records.out", "data": [{ "time": 0, "value": 1005 }] }, { "metricName": "user.JavaScript.records.out.raw", "data": [{ "time": 0, "value": 1000 }] } ] } }Diagnostic Breakdown payload (by partition & attempt)
{ "qid": { "tags": { "namespace": "default", "app": "fail_retry_records", "run": "d9faaa02-75e5-11f1-b343-d65e96f69bd1" }, "metrics": [ "user.JavaScript.records.out.raw" ], "groupBy": [ "spark_part", "spark_att" ], "timeRange": {"startTime": 0, "endTime": "now"} } }Diagnostic Response
Only successful attempt metrics exist in the series list:
{ "qid": { "series": [ { "metricName": "user.JavaScript.records.out.raw", "grouping": { "spark_part": "0", "spark_att": "1" }, "data": [{ "time": 0, "value": 1000 }] } ] } }