WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Using items will break when run with uvicorn --workers 2. Global app state is not shared between worker instances. #7

@JamesParrott

Description

@JamesParrott

I know this repo is a simple example for educational purposes, and in the accompanying video Arjan mentions the global dictionary items is a stand-in for a proper database. But using global variables in multi-threaded or multi-process server code is a good way to create subtle bugs (that will only be seen in production, if the dev, staging and test environments only used a single worker). If fastAPI is revisited by the channel in future (I'd love to see that one about OAuth2 flows in FastAPI), I think avoiding global variables should be mentioned (once again).

Explanation

Using global variables in the main:app module for other coding purposes, does not scale beyond one single worker serving all requests (using multiple workers is a big advantage of using uvicorn). Each worker is its own separate Python process. As ever, avoid global variables. Even mutable ones. In particular, if one end point in the custom FastAPI code did app.foo = 'bar', Another worker serving a different request will throw an error if it did assert app.foo == 'bar', just as if they were two entirely separate Python applications.

Otherwise, immutable global constants can be fine as long as each constant really does have the same value in each process (i.e. if each one's value doesn't vary between worker processes somehow).

Reproduction

To demonstrate this two terminal windows are required:
(with uvicorn and fastAPI installed in the venv).

Note, the bug will not occur with uvicorn's `--reload' option, as:

WARNING:  "workers" flag is ignored when reloading is enabled.
\venvs\Arjan_FastAPI\2023-fastapi>uvicorn 3_more_routing:app --workers 2
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started parent process [14104]
INFO:     Started server process [13420]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Started server process [13576]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

(with requests installed)

(requests) \venvs\Arjan_FastAPI\2023-fastapi>python 1_main.py
{'items': {'0': {'name': 'Hammer', 'price': 9.99, 'count': 20, 'id': 0, 'category': 'tools'}, '1': {'name': 'Pliers', 'price': 5.99, 'count': 20, 'id': 1, 'category': 'tools'}, '2': {'name': 'Nails', 'price': 1.99, 'count': 100, 'id': 2, 'category': 'consumables'}}}

(requests) \venvs\Arjan_FastAPI\2023-fastapi>python 3_main.py
Adding an item:
{'added': {'name': 'Screwdriver', 'price': 3.99, 'count': 10, 'id': 4, 'category': 'tools'}}
{'items': {'0': {'name': 'Hammer', 'price': 9.99, 'count': 20, 'id': 0, 'category': 'tools'}, '1': {'name': 'Pliers', 'price': 5.99, 'count': 20, 'id': 1, 'category': 'tools'}, '2': {'name': 'Nails', 'price': 1.99, 'count': 100, 'id': 2, 'category': 'consumables'}, '4': {'name': 'Screwdriver', 'price': 3.99, 'count': 10, 'id': 4, 'category': 'tools'}}}

Updating an item:
{'updated': {'name': 'Hammer', 'price': 9.99, 'count': 9001, 'id': 0, 'category': 'tools'}}
{'items': {'0': {'name': 'Hammer', 'price': 9.99, 'count': 9001, 'id': 0, 'category': 'tools'}, '1': {'name': 'Pliers', 'price': 5.99, 'count': 20, 'id': 1, 'category': 'tools'}, '2': {'name': 'Nails', 'price': 1.99, 'count': 100, 'id': 2, 'category': 'consumables'}, '4': {'name': 'Screwdriver', 'price': 3.99, 'count': 10, 'id': 4, 'category': 'tools'}}}

Deleting an item:
{'deleted': {'name': 'Hammer', 'price': 9.99, 'count': 9001, 'id': 0, 'category': 'tools'}}
{'items': {'1': {'name': 'Pliers', 'price': 5.99, 'count': 20, 'id': 1, 'category': 'tools'}, '2': {'name': 'Nails', 'price': 1.99, 'count': 100, 'id': 2, 'category': 'consumables'}, '4': {'name': 'Screwdriver', 'price': 3.99, 'count': 10, 'id': 4, 'category': 'tools'}}}

(requests) \venvs\Arjan_FastAPI\2023-fastapi>python 1_main.py
{'items': {'1': {'name': 'Pliers', 'price': 5.99, 'count': 20, 'id': 1, 'category': 'tools'}, '2': {'name': 'Nails', 'price': 1.99, 'count': 100, 'id': 2, 'category': 'consumables'}, '4': {'name': 'Screwdriver', 'price': 3.99, 'count': 10, 'id': 4, 'category': 'tools'}}}

(requests) \venvs\Arjan_FastAPI\2023-fastapi>python 1_main.py
{'items': {'0': {'name': 'Hammer', 'price': 9.99, 'count': 20, 'id': 0, 'category': 'tools'}, '1': {'name': 'Pliers', 'price': 5.99, 'count': 20, 'id': 1, 'category': 'tools'}, '2': {'name': 'Nails', 'price': 1.99, 'count': 100, 'id': 2, 'category': 'consumables'}}}

Note on the third request, the "deleted" item, Hammer seems to have reappeared, because that request was served by the other worker, with its own separate never-mutated global variable, items.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions