Skip to content

Commit

Permalink
[FIX] fastapi: Ensure thread safety of the FastAPI app cache
Browse files Browse the repository at this point in the history
defaultdict in python is not thread safe. Since this data structure
is used to store the cache of FastAPI apps, we must ensure that the
access to this cache is thread safe. This is done by using a lock
to protect the access to the cache.
  • Loading branch information
lmignon committed Jan 11, 2025
1 parent 2db95d5 commit 1bf9751
Showing 1 changed file with 15 additions and 6 deletions.
21 changes: 15 additions & 6 deletions fastapi/pools/fastapi_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/LGPL).

import queue
import threading
from collections import defaultdict
from contextlib import contextmanager
from typing import Generator
Expand All @@ -16,18 +17,25 @@ def __init__(self):
self._queue_by_db_by_root_path: dict[
str, dict[str, queue.Queue[FastAPI]]
] = defaultdict(lambda: defaultdict(queue.Queue))
self._lock = threading.Lock()

def __get_app(self, env: Environment, root_path: str) -> FastAPI:
def __get_pool(self, env: Environment, root_path: str) -> queue.Queue[FastAPI]:
db_name = env.cr.dbname
with self._lock:
# default dict is not thread safe but the use
return self._queue_by_db_by_root_path[db_name][root_path]

def __get_app(self, env: Environment, root_path: str) -> FastAPI:
pool = self.__get_pool(env, root_path)
try:
return self._queue_by_db_by_root_path[db_name][root_path].get_nowait()
return pool.get_nowait()
except queue.Empty:
env["fastapi.endpoint"].sudo()
return env["fastapi.endpoint"].sudo().get_app(root_path)

def __return_app(self, env: Environment, app: FastAPI, root_path: str) -> None:
db_name = env.cr.dbname
self._queue_by_db_by_root_path[db_name][root_path].put(app)
pool = self.__get_pool(env, root_path)
pool.put(app)

@contextmanager
def get_app(
Expand All @@ -48,5 +56,6 @@ def get_app(
self.__return_app(env, app, root_path)

def invalidate(self, root_path: str, env: Environment) -> None:
db_name = env.cr.dbname
self._queue_by_db_by_root_path[db_name][root_path] = queue.Queue()
with self._lock:
db_name = env.cr.dbname
self._queue_by_db_by_root_path[db_name][root_path] = queue.Queue()

Check warning on line 61 in fastapi/pools/fastapi_app.py

View check run for this annotation

Codecov / codecov/patch

fastapi/pools/fastapi_app.py#L59-L61

Added lines #L59 - L61 were not covered by tests

0 comments on commit 1bf9751

Please sign in to comment.