Skip to content

Commit a7da556

Browse files
davidfu13gopherbot
authored andcommitted
http2: optimize buffer allocation in transport
We have identified a high memory usage problem in our production service, which utilizes Traefik as a gRPC proxy. This service handles a substantial volume of gRPC bi-directional streaming requests that can persist for extended periods, spanning many days. Currently, there exists only a single global buffer pool in the http2 package. The allocated buffers, regardless of their sizes, are shared among requests with vastly different characteristics. For instance, gRPC streaming requests typically require smaller buffer sizes and occupy buffers for significant durations. Conversely, general HTTP requests may necessitate larger buffer sizes but only retain them temporarily. Unfortunately, the large buffers allocated by HTTP requests are can be chosen for subsequent gRPC streaming requests, resulting in numerous large buffers being unable to be recycled. In our production environment, which processes approximately 1 million gRPC streaming requests, memory usage can soar to an excessive 800 GiB. This is a substantial waste of resources. To address this challenge, we propose implementing a multi-layered buffer pool mechanism. This mechanism allows requests with varying characteristics to select buffers of appropriate sizes, optimizing resource allocation and recycling. Change-Id: I834f7c08d90fd298aac7971ad45dc1a36251788b GitHub-Last-Rev: 4771976 GitHub-Pull-Request: #182 Reviewed-on: https://go-review.googlesource.com/c/net/+/508415 Run-TryBot: Damien Neil <[email protected]> Reviewed-by: David Chase <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Damien Neil <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent 167593b commit a7da556

File tree

1 file changed

+26
-4
lines changed

1 file changed

+26
-4
lines changed

http2/transport.go

+26-4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"io/fs"
2020
"log"
2121
"math"
22+
"math/bits"
2223
mathrand "math/rand"
2324
"net"
2425
"net/http"
@@ -1680,7 +1681,27 @@ func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int {
16801681
return int(n) // doesn't truncate; max is 512K
16811682
}
16821683

1683-
var bufPool sync.Pool // of *[]byte
1684+
// Seven bufPools manage different frame sizes. This helps to avoid scenarios where long-running
1685+
// streaming requests using small frame sizes occupy large buffers initially allocated for prior
1686+
// requests needing big buffers. The size ranges are as follows:
1687+
// {0 KB, 16 KB], {16 KB, 32 KB], {32 KB, 64 KB], {64 KB, 128 KB], {128 KB, 256 KB],
1688+
// {256 KB, 512 KB], {512 KB, infinity}
1689+
// In practice, the maximum scratch buffer size should not exceed 512 KB due to
1690+
// frameScratchBufferLen(maxFrameSize), thus the "infinity pool" should never be used.
1691+
// It exists mainly as a safety measure, for potential future increases in max buffer size.
1692+
var bufPools [7]sync.Pool // of *[]byte
1693+
func bufPoolIndex(size int) int {
1694+
if size <= 16384 {
1695+
return 0
1696+
}
1697+
size -= 1
1698+
bits := bits.Len(uint(size))
1699+
index := bits - 14
1700+
if index >= len(bufPools) {
1701+
return len(bufPools) - 1
1702+
}
1703+
return index
1704+
}
16841705

16851706
func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
16861707
cc := cs.cc
@@ -1698,12 +1719,13 @@ func (cs *clientStream) writeRequestBody(req *http.Request) (err error) {
16981719
// Scratch buffer for reading into & writing from.
16991720
scratchLen := cs.frameScratchBufferLen(maxFrameSize)
17001721
var buf []byte
1701-
if bp, ok := bufPool.Get().(*[]byte); ok && len(*bp) >= scratchLen {
1702-
defer bufPool.Put(bp)
1722+
index := bufPoolIndex(scratchLen)
1723+
if bp, ok := bufPools[index].Get().(*[]byte); ok && len(*bp) >= scratchLen {
1724+
defer bufPools[index].Put(bp)
17031725
buf = *bp
17041726
} else {
17051727
buf = make([]byte, scratchLen)
1706-
defer bufPool.Put(&buf)
1728+
defer bufPools[index].Put(&buf)
17071729
}
17081730

17091731
var sawEOF bool

0 commit comments

Comments
 (0)