fix: cancel volume server requests on client disconnect during S3 downloads (#8373)

* fix: cancel volume server requests on client disconnect during S3 downloads

- Use http.NewRequestWithContext in ReadUrlAsStream so in-flight volume
  server requests are properly aborted when the client disconnects and
  the request context is canceled
- Distinguish context-canceled errors (client disconnect, expected) from
  real server errors in streamFromVolumeServers; log at V(3) instead of
  ERROR to reduce noise from client-side disconnects (e.g. Nginx upstream
  timeout, browser cancel, curl --max-time)

Fixes: streamFromVolumeServers: streamFn failed...context canceled"

* fixup: separate Canceled/DeadlineExceeded log severity in streamFromVolumeServers

- context.Canceled → V(3) Infof "client disconnected" (expected, no noise)
- context.DeadlineExceeded → Warningf "server-side deadline exceeded" (unexpected, needs attention)
- all other errors → Errorf (unchanged)"
This commit is contained in:
Chris Lu
2026-02-18 17:14:54 -08:00
committed by GitHub
parent eda4a000cc
commit 38e14a867b
2 changed files with 12 additions and 3 deletions

View File

@@ -1093,7 +1093,16 @@ func (s3a *S3ApiServer) streamFromVolumeServers(w http.ResponseWriter, r *http.R
BucketTrafficSent(cw.written, r)
}
if err != nil {
glog.Errorf("streamFromVolumeServers: streamFn failed after writing %d bytes: %v", cw.written, err)
switch {
case errors.Is(err, context.Canceled):
// Client disconnected mid-stream (e.g. Nginx upstream timeout, browser cancel) - expected
glog.V(3).Infof("streamFromVolumeServers: client disconnected after writing %d bytes: %v", cw.written, err)
case errors.Is(err, context.DeadlineExceeded):
// Server-side deadline exceeded - unexpected, warrants operator attention
glog.Warningf("streamFromVolumeServers: server-side deadline exceeded after writing %d bytes: %v", cw.written, err)
default:
glog.Errorf("streamFromVolumeServers: streamFn failed after writing %d bytes: %v", cw.written, err)
}
// Streaming error after WriteHeader was called - response already partially written
return newStreamErrorWithResponse(err)
}