Repository navigation
Split large write buffers into chunks libuv accepts - #793
Open
jensbjorgensen wants to merge 1 commit into
Open
jensbjorgensen wants to merge 1 commit into
jensbjorgensen wants to merge 1 commit into
Conversation
libuv >= 1.52 rejects uv_write()/uv_try_write() requests carrying more than 1024*1024 buffers with UV_EINVAL. A transport that keeps receiving write() calls while libuv still has pending data accumulates every buffer in UVStream._buffer and flushed them in a single request, so a burst of more than 1M small writes in one loop iteration killed the connection with OSError(22, 'Invalid argument'). Hand the buffered writes to libuv in chunks of at most 1024*1024 buffers, one uv_write() request each.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
libuv 1.52.0 added
uv__check_before_write()(libuv/libuv#5014), which makesuv_write()anduv_try_write()returnUV_EINVALwhen a request carriesmore than
1024*1024buffers. uvloop 0.23.0 picked that up with the libuv1.52.1 upgrade (#753).
UVStreamappends everytransport.write()toself._bufferwhile libuvstill has pending data for the stream, and
_exec_write()then hands thewhole list to libuv as a single request. Any code that issues more than 1M
write()calls in one loop iteration, or onewritelines()with more than1M items, now gets the transport closed with
OSError(22, 'Invalid argument')instead of the data being sent. uvloop 0.22.1 (libuv 1.48.0) and vanilla
asyncio, which coalesces into a
bytearray, both handle this fine.We hit this in production on a server that streams a large snapshot to a
newly connected client as a few million small
write()calls from onecallback; every snapshot died with EINVAL and the client reconnect loop
pegged a CPU.
Fix
_exec_write()now hands the buffered data to libuv in chunks of at most1024*1024buffers, oneuv_write()request per chunk, and leaves the lastchunk to the existing code path. Writes under the limit are unaffected.
Reproducer