♻️ Simplify database readiness checks (#2460)

This commit is contained in:
Sebastián Ramírez
2026-08-28 13:53:10 +02:00
committed by GitHub
parent 2ccfa25845
commit f8dd304eeb
11 changed files with 3 additions and 164 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
with:
version: "latest-known"
- run: docker compose down -v --remove-orphans
- run: docker compose up -d db mailpit
- run: docker compose up -d --wait db mailpit
- name: Migrate DB
run: uv run bash scripts/prestart.sh
working-directory: backend
+1 -1
View File
@@ -78,7 +78,7 @@ If your stack is already up and you just want to run the tests, you can use:
docker compose exec backend bash scripts/tests-start.sh
```
The `/app/backend/scripts/tests-start.sh` script calls `pytest` after making sure that the rest of the stack is running. If you need to pass extra arguments to `pytest`, you can pass them to that command and they will be forwarded.
The `/app/backend/scripts/tests-start.sh` script calls `pytest`. If you need to pass extra arguments to `pytest`, you can pass them to that command and they will be forwarded.
For example, to stop on first error:
-39
View File
@@ -1,39 +0,0 @@
import logging
from sqlalchemy import Engine
from sqlmodel import Session, select
from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
from app.core.db import engine
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
max_tries = 60 * 5 # 5 minutes
wait_seconds = 1
@retry(
stop=stop_after_attempt(max_tries),
wait=wait_fixed(wait_seconds),
before=before_log(logger, logging.INFO),
after=after_log(logger, logging.WARN),
)
def init(db_engine: Engine) -> None:
try:
with Session(db_engine) as session:
# Try to create session to check if DB is awake
session.exec(select(1))
except Exception as e:
logger.error(e)
raise e
def main() -> None:
logger.info("Initializing service")
init(engine)
logger.info("Service finished initializing")
if __name__ == "__main__":
main()
+1 -1
View File
@@ -4,7 +4,7 @@ from app import crud
from app.core.config import settings
from app.models import User, UserCreate
engine = create_engine(str(settings.DATABASE_URL))
engine = create_engine(str(settings.DATABASE_URL), pool_pre_ping=True)
# make sure all SQLModel models are imported (app.models) before initializing DB
-39
View File
@@ -1,39 +0,0 @@
import logging
from sqlalchemy import Engine
from sqlmodel import Session, select
from tenacity import after_log, before_log, retry, stop_after_attempt, wait_fixed
from app.core.db import engine
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
max_tries = 60 * 5 # 5 minutes
wait_seconds = 1
@retry(
stop=stop_after_attempt(max_tries),
wait=wait_fixed(wait_seconds),
before=before_log(logger, logging.INFO),
after=after_log(logger, logging.WARN),
)
def init(db_engine: Engine) -> None:
try:
# Try to create session to check if DB is awake
with Session(db_engine) as session:
session.exec(select(1))
except Exception as e:
logger.error(e)
raise e
def main() -> None:
logger.info("Initializing service")
init(engine)
logger.info("Service finished initializing")
if __name__ == "__main__":
main()
-1
View File
@@ -7,7 +7,6 @@ dependencies = [
"fastapi[standard]>=0.141.1,<1.0.0",
"python-multipart<1.0.0,>=0.0.27",
"email-validator<3.0.0.0,>=2.1.0.post1",
"tenacity<10.0.0,>=8.2.3",
"pydantic>2.0",
"emails>=1.1.2,<2.0",
"jinja2<4.0.0,>=3.1.4",
-3
View File
@@ -3,9 +3,6 @@
set -e
set -x
# Let the DB start
python app/backend_pre_start.py
# Run migrations
alembic upgrade head
-2
View File
@@ -2,6 +2,4 @@
set -e
set -x
python app/tests_pre_start.py
bash scripts/test.sh "$@"
@@ -1,33 +0,0 @@
from unittest.mock import MagicMock, patch
from sqlmodel import select
from app.backend_pre_start import init, logger
def test_init_successful_connection() -> None:
engine_mock = MagicMock()
session_mock = MagicMock()
session_mock.__enter__.return_value = session_mock
select1 = select(1)
with (
patch("app.backend_pre_start.Session", return_value=session_mock),
patch("app.backend_pre_start.select", return_value=select1),
patch.object(logger, "info"),
patch.object(logger, "error"),
patch.object(logger, "warn"),
):
try:
init(engine_mock)
connection_successful = True
except Exception:
connection_successful = False
assert connection_successful, (
"The database connection should be successful and not raise an exception."
)
session_mock.exec.assert_called_once_with(select1)
@@ -1,33 +0,0 @@
from unittest.mock import MagicMock, patch
from sqlmodel import select
from app.tests_pre_start import init, logger
def test_init_successful_connection() -> None:
engine_mock = MagicMock()
session_mock = MagicMock()
session_mock.__enter__.return_value = session_mock
select1 = select(1)
with (
patch("app.tests_pre_start.Session", return_value=session_mock),
patch("app.tests_pre_start.select", return_value=select1),
patch.object(logger, "info"),
patch.object(logger, "error"),
patch.object(logger, "warn"),
):
try:
init(engine_mock)
connection_successful = True
except Exception:
connection_successful = False
assert connection_successful, (
"The database connection should be successful and not raise an exception."
)
session_mock.exec.assert_called_once_with(select1)
Generated
-11
View File
@@ -82,7 +82,6 @@ dependencies = [
{ name = "python-multipart" },
{ name = "sentry-sdk", extra = ["fastapi"] },
{ name = "sqlmodel" },
{ name = "tenacity" },
]
[package.dev-dependencies]
@@ -110,7 +109,6 @@ requires-dist = [
{ name = "python-multipart", specifier = ">=0.0.27,<1.0.0" },
{ name = "sentry-sdk", extras = ["fastapi"], specifier = ">=2.66.1,<3.0.0" },
{ name = "sqlmodel", specifier = ">=0.0.39,<1.0.0" },
{ name = "tenacity", specifier = ">=8.2.3,<10.0.0" },
]
[package.metadata.requires-dev]
@@ -1284,15 +1282,6 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", hash = "sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6", size = 73632, upload-time = "2026-06-12T09:23:10.017Z" },
]
[[package]]
name = "tenacity"
version = "9.1.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/47/c6/ee486fd809e357697ee8a44d3d69222b344920433d3b6666ccd9b374630c/tenacity-9.1.4.tar.gz", hash = "sha256:adb31d4c263f2bd041081ab33b498309a57c77f9acf2db65aadf0898179cf93a", size = 49413, upload-time = "2026-02-07T10:45:33.841Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d7/c1/eb8f9debc45d3b7918a32ab756658a0904732f75e555402972246b0b8e71/tenacity-9.1.4-py3-none-any.whl", hash = "sha256:6095a360c919085f28c6527de529e76a06ad89b23659fa881ae0649b867a9d55", size = 28926, upload-time = "2026-02-07T10:45:32.24Z" },
]
[[package]]
name = "ty"
version = "0.0.54"