Skip to content

Commit 56a6d4a

Browse files
committed
[MIG] fastapi: Add support for multiple Odoo cache sequences and fix linting
[FIX] fastapi: Apply linting recommendations in 18
1 parent 13f9de0 commit 56a6d4a

4 files changed

Lines changed: 38 additions & 29 deletions

File tree

fastapi/middleware.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
1010
"""
1111

12-
from typing import Iterable
12+
from collections.abc import Iterable
1313

1414
import a2wsgi
1515
from a2wsgi.asgi import ASGIResponder

fastapi/models/fastapi_endpoint.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def _registered_endpoint_rule_keys(self):
121121
return tuple(res)
122122

123123
@api.model
124-
def _routing_impacting_fields(self) -> Tuple[str, ...]:
124+
def _routing_impacting_fields(self) -> tuple[str, ...]:
125125
"""The list of fields requiring to refresh the mount point of the pp
126126
into odoo if modified"""
127127
return ("root_path", "save_http_session")

fastapi/pools/event_loop.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import asyncio
55
import queue
66
import threading
7+
from collections.abc import Generator
78
from contextlib import contextmanager
8-
from typing import Generator
99

1010

1111
class EventLoopPool:
@@ -16,7 +16,8 @@ def __get_event_loop_and_thread(
1616
self,
1717
) -> tuple[asyncio.AbstractEventLoop, threading.Thread]:
1818
"""
19-
Get an event loop from the pool. If no event loop is available, create a new one.
19+
Get an event loop from the pool. If no event loop is available,
20+
create a new one.
2021
"""
2122
try:
2223
return self.pool.get_nowait()
@@ -47,9 +48,11 @@ def shutdown(self):
4748
@contextmanager
4849
def get_event_loop(self) -> Generator[asyncio.AbstractEventLoop, None, None]:
4950
"""
50-
Get an event loop from the pool. If no event loop is available, create a new one.
51+
Get an event loop from the pool. If no event loop is available,
52+
create a new one.
5153
52-
After the context manager exits, the event loop is returned to the pool for reuse.
54+
After the context manager exits, the event loop is returned to
55+
the pool for reuse.
5356
"""
5457
loop, thread = self.__get_event_loop_and_thread()
5558
try:

fastapi/pools/fastapi_app.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import queue
55
import threading
66
from collections import defaultdict
7+
from collections.abc import Generator
78
from contextlib import contextmanager
8-
from typing import Generator
99

1010
from odoo.api import Environment
1111

@@ -45,8 +45,8 @@ class FastApiAppPool:
4545
updated by the increment of the `cache_sequence` SQL sequence. This cache sequence
4646
on the registry is reloaded from the DB on each request made to a specific database.
4747
When an app is retrieved from the pool, we always compare the cache sequence of
48-
the pool with the cache sequence of the registry. If the two sequences are different,
49-
we invalidate the pool and save the new cache sequence on the pool.
48+
the pool with the cache sequence of the registry. If the two sequences are
49+
different, we invalidate the pool and save the new cache sequence on the pool.
5050
5151
The cache is based on a defaultdict of defaultdict of queue.Queue. We are cautious
5252
that the use of defaultdict is not thread-safe for operations that modify the
@@ -61,10 +61,10 @@ class FastApiAppPool:
6161
"""
6262

6363
def __init__(self):
64-
self._queue_by_db_by_root_path: dict[
65-
str, dict[str, queue.Queue[FastAPI]]
66-
] = defaultdict(lambda: defaultdict(queue.Queue))
67-
self.__cache_sequence = 0
64+
self._queue_by_db_by_root_path: dict[str, dict[str, queue.Queue[FastAPI]]] = (
65+
defaultdict(lambda: defaultdict(queue.Queue))
66+
)
67+
self.__cache_sequences = {}
6868
self._lock = threading.Lock()
6969

7070
def __get_pool(self, env: Environment, root_path: str) -> queue.Queue[FastAPI]:
@@ -102,25 +102,31 @@ def get_app(
102102
finally:
103103
self.__return_app(env, app, root_path)
104104

105-
@property
106-
def cache_sequence(self) -> int:
107-
return self.__cache_sequence
105+
def get_cache_sequence(self, key: str) -> int:
106+
with self._lock:
107+
return self.__cache_sequences.get(key, 0)
108108

109-
@cache_sequence.setter
110-
def cache_sequence(self, value: int) -> None:
111-
if value != self.__cache_sequence:
112-
with self._lock:
113-
self.__cache_sequence = value
109+
def set_cache_sequence(self, key: str, value: int) -> None:
110+
with self._lock:
111+
if (
112+
key not in self.__cache_sequences
113+
or value != self.__cache_sequences[key]
114+
):
115+
self.__cache_sequences[key] = value
114116

115117
def _check_cache(self, env: Environment) -> None:
116-
cache_sequence = env.registry.cache_sequence
117-
if cache_sequence != self.cache_sequence and self.cache_sequence != 0:
118-
_logger.info(
119-
"Cache registry updated, reset fastapi_app pool for the current "
120-
"database"
121-
)
122-
self.invalidate(env)
123-
self.cache_sequence = cache_sequence
118+
cache_sequences = env.registry.cache_sequences
119+
for key, value in cache_sequences.items():
120+
if (
121+
value != self.get_cache_sequence(key)
122+
and self.get_cache_sequence(key) != 0
123+
):
124+
_logger.info(
125+
"Cache registry updated, reset fastapi_app pool for the current "
126+
"database"
127+
)
128+
self.invalidate(env)
129+
self.set_cache_sequence(key, value)
124130

125131
def invalidate(self, env: Environment, root_path: str | None = None) -> None:
126132
db_name = env.cr.dbname

0 commit comments

Comments
 (0)