aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorchrislu <chris.lu@gmail.com>2025-12-09 17:14:36 -0800
committerchrislu <chris.lu@gmail.com>2025-12-09 17:14:36 -0800
commita4368b751ede9de5f49cc199d1a60c73cb60b32a (patch)
treead6bd07610a46ad2e962889a0f21e4ad8c62607f
parent4f382b77c898b9685e16a49048681bb73d07ee54 (diff)
downloadseaweedfs-a4368b751ede9de5f49cc199d1a60c73cb60b32a.tar.xz
seaweedfs-a4368b751ede9de5f49cc199d1a60c73cb60b32a.zip
mount: improve read throughput by enabling parallel HTTP connectionsorigin/fix-mount-read-throughput-7504
This addresses issue #7504 where a single weed mount FUSE instance does not fully utilize node network bandwidth when reading large files. The root cause is that HTTP GET streaming reads were getting serialized even with multiple concurrent goroutines, due to: 1. HTTP/2 multiplexing all requests over a single TCP connection 2. The streaming loop using a 64KB intermediate buffer with extra copies Changes: http/client: Enable truly parallel connections - Set ForceAttemptHTTP2: false to disable HTTP/2 multiplexing - Set MaxConnsPerHost: 0 (unlimited) to allow parallel connections per host - This lets multiple goroutines use separate TCP connections to the same volume server filer/stream: Add direct-to-buffer fast path - For full, uncompressed, unencrypted chunks, use RetriedFetchChunkData to read directly into the destination buffer - This skips the 64KB staged streaming loop and avoids an extra memory copy - Falls back to streaming path on partial fill or error These changes should improve bandwidth utilization when reading large files through a single mount instance. Ref: #7504
-rw-r--r--weed/filer/stream.go18
-rw-r--r--weed/util/http/client/http_client.go5
2 files changed, 22 insertions, 1 deletions
diff --git a/weed/filer/stream.go b/weed/filer/stream.go
index b2ee00555..b7857041c 100644
--- a/weed/filer/stream.go
+++ b/weed/filer/stream.go
@@ -374,6 +374,24 @@ func (c *ChunkStreamReader) fetchChunkToBuffer(chunkView *ChunkView) error {
glog.V(1).Infof("operation LookupFileId %s failed, err: %v", chunkView.FileId, err)
return err
}
+
+ // Fast path: for full, plain chunks, read directly into a single buffer to avoid
+ // the streaming loop (which stages 64KB at a time and adds an extra copy).
+ if chunkView.CipherKey == nil && !chunkView.IsGzipped && chunkView.IsFullChunk() {
+ buf := make([]byte, chunkView.ViewSize)
+ n, fetchErr := util_http.RetriedFetchChunkData(context.Background(), buf, urlStrings, nil, false, true, chunkView.OffsetInChunk, chunkView.FileId)
+ if fetchErr == nil && n == len(buf) {
+ c.buffer = buf
+ c.bufferOffset = chunkView.ViewOffset
+ c.chunk = chunkView.FileId
+ return nil
+ }
+ // Fall back to the streaming path on partial fill or error
+ if fetchErr != nil {
+ glog.V(1).Infof("read %s direct failed, err: %v, falling back to stream", chunkView.FileId, fetchErr)
+ }
+ }
+
var buffer bytes.Buffer
var shouldRetry bool
jwt := JwtForVolumeServer(chunkView.FileId)
diff --git a/weed/util/http/client/http_client.go b/weed/util/http/client/http_client.go
index d1d2f5c56..0494fd04b 100644
--- a/weed/util/http/client/http_client.go
+++ b/weed/util/http/client/http_client.go
@@ -130,7 +130,10 @@ func NewHttpClient(clientName ClientName, opts ...HttpClientOpt) (*HTTPClient, e
httpClient.Transport = &http.Transport{
MaxIdleConns: 1024,
MaxIdleConnsPerHost: 1024,
- TLSClientConfig: tlsConfig,
+ // Allow truly parallel per-host connections and avoid HTTP/2 multiplexing
+ MaxConnsPerHost: 0, // unlimited
+ ForceAttemptHTTP2: false, // disable HTTP/2 to avoid implicit single-connection multiplexing
+ TLSClientConfig: tlsConfig,
}
httpClient.Client = &http.Client{
Transport: httpClient.Transport,