Problem Statement
UserDBConnector.get_users_for_notifications() in quantara/web_app/db/crud/user.py:26-43 joins User, Position, and TelegramUser tables but uses default lazy loading, triggering N+1 queries when accessing related attributes.
Evidence
# quantara/web_app/db/crud/user.py:26-43
def get_users_for_notifications(self) -> list[tuple[str, str]]:
results = (
db.query(User.contract_address, TelegramUser.telegram_id)
.join(Position, Position.user_id == User.id)
.join(TelegramUser, TelegramUser.wallet_id == User.wallet_id)
.filter(
Position.status == Status.OPENED.value,
TelegramUser.is_allowed_notification == True,
)
.distinct()
.all()
)
return results # No joinedload — lazy loading will trigger N+1
Impact
Medium — unnecessary database load. When notification dispatch iterates over results and accesses related objects (User, Position, TelegramUser), each access triggers a separate query. With N users having open positions, this results in N+1 queries instead of 1.
Proposed Solution
Add sqlalchemy.orm.joinedload() to eagerly load related Position and TelegramUser data in the initial query, reducing N+1 queries to a single JOIN query. Use .options(joinedload(...)) on the query.
Acceptance Criteria
File Map
quantara/web_app/db/crud/user.py:26-43 — add joinedload() to query
Testing Strategy
- Unit: Verify query with
joinedload produces correct results
- Integration: Enable SQLAlchemy query logging, verify single query instead of N+1
Security Considerations
No security impact.
Definition of Done
Labels: performance
Priority: Medium
Difficulty: Intermediate
Estimated Effort: 1h
Problem Statement
UserDBConnector.get_users_for_notifications()inquantara/web_app/db/crud/user.py:26-43joinsUser,Position, andTelegramUsertables but uses default lazy loading, triggering N+1 queries when accessing related attributes.Evidence
Impact
Medium — unnecessary database load. When notification dispatch iterates over results and accesses related objects (User, Position, TelegramUser), each access triggers a separate query. With N users having open positions, this results in N+1 queries instead of 1.
Proposed Solution
Add
sqlalchemy.orm.joinedload()to eagerly load related Position and TelegramUser data in the initial query, reducing N+1 queries to a single JOIN query. Use.options(joinedload(...))on the query.Acceptance Criteria
joinedload()added to eagerly load related Position and TelegramUser dataFile Map
quantara/web_app/db/crud/user.py:26-43— addjoinedload()to queryTesting Strategy
joinedloadproduces correct resultsSecurity Considerations
No security impact.
Definition of Done
Labels: performance
Priority: Medium
Difficulty: Intermediate
Estimated Effort: 1h