Skip to content

fix(dendrogram): pass the color threshold to scipy as a number - #7234

Merged
aglinxinyuan merged 1 commit into
apache:mainfrom
kz930:fix/7232-dendrogram-threshold
Aug 3, 2026
Merged

fix(dendrogram): pass the color threshold to scipy as a number#7234
aglinxinyuan merged 1 commit into
apache:mainfrom
kz930:fix/7232-dendrogram-threshold

Conversation

@kz930

@kz930 kz930 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Dendrogram's Color Threshold could not be set at all:

  • the field was declared as a string and spliced into the generated Python as a decode expression, so scipy received '3' rather than 3 and raised UFuncTypeError comparing it against the linkage distances
  • the only values that plotted were a blank field and the literal default, and scipy documents those as equivalent — both mean 0.7 × max distance — so nothing a user could type ever changed the coloring

This PR declares the field as Option[Double]:

  • the number is spliced as a literal, so scipy gets a number
  • an unset threshold stays None, the same 0.7 × max distance a blank field already meant
  • @JsonDeserialize(contentAs = ...) names the boxed class: Scala erases Option's element type, so without it Jackson leaves the raw JSON value inside the Option and the first use throws ClassCastException, and the primitive class would read a blank as 0 — every link colored the same rather than "unset"

Compatibility: a numeric string saved earlier still reads as a number, and those workflows were failing before this change anyway. A workflow that stored the literal default no longer loads; clearing the field plots the identical chart.

Any related issues, documentation, discussions?

Fixes #7232.

How was this PR tested?

  • the operator's existing spec updated: a configured threshold is asserted to reach the template as color_threshold=42.5, not as a decoded string
  • deserialization tests for a JSON number, a numeric string, blank, null and absent — plus one that uses the value as a number, the case a round trip cannot catch
  • ran the generated Python against a pandas DataFrame: color_threshold=3.0 plots a figure and so does the unset case, while passing the same value as a string, which is what the operator does today, raises UFuncTypeError
  • whole workflow-operator module: 2020 tests passing, scalafmtCheck clean

Was this PR authored or co-authored using generative AI tooling?

Generated-by: Claude Code (claude-opus-5[1m])

@Yicong-Huang Yicong-Huang added the release/v1.2 back porting to release/v1.2 label Aug 1, 2026
@github-actions
github-actions Bot requested a review from xuang7 August 1, 2026 07:33
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Backport auto-label report

This fix: PR was checked against each actively-supported release branch. release/* labels drive the post-merge backport, so add or remove one to change where this fix lands.

Release branch Analysis
release/v1.2 Change detected on this branch — label added; this fix is queued to backport here. Requested review from @xuang7.

Auto-label run.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @eugenegujing
    You can notify them by mentioning @eugenegujing in a comment.

@codecov-commenter

codecov-commenter commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.08%. Comparing base (348fa88) to head (90a8871).
⚠️ Report is 24 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #7234      +/-   ##
============================================
+ Coverage     80.58%   82.08%   +1.50%     
- Complexity     3890     4078     +188     
============================================
  Files          1162     1162              
  Lines         46264    46302      +38     
  Branches       5153     5159       +6     
============================================
+ Hits          37280    38009     +729     
+ Misses         7319     6584     -735     
- Partials       1665     1709      +44     
Flag Coverage Δ *Carryforward flag
access-control-service 70.00% <ø> (ø)
agent-service 77.42% <ø> (ø) Carriedforward from 348fa88
amber 79.29% <100.00%> (+3.70%) ⬆️
computing-unit-managing-service 43.60% <ø> (+23.11%) ⬆️
config-service 65.97% <ø> (ø)
file-service 66.80% <ø> (ø)
frontend 83.22% <ø> (ø) Carriedforward from 348fa88
notebook-migration-service 78.89% <ø> (ø)
pyamber 97.36% <ø> (ø) Carriedforward from 348fa88
workflow-compiling-service 26.31% <ø> (ø)

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

⚠️ Benchmark changes need a look

🟢 0 better · 🔴 7 worse · ⚪ 8 noise (<±5%) · 0 without baseline

Compared against main 436b37e benchmarked on this same runner, so the delta is largely free of cross-runner hardware noise. The "7d avg" column still reflects the gh-pages dashboard. Treat <±5% as noise unless repeated.

Dashboard · Run

config throughput MB/s latency max Δ latest / 7d
🔴 bs=10 sw=10 sl=64 403 0.246 23,584/35,490/35,490 us 🔴 +20.0% / 🔴 +126.1%
🔴 bs=100 sw=10 sl=64 941 0.574 104,646/133,274/133,274 us 🔴 +16.2% / 🔴 +24.1%
bs=1000 sw=10 sl=64 1,106 0.675 906,259/953,581/953,581 us ⚪ within ±5% / 🟢 -9.9%
Baseline details

Latest main 436b37e from same runner

config metric PR latest main 7d avg Δ latest Δ 7d
bs=10 sw=10 sl=64 throughput 403 tuples/sec 462 tuples/sec 782.98 tuples/sec -12.8% -48.5%
bs=10 sw=10 sl=64 MB/s 0.246 MB/s 0.282 MB/s 0.478 MB/s -12.8% -48.5%
bs=10 sw=10 sl=64 p50 23,584 us 20,943 us 12,467 us +12.6% +89.2%
bs=10 sw=10 sl=64 p95 35,490 us 29,583 us 15,695 us +20.0% +126.1%
bs=10 sw=10 sl=64 p99 35,490 us 29,583 us 18,950 us +20.0% +87.3%
bs=100 sw=10 sl=64 throughput 941 tuples/sec 972 tuples/sec 996.92 tuples/sec -3.2% -5.6%
bs=100 sw=10 sl=64 MB/s 0.574 MB/s 0.593 MB/s 0.608 MB/s -3.2% -5.7%
bs=100 sw=10 sl=64 p50 104,646 us 101,914 us 100,899 us +2.7% +3.7%
bs=100 sw=10 sl=64 p95 133,274 us 114,697 us 107,385 us +16.2% +24.1%
bs=100 sw=10 sl=64 p99 133,274 us 114,697 us 115,226 us +16.2% +15.7%
bs=1000 sw=10 sl=64 throughput 1,106 tuples/sec 1,108 tuples/sec 1,024 tuples/sec -0.2% +8.0%
bs=1000 sw=10 sl=64 MB/s 0.675 MB/s 0.676 MB/s 0.625 MB/s -0.1% +8.0%
bs=1000 sw=10 sl=64 p50 906,259 us 909,902 us 989,300 us -0.4% -8.4%
bs=1000 sw=10 sl=64 p95 953,581 us 965,359 us 1,031,689 us -1.2% -7.6%
bs=1000 sw=10 sl=64 p99 953,581 us 965,359 us 1,058,394 us -1.2% -9.9%
Raw CSV
config_idx,batch_size,schema_width,string_len,num_batches,total_ms,total_tuples,total_bytes,tuples_per_sec,mb_per_sec,lat_p50_us,lat_p95_us,lat_p99_us
0,10,10,64,20,495.89,200,128000,403,0.246,23583.95,35490.07,35490.07
1,100,10,64,20,2125.70,2000,1280000,941,0.574,104646.33,133273.50,133273.50
2,1000,10,64,20,18085.26,20000,12800000,1106,0.675,906259.07,953580.92,953580.92

Color Threshold could not be set at all. The field was declared as a string and
spliced into the generated Python as a decode expression, so scipy received the
Python string '3' rather than 3 and raised UFuncTypeError comparing it against the
linkage distances. The only values that plotted were a blank field and the literal
"default", and scipy documents those as equivalent -- both mean 0.7 * max
distance -- so nothing a user could type ever changed the coloring.

Declaring the field numeric fixes it: the number is spliced as a literal, so scipy
gets a number, and an unset threshold stays None, which is the same 0.7 * max
distance a blank field already meant.

`@JsonDeserialize(contentAs = ...)` is required and names the boxed class on
purpose. Scala erases `Option[Double]`'s element type, so without the annotation
Jackson leaves the raw JSON value inside the Option and the first use throws
ClassCastException; with the primitive class instead, a blank coerces to 0, which
colors every link the same rather than meaning "unset".

Workflows that stored a number keep working -- a numeric string still reads as a
number, and those workflows were failing before this change anyway. A workflow
that stored the literal "default" no longer loads; clearing the field plots the
identical chart.

Fixes apache#7232

Generated-by: Claude Code (claude-opus-5[1m])
@aglinxinyuan
aglinxinyuan added this pull request to the merge queue Aug 3, 2026
Merged via the queue into apache:main with commit 351ce20 Aug 3, 2026
32 checks passed
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Backport PR opened: draft #7291 (#7291) to release/v1.2, assigned to @kz930 — needs manual work because the cherry-pick conflicts.

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

Labels

common fix release/v1.2 back porting to release/v1.2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dendrogram's Color Threshold raises on any numeric value

4 participants