Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

⚡️(api) optimize create/bulk dynamic endpoints number of db queries #297

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to
number of database queries
- CLI: sort groups and operational units alphabetically in the `list-groups`
command
- Decrease the number of database queries for dynamic endpoints

## [0.16.0] - 2024-12-12

Expand Down
20 changes: 14 additions & 6 deletions src/api/qualicharge/api/v1/routers/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,11 +324,13 @@ async def create_status(
detail="Attached point of charge does not exist",
)
db_status = Status(**status.model_dump(exclude={"id_pdc_itinerance"}))
# Store status id so that we do not need to perform another request
db_status_id = db_status.id
db_status.point_de_charge_id = pdc_id
session.add(db_status)
session.commit()

return DynamiqueItemCreatedResponse(id=db_status.id)
return DynamiqueItemCreatedResponse(id=db_status_id)


@router.post("/status/bulk", status_code=fa_status.HTTP_201_CREATED, tags=["Status"])
Expand Down Expand Up @@ -365,16 +367,18 @@ async def create_status_bulk(

# Create all statuses
db_statuses = []
db_status_ids = []
for status in statuses:
db_status = Status(**status.model_dump(exclude={"id_pdc_itinerance"}))
db_status_ids.append(db_status.id)
db_status.point_de_charge_id = db_pdcs[status.id_pdc_itinerance]
db_statuses.append(db_status)
session.add_all(db_statuses)
session.commit()

return DynamiqueItemsCreatedResponse(
size=len(db_statuses),
items=[s.id for s in db_statuses],
size=len(db_status_ids),
items=db_status_ids,
)


Expand Down Expand Up @@ -403,11 +407,13 @@ async def create_session(
detail="Attached point of charge does not exist",
)
db_qc_session = QCSession(**session.model_dump(exclude={"id_pdc_itinerance"}))
# Store session id so that we do not need to perform another request
db_qc_session_id = db_qc_session.id
db_qc_session.point_de_charge_id = pdc_id
db_session.add(db_qc_session)
db_session.commit()

return DynamiqueItemCreatedResponse(id=db_qc_session.id)
return DynamiqueItemCreatedResponse(id=db_qc_session_id)


@router.post("/session/bulk", status_code=fa_status.HTTP_201_CREATED, tags=["Session"])
Expand Down Expand Up @@ -444,14 +450,16 @@ async def create_session_bulk(

# Create all statuses
db_qc_sessions = []
db_qc_session_ids = []
for session in sessions:
db_qc_session = QCSession(**session.model_dump(exclude={"id_pdc_itinerance"}))
db_qc_session_ids.append(db_qc_session.id)
db_qc_session.point_de_charge_id = db_pdcs[session.id_pdc_itinerance]
db_qc_sessions.append(db_qc_session)
db_session.add_all(db_qc_sessions)
db_session.commit()

return DynamiqueItemsCreatedResponse(
size=len(db_qc_sessions),
items=[s.id for s in db_qc_sessions],
size=len(db_qc_session_ids),
items=db_qc_session_ids,
)
111 changes: 111 additions & 0 deletions src/api/tests/api/v1/routers/test_dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from qualicharge.auth.factories import GroupFactory
from qualicharge.auth.schemas import GroupOperationalUnit, ScopesEnum, User
from qualicharge.conf import settings
from qualicharge.db import SAQueryCounter
from qualicharge.factories.dynamic import (
SessionCreateFactory,
StatusCreateFactory,
Expand Down Expand Up @@ -763,6 +764,30 @@ def test_create_status_for_superuser(db_session, client_auth):
assert response.json() == {"id": str(db_status.id)}


def test_create_status_number_of_queries(db_session, client_auth):
"""Test the /status/ create endpoint number of db queries."""
id_pdc_itinerance = "FR911E1111ER1"
qc_status = StatusCreateFactory.build(id_pdc_itinerance=id_pdc_itinerance)

# Create point of charge
save_statique(
db_session, StatiqueFactory.build(id_pdc_itinerance=id_pdc_itinerance)
)

# Create a new status
with SAQueryCounter(db_session.connection()) as counter:
response = client_auth.post(
"/dynamique/status/", json=json.loads(qc_status.model_dump_json())
)
assert response.status_code == status.HTTP_201_CREATED
# We expect 3 database requests:
# 1. select request user
# 2. select point of charge
# 3. insert status
expected = 3
assert counter.count == expected


@pytest.mark.parametrize(
"client_auth",
(
Expand Down Expand Up @@ -938,6 +963,37 @@ def test_create_status_bulk_for_superuser(db_session, client_auth):
assert db_status.etat_prise_type_ef == qc_status.etat_prise_type_ef


def test_create_status_bulk_number_of_queries(db_session, client_auth):
"""Test the /status/bulk create endpoint number of db queries."""
qc_statuses = StatusCreateFactory.batch(3)

# Create points of charge
save_statiques(
db_session,
[
StatiqueFactory.build(id_pdc_itinerance=s.id_pdc_itinerance)
for s in qc_statuses
],
)

# Assert no status exist
assert db_session.exec(select(func.count(Status.id))).one() == 0

# We expect the same answer as one point of charge does not exist
with SAQueryCounter(db_session.connection()) as counter:
response = client_auth.post(
"/dynamique/status/bulk",
json=[json.loads(s.model_dump_json()) for s in qc_statuses],
)
assert response.status_code == status.HTTP_201_CREATED
# We expect 3 database requests:
# 1. select request user
# 2. select points of charge
# 3. insert statuses
expected = 3
assert counter.count == expected


@pytest.mark.parametrize(
"client_auth",
(
Expand Down Expand Up @@ -1178,6 +1234,30 @@ def test_create_session_for_superuser(db_session, client_auth):
assert response.json() == {"id": str(db_qc_session.id)}


def test_create_session_number_of_queries(db_session, client_auth):
"""Test the /session/ create endpoint number of db queries."""
id_pdc_itinerance = "FR911E1111ER1"
qc_session = SessionCreateFactory.build(id_pdc_itinerance=id_pdc_itinerance)

# Create point of charge
save_statique(
db_session, StatiqueFactory.build(id_pdc_itinerance=id_pdc_itinerance)
)

# Create a new status
with SAQueryCounter(db_session.connection()) as counter:
response = client_auth.post(
"/dynamique/session/", json=json.loads(qc_session.model_dump_json())
)
assert response.status_code == status.HTTP_201_CREATED
# We expect 3 database requests:
# 1. select request user
# 2. select point of charge
# 3. insert session
expected = 3
assert counter.count == expected


@pytest.mark.parametrize(
"client_auth",
(
Expand Down Expand Up @@ -1348,6 +1428,37 @@ def test_create_session_bulk_for_superuser(db_session, client_auth):
assert db_qc_session.energy == qc_session.energy


def test_create_session_bulk_number_of_queries(db_session, client_auth):
"""Test the /session/bulk create endpoint number of db queries."""
qc_sessions = SessionCreateFactory.batch(3)

# Create points of charge
save_statiques(
db_session,
[
StatiqueFactory.build(id_pdc_itinerance=s.id_pdc_itinerance)
for s in qc_sessions
],
)

# Assert no session exist
assert db_session.exec(select(func.count(Session.id))).one() == 0

# We expect the same answer as one point of charge does not exist
with SAQueryCounter(db_session.connection()) as counter:
response = client_auth.post(
"/dynamique/session/bulk",
json=[json.loads(s.model_dump_json()) for s in qc_sessions],
)
assert response.status_code == status.HTTP_201_CREATED
# We expect 3 database requests:
# 1. select request user
# 2. select points of charge
# 3. insert sessions
expected = 3
assert counter.count == expected


@pytest.mark.parametrize(
"client_auth",
(
Expand Down
Loading