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]