Search code examples
herokufastapi

Why does the FastAPI startup event run twice in HEROKU?


When I deployed a FastAPI app to heroku the startup event is fired twice. Is it my problem?

The code below is not my original app, but a temporary app I made for testing. And the Heroku was also made fresh for testing.

# Heroku logs

2022-10-18T08:37:32 app[api]: Build started by user [email protected]
2022-10-18T08:37:49 app[api]: Deploy 298098b0 by user [email protected]
2022-10-18T08:37:49 app[api]: Release v6 created by user [email protected]
2022-10-18T08:37:52 heroku[web.1]: State changed from down to starting
2022-10-18T08:37:54 heroku[web.1]: Starting process with command `uvicorn app.main:app --host=0.0.0.0 --port=${PORT:-5000}`
2022-10-18T08:37:55 app[api]: Build succeeded
2022-10-18T08:37:55.490285+00:00 app[web.1]: INFO:     Uvicorn running on http://0.0.0.0:51121 (Press CTRL+C to quit)
2022-10-18T08:37:55 app[web.1]: INFO:     Started parent process [4]
2022-10-18T08:37:55 app[web.1]: INFO:     Started server process [11]
2022-10-18T08:37:55 app[web.1]: INFO:     Waiting for application startup.
2022-10-18T08:37:55 app[web.1]: INFO:     Application startup complete.
2022-10-18T08:37:55 app[web.1]: INFO:     Started server process [10]
2022-10-18T08:37:55 app[web.1]: INFO:     Waiting for application startup.
2022-10-18T08:37:55 app[web.1]: INFO:     Application startup complete.
2022-10-18T08:37:56 heroku[web.1]: State changed from starting to up
# main.py

from fastapi import Depends, FastAPI
import uvicorn

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}
# Procfile

web: uvicorn app.main:app --host=0.0.0.0 --port=${PORT:-5000}

Solution

  • Uvicorn operates by running worker processes.

    If you don't give it a number of processes to use via the --workers argument, it defaults to the value of the WEB_CONCURRENCY environment variable, falling back to 1 if neither is set:

    --workers INTEGER               Number of worker processes. Defaults to the
                                    $WEB_CONCURRENCY environment variable if
                                    available, or 1. Not valid with --reload.
    

    Heroku sets a default value for the WEB_CONCURRENCY environment variable:

    The WEB_CONCURRENCY environment variable is automatically set by Heroku, based on the processes’ Dyno size. This feature is intended to be a sane starting point for your application. We recommend knowing the memory requirements of your processes and setting this configuration variable accordingly.

    Heroku has probably set this value to 2 based on the size of the dynos you are running. This would cause two workers to run in parallel, which is what your logs are showing:

    • A parent process: Started parent process [4]
    • And two worker processes:
      • Started server process [11]
      • Started server process [10]

    You can change the value of WEB_CONCURRENCY if you wish.

    If your code is malfunctioning with multiple workers that indicates a problem with your code. In that case, instead of setting this to 1, I suggest you identify why it is misbehaving and fix the problem. One common cause of this is use of global variables.