diff --git a/.jules/bolt.md b/.jules/bolt.md index bf784525..a6dabbfc 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -25,3 +25,7 @@ ## 2025-07-15 - Fast Bounding Box Pre-filter **Learning:** Calculating great circle distance (Haversine) for every issue against a target location is computationally expensive (O(N) with heavy math ops like sin, cos, atan2). In high-traffic aggregations, this can become a bottleneck. **Action:** Use a fast bounding box pre-filter (`get_bounding_box` with a 5% epsilon) to quickly discard issues that are definitely outside the search radius before running the expensive exact haversine distance calculation. + +## 2025-07-21 - N+1 Queries in Periodic Jobs +**Learning:** During periodic tasks that evaluate many database records (like checking SLA deadlines in an escalation engine), accessing related models (like `grievance.jurisdiction.level`) without eager loading causes N+1 query bottlenecks. This can severely degrade performance as the number of records grows. +**Action:** Use SQLAlchemy's `joinedload` (e.g., `.options(joinedload(Model.relationship))`) in the initial query to pre-fetch related data in a single SQL join instead of making separate queries for each record. diff --git a/backend/escalation_engine.py b/backend/escalation_engine.py index 67137b9b..34780949 100644 --- a/backend/escalation_engine.py +++ b/backend/escalation_engine.py @@ -5,7 +5,7 @@ import datetime from typing import List, Dict, Any, Optional -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, joinedload from sqlalchemy import and_, or_ from backend.models import Grievance, Jurisdiction, EscalationAudit, GrievanceStatus, JurisdictionLevel, EscalationReason, SeverityLevel from backend.database import SessionLocal @@ -150,7 +150,7 @@ def _get_grievances_for_evaluation(self, db: Session) -> List[Grievance]: now = datetime.datetime.now(datetime.timezone.utc) # Get grievances that are active and past SLA deadline - return db.query(Grievance).filter( + return db.query(Grievance).options(joinedload(Grievance.jurisdiction)).filter( and_( Grievance.status.in_([GrievanceStatus.OPEN, GrievanceStatus.IN_PROGRESS, GrievanceStatus.ESCALATED]), Grievance.sla_deadline < now