python/etl: classify direct-put responses by Ais-Direct-Put-Complete marker#334
python/etl: classify direct-put responses by Ais-Direct-Put-Complete marker#334chanu1406 wants to merge 1 commit into
Conversation
| return resp.status_code, resp.content, 0 | ||
|
|
||
| return STATUS_NO_CONTENT, b"", size # from target, no content | ||
| # Keyed on the Content-Length header, mirroring the Go webserver's |
There was a problem hiding this comment.
This fixes the original issue for a 2-stage same-target pipeline, but not for 3+ stages.
The final streaming ETL can return 200 with an empty chunked body and no Content-Length, meaning the transformed object is valid but empty. The previous ETL now recognizes that correctly. However, when it forwards the result, the Python server turns it into a buffered empty response with Content-Length: 0. If there is another ETL upstream, that ETL sees Content-Length: 0, assumes the object was already direct-put to the target, and returns 204. In the same-target case no direct put actually happened, so AIS skips writing the empty object.
With only 2 stages, the rewritten 200 goes directly back to AIS, which accepts it as an empty object, so that case works. This is a partial fix to an existing bug, not a new regression. We still need to preserve the unknown/chunked-length state when forwarding the empty response and add a 3-stage test.
This specifically affects same-target pipelines, where the source and destination resolve to the same AIS target. In that case no final direct PUT occurs, so the incorrect 204 causes AIS to skip the actual write.
can you please debug this @chanu1406, its slightly more complex that it seems. We are mixing streaming + pipeline + direct put which is creating a complex situation. If possible add a test but its difficult because how would you know before hand whether on which target the object will be stored. Lmk if you need help
|
@gaikwadabhishek Thanks for the review, this is a valuable finding can you let me know what you think of this approach for the python servers: The issue is the middle stage classifies the empty chunked 200 correctly, but then re-emits it as a buffered empty response. All three Python servers stamp Approach: for transform-content responses, the Python servers should never emit
This keeps For coverage, |
|
@Nahemah1022 thoughts? |
|
@gaikwadabhishek @Nahemah1022 I looked into this issue a bit further, and had some findings. The problem with my earlier idea is that whether a response is chunked or has However the stage already knows what it just sent the PUT to. ETL pipeline entries are host-only URLs, while the destination target URL carries the object path —
Still the same 3 stage regression test plan as before. The failing to_target tests pass again without touching their mocks, and I'll switch the ETL hop URLs in the existing tests to host-only so they match what AIS actually sends. The Go webserver has the same issue on its side, can follow up on that as well. Let me know what you think |
|
I think the core problem is the ambiguity around whether a @chanu1406 You’re right that inspecting the URL could help distinguish between them, but I’m concerned that this would introduce another branch of logic to maintain on top of the already complicated pipeline logic. I wonder whether it would make our lives easier if we updated the AIS target to return This seems like it could remove the ambiguity entirely: ETL responses would always use This is just an idea. @chanu1406 Could you confirm whether this approach would work better based on your understanding of the core issue? |
|
@Nahemah1022 After looking into it, I agree this is a cleaner approach and it should work well. The target currently returns the 200 implicitly after a successful The target would return However, while tracing, I found that there may be a compatibility issue. A webserver using the new semantics but talking to an older target would treat the target’s legacy bare 200 acknowledgment as empty ETL output. In a different-target pipeline, AIS would then send that empty result to the destination and overwrite the object that was just direct-put. To avoid that, either all targets must be upgraded before deploying webservers with the new behavior, or we keep a |
|
@chanu1406 I think we can still keep the backward-compatibility handling very small without introducing URL-based logic. Can we add a behavior-based response header whose presence indicates that a The shared response handler in both Go and Python could then have a clean if-else branch:
Every updated ETL webserver should add this marker to successful This provides a clean compatibility path:
Once the new behavior has been deployed and stabilized, we can remove the legacy fallback. Would this fit cleanly into the current Go and Python implementations? |
|
@Nahemah1022 Sounds good, it should fit cleanly into both implementations.
I can work on the PR with this approach. Do you prefer the target side 204 change and the Go webserver work to go in this PR as well, or separate it? |
|
@chanu1406 Thanks, that sounds good. For the header name, I prefer The target should set it alongside The compatibility handling can then remain very small: This keeps the compatibility path isolated. Around the next major release or so, we can deprecate and gradually phase out the legacy handling. I also think the Go and Python changes should be split into separate PRs, with their own focused testing. The target-side |
|
@Nahemah1022 Sounds good, I'll start working on it Also, the case with the empty transform result may not be fully fixed yet with this. When a middle stage buffers it, it comes out as 200 + Content-Length: 0, so the legacy handling will read it as delivered. This can only get resolved once the legacy fallback is removed, so I'll leave out the empty-output regression test that I mentioned earlier. |
c2d0a91 to
21926ed
Compare
|
Add a brief desc of the changes and important things in this PR (in bullets). Check other commits for suggestions |
…marker * the AIS target acks a successful direct PUT with 204 + Ais-Direct-Put-Complete + Ais-Direct-Put-Length; webservers classify by marker presence and propagate the ack back through the pipeline - status, marker, and length (including 0 for an empty stored object) * markerless responses fall through to the existing Content-Length-keyed handling, kept as a legacy fallback for targets that predate the marker * handle_direct_put_response now returns (status, body, length, complete); all three servers (FastAPI, Flask, HTTP) thread the flag through the buffered, streaming, and websocket paths * tests: marker classification, multi-stage ack propagation (incl. zero-length), legacy-target fallback; target-ack mocks updated to the new contract * Python webservers only; target-side 204 + Go webserver handling in a follow-up PR Signed-off-by: Chanu Ollala <chanuollala@gmail.com>
21926ed to
d2a3aad
Compare
@gaikwadabhishek amended the commit |
|
@chanu1406 Thanks for updating this. Can we treat this PR as blocked on the target/Go changes and land those first? Without the target returning |
Sounds good, I will open a separate PR once I finish implementing |
|
Moving this MR to draft as Python side changes look complete, but Go (server side) changes need to land first. |
Follow up to #330
Reworking per the review discussion. classify direct-put responses by the new
Ais-Direct-Put-Completemarker instead of the read body.