Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WSGI interface: reading request body is O(n^2) wrt payload size #708

Closed
vytas7 opened this issue Jun 30, 2020 · 2 comments · Fixed by #1329
Closed

WSGI interface: reading request body is O(n^2) wrt payload size #708

vytas7 opened this issue Jun 30, 2020 · 2 comments · Fixed by #1329
Labels

Comments

@vytas7
Copy link
Contributor

vytas7 commented Jun 30, 2020

Hello!
This is related to #371 and potentially a duplicate/complementary information, feel free to close if it is deemed to be the case.

I was fooling around with different WSGI and ASGI servers while hacking on a web framework that would support both in the future.

As noted in #371, this is indeed a very inefficient scenario, however, what is worse, I found out the time required to turn around the request is at least O(n^2) or worse wrt the payload size.

POSTing some files and multipart forms with curl:

Payload size: 94171344
real	0m5.763s
user	0m0.033s
sys	0m0.012s
Payload size: 186054864
real	0m24.183s
user	0m0.018s
sys	0m0.061s
Payload size: 372178858
real	1m37.760s
user	0m0.044s
sys	0m0.152s

So it is not just inefficient loading everything into memory, but something else is going on.

Test case used:

def app(environ, start_response):
    status = '200 OK'  # HTTP Status
    headers = [('Content-type', 'text/plain')]  # HTTP Headers
    start_response(status, headers)

    stream = environ['wsgi.input']
    length = 0
    while True:
        chunk = stream.read(8192)
        if not chunk:
            break
        length += len(chunk)

    resp = "Payload size: {}".format(length)
    return [resp.encode()]

Running uvicorn==0.11.5 as uvicorn --interface wsgi simple_reader:app.

@vytas7 vytas7 changed the title WSGI middleware: reading request body is worse than O(n^2) wrt payload size WSGI interface: reading request body is worse than O(n^2) wrt payload size Jun 30, 2020
@vytas7 vytas7 changed the title WSGI interface: reading request body is worse than O(n^2) wrt payload size WSGI interface: reading request body is O(n^2) wrt payload size Jun 30, 2020
@vytas7
Copy link
Contributor Author

vytas7 commented Jul 1, 2020

The issue most probably lies in bytestring concatenation: https://github.com/encode/uvicorn/blob/master/uvicorn/middleware/wsgi.py#L85
chunks.append(chunk)... b''.join(chunks), accumulating in a BytesIO, bytearray_var += ... should all work O(n).

@abersheeran
Copy link
Member

Maybe you can try https://github.com/abersheeran/a2wsgi
It works very well on our production program.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
3 participants