question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Uvicorn with reload hangs when using a ProcessPoolExecutor

See original GitHub issue

Checklist

  • The bug is reproducible against the latest release and/or master.
  • There are no similar issues or pull requests to fix it yet.

Describe the bug

When at last 1 task is submitted to a ProcessPoolExecutor uvicorn fails to reload when a file change has been detected. It detects the file change and the server is shutdown but it doesn’t start again. As long as no tasks are submitted uvicorn is able to reload properly.

To reproduce

"""ProcessPoolExecutor Example.

Run
---
uvicorn main:app --reload

Versions
--------
fastapi~=0.63.0
uvicorn[standard]~=0.13.3
"""
from concurrent.futures import ProcessPoolExecutor
from typing import Any, Dict

from fastapi import FastAPI

app = FastAPI(title="Example API")
POOL = ProcessPoolExecutor(max_workers=1)


def task() -> None:
    """."""
    print("Executed in process pool")


@app.get("/")
def index() -> Dict[str, Any]:
    """Index."""
    POOL.submit(task)
    return {"message": "Hello World"}

Expected behavior

Uvicorn should reload when file changes are detected.

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [9042] using watchgod
INFO:     Started server process [9044]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
WARNING:  WatchGodReload detected file change in '['/Users/maartenhuijsmans/main.py']'. Reloading...
INFO:     Shutting down
INFO:     Waiting for application shutdown.
INFO:     Application shutdown complete.
INFO:     Finished server process [9044]
INFO:     Started server process [9047]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:61607 - "GET / HTTP/1.1" 200 OK

Actual behavior

Uvicorn doesn’t start

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [9054] using watchgod
INFO:     Started server process [9056]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     127.0.0.1:61615 - "GET / HTTP/1.1" 200 OK
Executed in process pool
WARNING:  WatchGodReload detected file change in '['/Users/maartenhuijsmans/main.py']'. Reloading...
INFO:     Shutting down
INFO:     Waiting for application shutdown.
INFO:     Application shutdown complete.
INFO:     Finished server process [9056]

Debugging material

Environment

  • macOS 10.13.6 / python 3.8.6 / uvicorn 0.13.3
  • uvicorn main:app --reload

Additional context

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:5 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
euri10commented, Jan 13, 2021

incidentally it seems to not happen on #853

0reactions
Kludexcommented, Sep 11, 2022

This is not a bug. It works as intended.

On the BaseReload.restart() method, we terminate() the server process, and then we join(). As the process still have the ProcessPool alive, it will never terminate.

That said, we have two options:

  1. On Uvicorn’s side, as join() accepts a timeout parameter, we can set it to a value that makes sense (?).
  2. Consider this as a user issue, which the solution here would be to shut down the pool on the shutdown event.

On 2 you’d have:

from concurrent.futures import ProcessPoolExecutor
from typing import Any, Dict

from fastapi import FastAPI

app = FastAPI(title="Example API")
POOL = None

@app.on_event("startup")
def startup():
    global POOL
    POOL = ProcessPoolExecutor(max_workers=1)


@app.on_event("shutdown")
def shutdown():
    global POOL
    POOL.shutdown()

def task() -> None:
    """."""
    print("Executed in process pool")


@app.get("/")
def index() -> Dict[str, Any]:
    """Index."""
    POOL.submit(task)
    return {"message": "Hello World"}

Given that the snippet above already satisfies this issue, and that I’ve provided explanation about what’s happening, I’ll be closing this issue. If there’s a good argument, and a proposal to implement 1 in uvicorn, we can reconsider it.

Read more comments on GitHub >

github_iconTop Results From Across the Web

api - Problem enabling Uvicorn auto-restart when launching ...
The documentation states that you can just use reload=True . Example: uvicorn.run("example:app", port=5000, reload=True, access_log=False).
Read more >
Settings - Uvicorn
When using Uvicorn through WSL, you might have to set the WATCHFILES_FORCE_POLLING environment variable, for file changes to trigger a reload. See watchfiles ......
Read more >
bocadilloproject/bocadillo - Gitter
If I run uvicorn asgi:app --reload in the second demo subdirectory, it errors: ... can't use in Python unless you're running from outside...
Read more >
Gunicorn - Hacker News
I have been using Gunicorn with Gevent for nearly 10 years and never had a problem ... The sockets would very silently crash...
Read more >
Falcon Documentation - Read the Docs
Uvicorn is a popular choice, owing to its fast and stable implementation ... Restart Gunicorn (unless you're using --reload), and send a GET ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found