Skip to content

Commit 7551107

Browse files
committed
✨ Refactor: Rename async engine creation function and update references
1 parent 6d6ff68 commit 7551107

File tree

6 files changed

+137
-12
lines changed

6 files changed

+137
-12
lines changed

packages/service-library/src/servicelib/aiohttp/db_asyncpg_engine.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
SEE migration aiopg->asyncpg https://github.com/ITISFoundation/osparc-simcore/issues/4529
55
"""
66

7-
87
import logging
98
from typing import Final
109

@@ -16,7 +15,7 @@
1615
)
1716
from sqlalchemy.ext.asyncio import AsyncEngine
1817

19-
from ..db_asyncpg_utils import create_async_engine_and_pg_database_ready
18+
from ..db_asyncpg_utils import create_async_engine_and_database_ready
2019
from ..logging_utils import log_context
2120

2221
APP_DB_ASYNC_ENGINE_KEY: Final[str] = f"{__name__ }.AsyncEngine"
@@ -56,7 +55,7 @@ async def connect_to_db(app: web.Application, settings: PostgresSettings) -> Non
5655
"Connecting app[APP_DB_ASYNC_ENGINE_KEY] to postgres with %s",
5756
f"{settings=}",
5857
):
59-
engine = await create_async_engine_and_pg_database_ready(settings)
58+
engine = await create_async_engine_and_database_ready(settings)
6059
_set_async_engine_to_app_state(app, engine)
6160

6261
_logger.info(

packages/service-library/src/servicelib/db_asyncpg_utils.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
from models_library.healthchecks import IsNonResponsive, IsResponsive, LivenessResult
66
from settings_library.postgres import PostgresSettings
7-
from simcore_postgres_database.utils_aiosqlalchemy import ( # type: ignore[import-not-found] # this on is unclear
8-
raise_if_migration_not_ready,
9-
)
107
from sqlalchemy.exc import SQLAlchemyError
118
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
129
from tenacity import retry
@@ -17,7 +14,7 @@
1714

1815

1916
@retry(**PostgresRetryPolicyUponInitialization(_logger).kwargs)
20-
async def create_async_engine_and_pg_database_ready(
17+
async def create_async_engine_and_database_ready(
2118
settings: PostgresSettings,
2219
) -> AsyncEngine:
2320
"""
@@ -26,6 +23,10 @@ async def create_async_engine_and_pg_database_ready(
2623
- waits until db data is migrated (i.e. ready to use)
2724
- returns engine
2825
"""
26+
from simcore_postgres_database.utils_aiosqlalchemy import ( # type: ignore[import-not-found] # this on is unclear
27+
raise_if_migration_not_ready,
28+
)
29+
2930
server_settings = None
3031
if settings.POSTGRES_CLIENT_NAME:
3132
server_settings = {
@@ -43,9 +44,10 @@ async def create_async_engine_and_pg_database_ready(
4344

4445
try:
4546
await raise_if_migration_not_ready(engine)
46-
except Exception:
47+
except Exception as exc:
4748
# NOTE: engine must be closed because retry will create a new engine
4849
await engine.dispose()
50+
exc.add_note("Failed during migration check. Created engine disposed.")
4951
raise
5052

5153
return engine

packages/service-library/src/servicelib/fastapi/db_asyncpg_engine.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
)
88
from sqlalchemy.ext.asyncio import AsyncEngine
99

10-
from ..db_asyncpg_utils import create_async_engine_and_pg_database_ready
10+
from ..db_asyncpg_utils import create_async_engine_and_database_ready
1111
from ..logging_utils import log_context
1212

1313
_logger = logging.getLogger(__name__)
@@ -19,7 +19,7 @@ async def connect_to_db(app: FastAPI, settings: PostgresSettings) -> None:
1919
logging.DEBUG,
2020
f"Connecting and migraging {settings.dsn_with_async_sqlalchemy}",
2121
):
22-
engine = await create_async_engine_and_pg_database_ready(settings)
22+
engine = await create_async_engine_and_database_ready(settings)
2323

2424
app.state.engine = engine
2525
_logger.debug(
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import logging
2+
from collections.abc import AsyncIterator
3+
4+
from fastapi import FastAPI
5+
from fastapi_lifespan_manager import LifespanManager, State
6+
from servicelib.logging_utils import log_catch, log_context
7+
from settings_library.postgres import PostgresSettings
8+
from sqlalchemy.ext.asyncio import AsyncEngine
9+
10+
from ..db_asyncpg_utils import create_async_engine_and_database_ready
11+
12+
_logger = logging.getLogger(__name__)
13+
14+
15+
postgres_lifespan = LifespanManager()
16+
17+
18+
@postgres_lifespan.add
19+
async def setup_postgres_database(app: FastAPI) -> AsyncIterator[State]:
20+
with log_context(_logger, logging.INFO, f"{__name__}"):
21+
22+
pg_settings: PostgresSettings = app.state.settings.CATALOG_POSTGRES
23+
24+
async_engine: AsyncEngine = await create_async_engine_and_database_ready(
25+
pg_settings
26+
)
27+
28+
yield {"postgres.async_engine": async_engine}
29+
30+
with log_catch(_logger, reraise=False):
31+
await async_engine.dispose()
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# pylint: disable=protected-access
2+
# pylint: disable=redefined-outer-name
3+
# pylint: disable=too-many-arguments
4+
# pylint: disable=unused-argument
5+
# pylint: disable=unused-variable
6+
7+
from typing import Annotated, Any
8+
9+
import pytest
10+
from asgi_lifespan import LifespanManager as ASGILifespanManager
11+
from fastapi import FastAPI
12+
from fastapi_lifespan_manager import LifespanManager, State
13+
from pydantic import Field
14+
from pytest_mock import MockerFixture, MockType
15+
from pytest_simcore.helpers.monkeypatch_envs import setenvs_from_dict
16+
from pytest_simcore.helpers.typing_env import EnvVarsDict
17+
from servicelib.fastapi.postgres_lifespan import postgres_lifespan
18+
from settings_library.application import BaseApplicationSettings
19+
from settings_library.postgres import PostgresSettings
20+
21+
22+
@pytest.fixture
23+
def mock_create_async_engine_and_database_ready(mocker: MockerFixture) -> MockType:
24+
return mocker.patch(
25+
"servicelib.fastapi.postgres_lifespan.create_async_engine_and_database_ready",
26+
return_value=mocker.AsyncMock(),
27+
)
28+
29+
30+
@pytest.fixture
31+
def app_environment(monkeypatch: pytest.MonkeyPatch) -> EnvVarsDict:
32+
return setenvs_from_dict(
33+
monkeypatch, PostgresSettings.model_json_schema()["examples"][0]
34+
)
35+
36+
37+
async def test_setup_postgres_database_in_an_app(
38+
is_pdb_enabled: bool,
39+
app_environment: EnvVarsDict,
40+
mock_create_async_engine_and_database_ready: MockType,
41+
):
42+
assert app_environment
43+
44+
@postgres_lifespan.add
45+
async def my_db_setup(app: FastAPI, state: State):
46+
app.state.my_db_engine = state["postgres.async_engine"]
47+
48+
assert (
49+
app.state.my_db_engine
50+
== mock_create_async_engine_and_database_ready.return_value
51+
)
52+
53+
yield
54+
55+
# compose lifespans
56+
app_lifespan = LifespanManager()
57+
app_lifespan.include(postgres_lifespan)
58+
59+
# define app
60+
app = FastAPI(lifespan=app_lifespan)
61+
62+
# settings
63+
class AppSettings(BaseApplicationSettings):
64+
CATALOG_POSTGRES: Annotated[
65+
PostgresSettings,
66+
Field(json_schema_extra={"auto_default_from_env": True}),
67+
]
68+
69+
app.state.settings = AppSettings.create_from_envs()
70+
71+
async with ASGILifespanManager(
72+
app,
73+
startup_timeout=None if is_pdb_enabled else 10,
74+
shutdown_timeout=None if is_pdb_enabled else 10,
75+
) as asgi_manager:
76+
# Verify that the async engine was created
77+
mock_create_async_engine_and_database_ready.assert_called_once_with(
78+
app.state.settings.CATALOG_POSTGRES
79+
)
80+
81+
# Verify that the async engine is in the lifespan manager state
82+
assert "postgres.async_engine" in asgi_manager._state # noqa: SLF001
83+
assert app.state.my_db_engine
84+
assert (
85+
app.state.my_db_engine
86+
== asgi_manager._state["postgres.async_engine"] # noqa: SLF001
87+
)
88+
89+
# Verify that the engine was disposed
90+
async_engine: Any = mock_create_async_engine_and_database_ready.return_value
91+
async_engine.dispose.assert_called_once()

services/director-v2/src/simcore_service_director_v2/modules/db/_asyncpg.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import logging
22

33
from fastapi import FastAPI
4-
from servicelib.db_asyncpg_utils import create_async_engine_and_pg_database_ready
4+
from servicelib.db_asyncpg_utils import (
5+
create_async_engine_and_database_ready,
6+
)
57
from servicelib.logging_utils import log_context
68
from settings_library.postgres import PostgresSettings
79
from simcore_postgres_database.utils_aiosqlalchemy import get_pg_engine_stateinfo
@@ -15,7 +17,7 @@ async def asyncpg_connect_to_db(app: FastAPI, settings: PostgresSettings) -> Non
1517
logging.DEBUG,
1618
f"Connecting and migraging {settings.dsn_with_async_sqlalchemy}",
1719
):
18-
engine = await create_async_engine_and_pg_database_ready(settings)
20+
engine = await create_async_engine_and_database_ready(settings)
1921

2022
app.state.asyncpg_engine = engine
2123
_logger.debug(

0 commit comments

Comments
 (0)