Repository navigation
fix(workflow-compiler): anchor the codegen failure marker - #9215
eugenegujing wants to merge 1 commit into
Conversation
Match the `#EXCEPTION DURING CODE GENERATION:` marker only at the start of a Python-based operator's code in WorkflowCompiler, and let the captured message span several lines. Report an exception with a null or empty message by its type in PythonOperatorDescriptor instead of the literal `null`. Add WorkflowCompilerSpec tests for a Python UDF, an R UDF and a generated operator that mention the marker in their own code, for a Python UDF whose code starts with the marker, for a failure with no message and for LF and CRLF multi-line failure messages, and PythonOperatorDescriptorSpec tests for the null and empty message cases.
Automated Reviewer SuggestionsBased on the
|
Backport auto-label reportThis
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9215 +/- ##
============================================
+ Coverage 91.98% 92.05% +0.06%
+ Complexity 5083 5040 -43
============================================
Files 1285 1286 +1
Lines 56094 55911 -183
Branches 7137 7137
============================================
- Hits 51599 51467 -132
+ Misses 2742 2693 -49
+ Partials 1753 1751 -2
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
| config | throughput | MB/s | latency | max Δ latest / 7d | |
|---|---|---|---|---|---|
| 🔴 | bs=10 sw=10 sl=64 | 534 | 0.326 | 17,600/25,604/25,604 us | 🔴 +20.5% / 🔴 +82.5% |
| ⚪ | bs=100 sw=10 sl=64 | 1,203 | 0.734 | 82,381/111,734/111,734 us | ⚪ within ±5% / 🔴 +16.1% |
| 🔴 | bs=1000 sw=10 sl=64 | 1,451 | 0.885 | 675,201/803,224/803,224 us | 🔴 +5.5% / 🟢 -23.1% |
Baseline details
Latest main f673554 from same runner
| config | metric | PR | latest main | 7d avg | Δ latest | Δ 7d |
|---|---|---|---|---|---|---|
| bs=10 sw=10 sl=64 | throughput | 534 tuples/sec | 631 tuples/sec | 905.73 tuples/sec | -15.4% | -41.0% |
| bs=10 sw=10 sl=64 | MB/s | 0.326 MB/s | 0.385 MB/s | 0.553 MB/s | -15.3% | -41.0% |
| bs=10 sw=10 sl=64 | p50 | 17,600 us | 14,830 us | 11,331 us | +18.7% | +55.3% |
| bs=10 sw=10 sl=64 | p95 | 25,604 us | 21,254 us | 14,032 us | +20.5% | +82.5% |
| bs=10 sw=10 sl=64 | p99 | 25,604 us | 21,254 us | 17,338 us | +20.5% | +47.7% |
| bs=100 sw=10 sl=64 | throughput | 1,203 tuples/sec | 1,217 tuples/sec | 1,205 tuples/sec | -1.2% | -0.1% |
| bs=100 sw=10 sl=64 | MB/s | 0.734 MB/s | 0.743 MB/s | 0.735 MB/s | -1.2% | -0.2% |
| bs=100 sw=10 sl=64 | p50 | 82,381 us | 79,622 us | 89,237 us | +3.5% | -7.7% |
| bs=100 sw=10 sl=64 | p95 | 111,734 us | 112,982 us | 96,280 us | -1.1% | +16.1% |
| bs=100 sw=10 sl=64 | p99 | 111,734 us | 112,982 us | 111,344 us | -1.1% | +0.4% |
| bs=1000 sw=10 sl=64 | throughput | 1,451 tuples/sec | 1,441 tuples/sec | 1,239 tuples/sec | +0.7% | +17.1% |
| bs=1000 sw=10 sl=64 | MB/s | 0.885 MB/s | 0.88 MB/s | 0.756 MB/s | +0.6% | +17.1% |
| bs=1000 sw=10 sl=64 | p50 | 675,201 us | 685,007 us | 877,826 us | -1.4% | -23.1% |
| bs=1000 sw=10 sl=64 | p95 | 803,224 us | 761,128 us | 919,412 us | +5.5% | -12.6% |
| bs=1000 sw=10 sl=64 | p99 | 803,224 us | 761,128 us | 963,000 us | +5.5% | -16.6% |
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,374.72,200,128000,534,0.326,17600.37,25603.95,25603.95
1,100,10,64,20,1663.16,2000,1280000,1203,0.734,82381.05,111734.27,111734.27
2,1000,10,64,20,13787.72,20000,12800000,1451,0.885,675201.38,803223.61,803223.61
What changes were proposed in this PR?
Root cause. A
PythonOperatorDescriptorwhose code generation throws replaces its whole code with the line#EXCEPTION DURING CODE GENERATION: <message>(PythonOperatorDescriptor.scala:29), andWorkflowCompilerturns that marker back into a per-operator COMPILATION_ERROR. The compiler's regex was unanchored, so it matched the marker text anywhere in the code of every Python-based operator, including Python and R UDFs whose code is the user's own script. A UDF that mentioned the marker text in a comment or a string was rejected, the physical plan was dropped, and the reported error was the rest of the user's comment line.Before (local main 92e86d4, workflow Text Input → Python UDF with the NOTE comment on line 3).
The run button reads "Invalid Workflow" and the Static Error tab reports
COMPILATION_ERROR: Operator is not configured properly: this is only a comment.The UDF code editor with the comment on line 3, next to the fabricated Static Error.
Control: a copy of the workflow with the same UDF minus the comment compiles, no Static Error tab, run button reads "Run".
After (this branch, the same workflow with the comment still on line 3).
The commented UDF compiles: run button reads "Run", the result panel has only the Console tab, no Static Error.
The code editor still shows the marker text in the line-3 comment while the run button reads "Run".
Changes.
WorkflowCompiler.scala: the regex becomes(?s)^#EXCEPTION DURING CODE GENERATION:\s*(.*).^restricts the match to the start of the code, which is the only place the marker is ever written;(?s)lets the captured message include line breaks, so a multi-line generation failure is reported in full instead of its first line.PythonOperatorDescriptor.scala: an exception whose message isnullor empty is rendered asex.toString(its class name, plus:for the empty case) instead of the literalnullor nothing.Scope. The in-band marker design itself is left as is; moving the failure out of the code string would touch
PhysicalOpand every consumer and is out of scope here.Any related issues, documentation, discussions?
Closes #9213
How was this PR tested?
Added to
WorkflowCompilerSpec(Python code-generation error path section):PythonUDFOpDescV2whose script contains the marker in a comment compiles with no error and keeps its physical plan (the test also asserts the marker text did reach the check);PythonOperatorDescriptorwhose generated code mentions the marker mid-code compiles with no error;nullmessage is reported asOperator is not configured properly: java.lang.RuntimeException;\nand\r\nline endings;RUDFOpDesc) whose code mentions the marker in a comment compiles with no error and keeps its physical plan.Added to
PythonOperatorDescriptorSpec: the embedded marker for an exception without a message and for one with an empty message (#EXCEPTION DURING CODE GENERATION: java.lang.RuntimeException:). The existing tests for real generation failures (unconfigured Sort, padded message) are unchanged and still pass.Operator is not configured properly: this is only a commentwithphysicalPlan = None.filter(_.nonEmpty)fallback was added.filter(_.nonEmpty)fails exactly the empty-message test (4 passed / 1 failed, compiler spec 41 / 0). Restored afterwards.scalafmtCheck,Test/scalafmtCheck,scalafix --checkandTest/scalafix --checkonWorkflowCompilerandWorkflowOperator: all pass.Was this PR authored or co-authored using generative AI tooling?
Yes. Generated-by: Claude Code (claude-fable-5-1). Co-authored with Claude; the author reviewed the change before submission.