Fastapi: TestClient takes 30s for every .get call on mac

Created on 13 Jun 2020  路  10Comments  路  Source: tiangolo/fastapi

Example

Set up a brand new FastAPI venv:

virtualenv venv
source venv/bin/activate
pip install fastapi[all] pytest

Copy/paste example from the testing docs at https://fastapi.tiangolo.com/tutorial/testing/:

from fastapi import FastAPI
from fastapi.testclient import TestClient

app = FastAPI()


@app.get("/")
async def read_main():
    return {"msg": "Hello World"}


client = TestClient(app)


def test_read_main():
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"msg": "Hello World"}

Observe that the tests take 30s to run. If you add another .get call, they take 60s.

Description

On Mac with new install of FastAPI, every call to TestClient.get() takes exactly 30s to complete. The response is correct.

Environment

  • OS: macOS
  • FastAPI Version: 0.56.1
  • Python version:

  • Python version: 3.6

answered question

Most helpful comment

Experimenting with this test code, I haven't seen these performance issues.

All using macOS 10.15.5 on a Mac Mini, FastAPI 0.56.1, pytest 5.4.3, requests 2.23.0:

| Python | Time (s)|
|--------|---------|
| CPython 3.8.2 | 0.01 |
| CPython 3.7.7 | 0.01
| CPython 3.6.10 | 0.01 |

That is executing with pytest. If I just run the same code directly (not with pytest) it's even faster. Can you provide the exact command you're using to run this?

All 10 comments

Experimenting with this test code, I haven't seen these performance issues.

All using macOS 10.15.5 on a Mac Mini, FastAPI 0.56.1, pytest 5.4.3, requests 2.23.0:

| Python | Time (s)|
|--------|---------|
| CPython 3.8.2 | 0.01 |
| CPython 3.7.7 | 0.01
| CPython 3.6.10 | 0.01 |

That is executing with pytest. If I just run the same code directly (not with pytest) it's even faster. Can you provide the exact command you're using to run this?

Hmm my command is just pytest. Running the code directly produces the same result.

It must be something odd about my setup. I'm on a Macbook Pro 10.13.6, FastAPI 0.56.1, pytest 5.4.3, requests 2.23.0, so identical to you.

My full pip freeze output:
packages.txt

If you send me yours, I'll reproduce your venv exactly and see if it still happens.

30s seems suspiciously close to the timeout for starlette workers, but I don't know enough about starlette to comment more.

Using CPython 3.6.10 installed via pyenv I:

  1. Ran python -m venv venv to create a new virtual environment
  2. Ran source venv/bin/activate to enter this new clean venv
  3. Ran pip install -r packages.txt using the frozen file you provided above
  4. Ran pytest test.py twice

    1. First time gave me "1 passed in 1.16s", second gave me "1 passed in 0.20s" (pyc I assume)

This is the content of test.py

from fastapi import FastAPI
from fastapi.testclient import TestClient

app = FastAPI()


@app.get("/")
async def read_main():
    return {"msg": "Hello World"}


def test_read_main():
    client = TestClient(app)
    response = client.get("/")
    assert response.status_code == 200
    assert response.json() == {"msg": "Hello World"}

So it must not be the dependencies causing the issue. I doubt small variations in Python versions would cause the issue, nor differences in the OS. Would you run a profiler to try and narrow down exactly what's taking so long?

Something like this:

import cProfile
prof = cProfile.Profile()
prof.enable()
response = client.get("/")
prof.dump_stats("stats.prof")

You can then analyze that file with something like snakeviz to see where the program is getting hung up. I'm not sure that will get us to the solution but it might help narrow down the problem.

Also, just fishing for more information that might lead us in the right direction:

  1. If you run the test code directly (just run the file and have it execute the function) instead of in pytest, do you see the same slowness?
  2. If you were to run FastAPI as a normal server with something like uvicorn and try to hit that endpoint with requests like requests.get("/"), is that slow as well? Or is that normal speed?
  3. Do you have any anti-virus / security software running you could try disabling?
  4. Does the basic test code for Starlette have the same behavior if you run their example? Or is that quick?

Duplicating those exact steps you did, I still see the error.

I've made a repo at https://github.com/charlesbaynham/fastapi_mwe which contains the exact code I'm using. Running the profiler in this repo produces:

image

The full profile output is in the repo and also here (renamed, to appease github):

stats.prof.txt

For your other questions:

  1. I see the same behaviour
  2. No that works at normal speed
  3. Nope
  4. Ah ha, yes that shows the same behaviour. Guess I'll head on over there then :)

Alright, well at least we've narrowed down the issue to TestClient and Starlette. I don't have any other ideas right now but I'll follow the other ticket in case I think of something. Hopefully someone with a better grasp on the inner workings of Starlette will be able to make more progress 馃槄.

Thanks for the help here @dbanty ! :clap: :bow:

@charlesbaynham could you try on another macOS, or another OS (maybe on a VM)?

Also maybe inside of Docker.

It seems very weird, and like it could be something strange related to your environment.

You could also try another installation, e.g. miniconda, with another Python version, just to try and narrow the environment of the issue.

Hi @tiangolo, I don't have another mac available I'm afraid. I've also tried on Windows, but it works fine. I'll continue looking at this but in the starlette issue (https://github.com/encode/starlette/issues/974) since it doesn't seem to be a fastapi problem.

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

Was this page helpful?
0 / 5 - 0 ratings