DanielLeens commented on PR #10941:
URL: https://github.com/apache/seatunnel/pull/10941#issuecomment-4528174638

   Thanks for the fix. I pulled the latest head locally and traced the real 
runtime path.
   
   # What This PR Fixes
   - User pain: when the same upstream `Record<SeaTunnelRow>` is fanned out 
into sibling transform branches, `ReplaceTransform` mutates the shared row in 
place, so one branch can unexpectedly change what the other branch sees.
   - Fix approach: `ReplaceTransform.transformRow(...)` now works on 
`inputRow.copy()` and returns the copied row instead of mutating the original 
shared row.
   - One-line summary: this is a precise fix for a real fan-out data-sharing 
bug, and it lands at the correct layer.
   
   Simple example:
   - Before this change, branch A could replace `title` first, and branch B 
would then read the already modified value from the same input row.
   - After this change, each `ReplaceTransform` output is derived from its own 
copied row, so sibling branches still see the original upstream row.
   
   # Runtime Chain Rechecked
   ```text
   Upstream row fan-out
     -> SeaTunnelTransformCollector.collect(record) 
[seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/SeaTunnelTransformCollector.java:35-41]
         -> forwards the same Record instance to every downstream output
   
   Transform execution
     -> TransformFlowLifeCycle.received(record) 
[seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/flow/TransformFlowLifeCycle.java:117-126]
         -> passes record.getData() into transform(...)
     -> TransformFlowLifeCycle.transform(inputData) 
[TransformFlowLifeCycle.java:138-162]
         -> each map transform receives the same SeaTunnelRow reference for 
that branch input
   
   Replace transform
     -> ReplaceTransform.transformRow(inputRow) 
[seatunnel-transforms-v2/src/main/java/org/apache/seatunnel/transform/replace/ReplaceTransform.java:125-136]
         -> old behavior: mutate inputRow directly
         -> new behavior: copy first, mutate the copy, return the copy
   ```
   
   # Key Findings
   - The bug is real because the collector fans out the same `Record` object to 
multiple downstream outputs.
   - The normal path does hit this change whenever `ReplaceTransform` is used 
in a shared-row fan-out topology.
   - I do not think this should be pushed down into `TransformFlowLifeCycle` as 
a blanket copy for all transforms. That would add unconditional per-row copy 
overhead even for transforms that are read-only or already allocate fresh 
output rows. The current fix is more targeted and matches the pattern already 
used by mutating transforms like `FieldEncryptTransform` and 
`TableRenameTransform`.
   - The new test in `ReplaceTransformTest.java:272-304` covers the real 
regression shape: two sibling transform branches sharing one input row.
   
   # Findings
   No blocking issue found in the current head.
   
   # Merge Decision
   ### Conclusion: can merge
   
   1. Blocking items
   - None.
   
   2. Suggested follow-up
   - The `Build` check was still running when I reviewed. Please just let that 
finish green.
   
   Overall, this looks like a clean and correctly scoped fix. Nice catch on the 
shared-row mutation behavior.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to