Skip to content

net/http: Stream starvation in http2 priority write scheduler #58804

Closed
@mborsz

Description

What version of Go are you using (go version)?

$ go version
go version go1.20 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/usr/local/google/home/maciejborsz/.cache/go-build"
GOENV="/usr/local/google/home/maciejborsz/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/usr/local/google/home/maciejborsz/.gvm/pkgsets/go1.19/global/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/usr/local/google/home/maciejborsz/.gvm/pkgsets/go1.19/global"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/google/home/maciejborsz/sdk/go1.20"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/google/home/maciejborsz/sdk/go1.20/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.20"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/usr/local/google/home/maciejborsz/repro/go.mod"
GOWORK=""
CGO_CFLAGS="-O2 -g"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-O2 -g"
CGO_FFLAGS="-O2 -g"
CGO_LDFLAGS="-O2 -g"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build187328419=/tmp/go-build -gno-record-gcc-switches"

What did you do?

  1. I create a single http2 connection with multiple streams inside:
  • one long running, low volume stream that periodically receives tiny amount of data from the server
  • many short requests getting larger amount of data
  1. I logged when the long running goroutine gets packets from the server

https://gist.github.com/mborsz/c2335eff2cd164b434ee37489f23aa2b is a full code with output that I get on my machine.
The file output-go1.20.txt presents two problems:

  • longrunning data is not transferred at all until the last short request finishes
  • the first short requests (with numbers 0-5) finish only after more recent request finishes.

https://go.dev/play/p/5febB68XX0j is slightly modified version of repro code (changed totalRequest to 100 to fit in the timeout)

What did you expect to see?

  • Longrunning stream gets data all the time

What did you see instead?

  • While the short requests are happening, the traffic on longrunning stream is blocked completely. By increasing totalRequests I'm able to prolong the period of no data arbitrarily. With infinite stream of incoming requests, we see long running to be starved forever (can be reproduced by changing totalRequests to e.g. 50000000 and increasing response size in handlerGet to e.g. 10M).

Notes:

  • I think the observed behavior matches code of PriorityWriteScheduler for case when all weights are equal (as in our case):
  • Changing the WriteScheduler to "random" fixes the problem on my machine (uncommenting the code in snippet). On the playground it still fails. Not sure why, but I'm suspecting some playground limitation or the test is too short on the playground to trigger the problem.
  • After d872374 (golang 1.19+), default http server uses priority (affected) write scheduler.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Labels

FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions