aicam opened a new pull request, #8446:
URL: https://github.com/apache/texera/pull/8446

   ### What changes were proposed in this PR?
   
   Downloading a large file from a dataset returns a **silently truncated 
file**: no
   browser error, no Texera error toast, nothing in the file-service log — the
   download reports as complete and the saved file is a small fraction of the 
object.
   Truncation happens at a constant *wall-clock* window of ~15s, not at a 
constant
   byte count.
   
   **Root cause.** Envoy Gateway applies a default 15s HTTP route request 
timeout to
   any `HTTPRoute` rule that does not declare a `timeouts` block. Two rules in
   `bin/k8s/templates/base/gateway/gateway-routes.yaml` stream whole objects and
   declared none:
   
   - `texera-minio-route`, which serves the presigned S3 URLs the browser is
     redirected to for a single dataset/model file;
   - the `file-service-svc` rule, which serves `/{did}/versionZip`.
   
   When the timeout fires, Envoy closes the connection *cleanly* mid-stream. The
   browser therefore treats the download as finished and writes the partial 
body to
   disk — silent data corruption rather than a visible failure.
   
   This PR fixes it in two places:
   
   1. **`bin/k8s` (the actual fix).** Both rules now set an explicit
      `timeouts.request` / `timeouts.backendRequest`, defaulting to `"0s"` — 
Gateway
      API treats zero as "no timeout". The value is configurable via a new
      `gatewayConfig.objectStreamTimeout` in `values.yaml` /
      `values-development.yaml`, so a deployment that prefers a finite upper 
bound
      can set e.g. `"1h"`.
   
   2. **`frontend` (defense in depth).** A new `verifyCompleteDownload()` 
operator
      unwraps blob responses and raises `TruncatedDownloadError` when the body 
is
      shorter than the `Content-Length` the response declared. It is applied to 
the
      presigned-URL file downloads and the version-zip downloads for both 
datasets
      and models, and `DownloadService` names the shortfall in the error
      notification. `Content-Length` is CORS-safelisted, so the check works on 
the
      cross-origin presigned responses too; a chunked response declares no 
length
      and still passes through unverified. This means the *next* proxy or 
network
      fault to cut a stream short surfaces as an error instead of as corrupt 
data.
   
   Observed before the fix, on a dataset of multi-GB Zeiss `.czi` files:
   
   | File | Size in `rootFileNodes` | Bytes downloaded | % |
   |---|---|---|---|
   | `A.czi` | 1,416,092,224 | 106,168,320 | 7.5% |
   | `B.czi` | 2,424,775,680 | 92,546,841 | 3.8% |
   | `<dataset>-v1.zip` | ~9.39 GB | 51,367,792 | 0.5% |
   
   The version zip had no central directory, so `unzip -t` reported
   "cannot find zipfile directory" and the archive could not be opened at all.
   
   ### Any related issues, documentation, discussions?
   
   Fixes #8445
   
   ### How was this PR tested?
   
   **Helm chart.** Rendered the template and checked the emitted `HTTPRoute`s:
   
   ```bash
   cd bin/k8s && helm dependency build
   # default: no timeout on both object-streaming rules
   helm template texera . -s templates/base/gateway/gateway-routes.yaml \
     --set minio.gateway.enabled=true --set 
minio.gateway.hostname=minio.example.com \
     | grep -A2 timeouts
   #   timeouts:
   #     request: "0s"
   #     backendRequest: "0s"      (on the file-service rule and on 
texera-minio-route)
   
   # override honored
   helm template texera . -s templates/base/gateway/gateway-routes.yaml \
     --set gatewayConfig.objectStreamTimeout=1h | grep -A2 timeouts
   #   timeouts:
   #     request: "1h"
   #     backendRequest: "1h"
   
   # values-development.yaml renders the same defaults
   helm template texera . -f values-development.yaml \
     -s templates/base/gateway/gateway-routes.yaml | grep -c 'request: "0s"'
   ```
   
   `backendRequest` is allowed to equal `request` here because Gateway API's
   "backendRequest must not exceed request" validation exempts a zero `request`.
   
   **Frontend.** New and updated unit tests, all passing:
   
   ```bash
   cd frontend
   npx ng test --watch=false \
     --include "src/app/common/util/download-integrity.util.spec.ts" \
     --include "src/app/dashboard/service/user/dataset/dataset.service.spec.ts" 
\
     --include 
"src/app/dashboard/service/user/download/download.service.spec.ts" \
     --include "src/app/dashboard/service/user/model/model.service.spec.ts"
   # Test Files  4 passed (4)
   #      Tests  116 passed (116)
   ```
   
   - `download-integrity.util.spec.ts` (new): a matching `Content-Length` 
passes; a
     short body is rejected as `TruncatedDownloadError`; a chunked response 
with no
     `Content-Length` passes; a body *longer* than the declared length passes
     (that means decoding, not truncation); a body-less response yields an 
empty blob.
   - `dataset.service.spec.ts`: the presigned single-file download and the 
version-zip
     download each reject a body shorter than the declared `Content-Length`.
   - `download.service.spec.ts`: the error notification names the shortfall, 
e.g.
     `Error downloading file 'big.czi'. Download truncated: expected 1416092224 
bytes but received 106168320.`
     The existing notification text is unchanged for every other error.
   
   `yarn eslint` and `prettier-eslint --list-different` are clean on the 
touched files.
   
   ### Was this PR authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Opus 5
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   
   https://claude.ai/code/session_016wpLu1zHRyP3aLsvCnoPH8
   


-- 
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