-
Notifications
You must be signed in to change notification settings - Fork 128
Closed
Description
This is a pretty fun bug: In (at least) HTTP2 mode, when uploading a huge body, AHC behaves badly in various different ways.
The writes (in a recursive loop) are run in a recursive loop and they are freewheeling meaning there is no real backpressure.
Specifically in
async-http-client/Sources/AsyncHTTPClient/RequestBag.swift
Lines 187 to 188 in bdaa3b1
writer.writeRequestBodyPart(part, request: self, promise: promise) | |
return future |
write
promise but rather a pre-succeeded future. This won't stop until the state machine switches into a mode to "pause" sending the request body.
The problem is twofold:
- When delegate & channel are not co-located, the "pause" can arrive quite a bit too late because it's triggered from the channel's
channelWritabilityChanged -> false
which of course happens on the channel's EL. But the writes are triggered on the delegate's EL... (That's bad usually not fatal, we're just eating up some extra memory) - Unfortunately, (in
async-http-client/Sources/AsyncHTTPClient/HTTPHandler.swift
Lines 58 to 69 in bdaa3b1
func writeNextChunk(_ chunk: Bytes.SubSequence) -> EventLoopFuture<Void> { if let nextChunk = iterator.wrappedValue.next() { return self.write(.byteBuffer(ByteBuffer(bytes: chunk))).flatMap { writeNextChunk(nextChunk) } } else { return self.write(.byteBuffer(ByteBuffer(bytes: chunk))) } } return writeNextChunk(chunk) } O(bodySize / 4MB)
recursive stack frames. 5 GB always does the job.
Metadata
Metadata
Assignees
Labels
No labels