Repository navigation
refactor(amber): count a materialized input port as completed once its END is processed - #8938
Conversation
A materialized input port's reader thread enqueues END_CHANNEL and only then flags itself finished. These tests deliver END before that flag flips (and the reverse: the flag set while END is still queued) and expect the port to count as completed exactly when END was processed, so the worker finalizes its output. They fail on main, where isPortCompleted reads the reader thread's flag.
… is processed InputManager.isPortCompleted returned the materialization reader thread's finished flag for a port read from storage. The thread sets that flag only after enqueuing END_CHANNEL, so a DP thread waiting on the queue could run EndChannelHandler first, see the port as not completed and skip finalizeOutput. Nothing retried it: the worker never finished and its region hung (the LoopIntegrationSpec timeouts). A port is now completed when END has been processed on it (WorkerPort.completed, set by EndChannelHandler), the same as a port fed by a link. The startInputPortReaderThreads fixture registers its port first, as addPort always does.
Automated Reviewer SuggestionsBased on the
|
Backport auto-label reportThis
|
|
| config | throughput | MB/s | latency | max Δ latest / 7d | |
|---|---|---|---|---|---|
| 🔴 | bs=10 sw=10 sl=64 | 390 | 0.238 | 25,446/33,752/33,752 us | 🔴 -9.2% / 🔴 +127.5% |
| ⚪ | bs=100 sw=10 sl=64 | 794 | 0.485 | 122,277/157,739/157,739 us | ⚪ within ±5% / 🔴 +56.7% |
| ⚪ | bs=1000 sw=10 sl=64 | 918 | 0.56 | 1,095,180/1,160,050/1,160,050 us | ⚪ within ±5% / 🔴 +21.2% |
Baseline details
Latest main 6a29c59 from same runner
| config | metric | PR | latest main | 7d avg | Δ latest | Δ 7d |
|---|---|---|---|---|---|---|
| bs=10 sw=10 sl=64 | throughput | 390 tuples/sec | 429 tuples/sec | 840.06 tuples/sec | -9.1% | -53.6% |
| bs=10 sw=10 sl=64 | MB/s | 0.238 MB/s | 0.262 MB/s | 0.513 MB/s | -9.2% | -53.6% |
| bs=10 sw=10 sl=64 | p50 | 25,446 us | 23,351 us | 11,813 us | +9.0% | +115.4% |
| bs=10 sw=10 sl=64 | p95 | 33,752 us | 32,381 us | 14,838 us | +4.2% | +127.5% |
| bs=10 sw=10 sl=64 | p99 | 33,752 us | 32,381 us | 17,837 us | +4.2% | +89.2% |
| bs=100 sw=10 sl=64 | throughput | 794 tuples/sec | 821 tuples/sec | 1,087 tuples/sec | -3.3% | -26.9% |
| bs=100 sw=10 sl=64 | MB/s | 0.485 MB/s | 0.501 MB/s | 0.663 MB/s | -3.2% | -26.9% |
| bs=100 sw=10 sl=64 | p50 | 122,277 us | 117,973 us | 94,074 us | +3.6% | +30.0% |
| bs=100 sw=10 sl=64 | p95 | 157,739 us | 152,228 us | 100,688 us | +3.6% | +56.7% |
| bs=100 sw=10 sl=64 | p99 | 157,739 us | 152,228 us | 116,015 us | +3.6% | +36.0% |
| bs=1000 sw=10 sl=64 | throughput | 918 tuples/sec | 923 tuples/sec | 1,128 tuples/sec | -0.5% | -18.6% |
| bs=1000 sw=10 sl=64 | MB/s | 0.56 MB/s | 0.563 MB/s | 0.689 MB/s | -0.5% | -18.7% |
| bs=1000 sw=10 sl=64 | p50 | 1,095,180 us | 1,086,170 us | 916,643 us | +0.8% | +19.5% |
| bs=1000 sw=10 sl=64 | p95 | 1,160,050 us | 1,208,131 us | 957,169 us | -4.0% | +21.2% |
| bs=1000 sw=10 sl=64 | p99 | 1,160,050 us | 1,208,131 us | 978,578 us | -4.0% | +18.5% |
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,512.88,200,128000,390,0.238,25445.79,33752.08,33752.08
1,100,10,64,20,2518.90,2000,1280000,794,0.485,122276.92,157738.96,157738.96
2,1000,10,64,20,21796.76,20000,12800000,918,0.560,1095179.64,1160050.12,1160050.12
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8938 +/- ##
============================================
- Coverage 92.61% 92.61% -0.01%
+ Complexity 5059 5056 -3
============================================
Files 1258 1258
Lines 53780 53770 -10
Branches 6707 6705 -2
============================================
- Hits 49811 49798 -13
- Misses 2313 2316 +3
Partials 1656 1656
*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:
|
…flag After the fix, nothing outside tests reads InputPortMaterializationReaderThread .finished, and it is exactly the signal that must not stand for port completion. Remove it. The tests that read it now check what it stood for: the END_CHANNEL the reader enqueued, or the thread's own state.
carloea2
left a comment
There was a problem hiding this comment.
Retested bb46d20 locally on Windows with JDK 17, Python 3.12 and isolated PostgreSQL databases. All 118 focused engine tests and all 8 LoopIntegrationSpec tests pass. Formatting also passes.
sbt -batch 'WorkflowExecutionService/testOnly *InputManagerSpec *EndChannelHandlerSpec *StartHandlerSpec *DataProcessorSpec *EndHandlerSpec *StartChannelHandlerSpec *AssignPortHandlerSpec *DPThreadSpec *WorkerSpec *InputPortMaterializationReaderThreadSpec -- -oDF' 'scalafmtCheckAll' 'WorkflowExecutionService/testOnly *LoopIntegrationSpec -- -oDF'
This fixes the completion race I reproduced. No competing PR is needed. Backport approvals are still pending.
|
@Xiao-zhen-Liu, required checks now pass on Linux and macOS, and the review thread is resolved. Could you merge this when you have time? I can then refresh #8142 and #8144 to trigger CI with the loop fix. |
Resolves the conflict with apache#8938 in InputManager.isPortCompleted: main counts a port as completed once its END is processed, which subsumes the prototype's extra check, so main's version is kept. The control-variable port's END still marks it completed before EndChannelHandler starts the data readers.
### What changes were proposed in this PR? The Python GlobalPortIdentity decoder now rejects invalid boolean tokens instead of silently treating them as false. Valid mixed-case true and false values remain supported. ### Any related issues, documentation, discussions? Closes apache#8239 ### How was this PR tested? Retested with Python 3.12 on Windows in a fresh detached worktree from upstream main at `7426431337`, which includes the merged loop-completion change apache#8938. Regression before implementation: 2 failed, 48 passed. After implementation: 50 passed. Generate protobufs with `bash bin/python-proto-gen.sh`, then run from `amber`: ```text python -m pytest src/test/python/core/util/test_virtual_identity.py src/test/python/core/storage/test_vfs_uri_factory.py -q --tb=line -p no:cacheprovider python -m ruff check src/main/python src/test/python python -m ruff format --check src/main/python src/test/python ``` Ruff lint and formatting pass for all 217 Python files. ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Codex
What changes were proposed in this PR?
InputManager.isPortCompletednow treats a port read from materialization the same as a port fed by a link: it counts as completed once END_CHANNEL has been processed on it.EndChannelHandlermarks that by settingWorkerPort.completed.Before, such a port reported its reader thread's
finishedflag. The thread sets that flag only after enqueuing END. So a DP thread waiting on the queue, typically the worker that got no tuples, could runEndChannelHandlerbefore the flag was set. It then saw the port as not completed and skippedfinalizeOutput. Nothing called it again, so the worker never finished and its region hung until the test deadline. That is the intermittentTimeoutException: 1.minutes+30.secondsinLoopIntegrationSpec.Two related effects of the change:
startInputPortReaderThreadsuses the same check to skip ports that already completed. The secondStartWorkerof a two-phase region arrives only after the coordinator has the port'sPortCompleted, which the worker sends after processing END. So it skips the same ports as before.finishedflag any more, so it is removed, and it can't stand in for port completion again. The reader-thread tests that read it now check the END_CHANNEL the reader enqueues instead.Any related issues, documentation, discussions?
Closes #8937. The two
LoopIntegrationSpeccases added in #8639 hit the race most often; the race itself dates from #3460.How was this PR tested?
Tests first: the first commit adds the tests and fails on
main, and the second commit makes them pass.InputManagerSpec: a materialized port is completed once END has been processed, even before its reader thread has returned. It is not completed while END is still queued, even after the thread has returned.EndChannelHandlerSpec: when END arrives on a materialized input port before its reader thread has returned, the worker emits the outputFinalizePortandFinalizeExecutor. Onmainit emits only the input port'sFinalizePort, which is the hang.InputPortMaterializationReaderThreadSpecreads real Iceberg tables. Locally only its two cases that don't need storage ran; CI runs the rest.LoopIntegrationSpec, run locally on Windows with the sbt JVM and its Python workers pinned to 3 CPUs (affinity mask0x7), one case per JVM:main$ireference after two branches of the loop body meet againUnpinned, the whole spec passes with this PR: 8 of 8 cases, 29–59 s each, no retries.
Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 5.5)
🤖 Generated with Claude Code