Skip to content

fix: skip course_meeting churn when schedule unchanged#405

Merged
reybahl merged 1 commit intomasterfrom
location-freeze-optimizations
Mar 26, 2026
Merged

fix: skip course_meeting churn when schedule unchanged#405
reybahl merged 1 commit intomasterfrom
location-freeze-optimizations

Conversation

@reybahl
Copy link
Copy Markdown
Member

@reybahl reybahl commented Mar 26, 2026

should speed up ferry run when locations are frozen

Summary by CodeRabbit

  • Bug Fixes
    • Improved change detection for course meetings when location freezing is enabled. The system now focuses on structural course changes rather than location data variations, resulting in more accurate course update tracking.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

The meeting comparison logic in sync_course_meetings_incremental is updated to conditionally exclude location columns when freeze_locations is enabled. When freezing, comparisons use only structural columns (start_time, end_time, days_of_week); otherwise, the full comparison including location_id is retained.

Changes

Cohort / File(s) Summary
Meeting Comparison Logic
ferry/database/sync_db_courses.py
Modified sync_course_meetings_incremental to apply conditional sort keys and column subsets based on freeze_locations flag, preventing location differences from triggering course change detection when locations are frozen.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 When locations freeze in place,
We sort by time, not by space—
Meetings stay true, unchanged and bright,
No phantom shifts in morning light! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: skip course_meeting churn when schedule unchanged' accurately describes the main change: optimizing course meeting comparisons to avoid unnecessary updates when schedules haven't changed.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch location-freeze-optimizations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
ferry/database/sync_db_courses.py (1)

472-482: Comparison logic correctly handles both modes.

The conditional comparison appropriately:

  • Compares only structural columns (start_time, end_time, days_of_week) when freezing locations, so NULL vs actual location_id values don't trigger unnecessary churn
  • Falls back to full equals() when not freezing

One minor readability suggestion: consider defining structural_cols alongside sort_cols (lines 449-453) to make their relationship explicit and reduce nesting.

💡 Optional refactor for clarity
         # With freeze_locations, new rows have null location_id but DB rows have IDs; a
         # full-frame equals() would mark every course changed. Compare times + days only.
-        sort_cols = (
-            ["start_time", "end_time", "days_of_week"]
-            if freeze_locations
-            else ["start_time", "end_time", "location_id"]
-        )
+        if freeze_locations:
+            sort_cols = ["start_time", "end_time", "days_of_week"]
+            compare_cols = ["start_time", "end_time", "days_of_week"]
+        else:
+            sort_cols = ["start_time", "end_time", "location_id"]
+            compare_cols = None  # use full DataFrame comparison
         old_meetings = (
             old_subset.iloc[:]
             .sort_values(sort_cols)
             .reset_index(drop=True)
         )
         new_meetings = (
             new_subset.iloc[:]
             .sort_values(sort_cols)
             .reset_index(drop=True)
         )
 
         # Check if any old meetings have missing location IDs - if so, treat as changed
         if old_meetings["location_id"].apply(safe_isna).any():
             changed_course_ids.add(course_id)
             continue
 
         # Compare the meeting sets (excluding course_id)
         try:
-            if freeze_locations:
-                structural_cols = ["start_time", "end_time", "days_of_week"]
-                if not old_meetings[structural_cols].equals(
-                    new_meetings[structural_cols]
-                ):
-                    changed_course_ids.add(course_id)
-            elif not old_meetings.equals(new_meetings):
+            if compare_cols is not None:
+                is_equal = old_meetings[compare_cols].equals(new_meetings[compare_cols])
+            else:
+                is_equal = old_meetings.equals(new_meetings)
+            if not is_equal:
                 changed_course_ids.add(course_id)
         except (ValueError, TypeError):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ferry/database/sync_db_courses.py` around lines 472 - 482, Move the
structural_cols definition up next to sort_cols to improve readability: declare
structural_cols = ["start_time", "end_time", "days_of_week"] alongside the
existing sort_cols definition, then reuse that variable in the comparison block
that checks freeze_locations (the block that currently sets structural_cols
inside the if). Keep the existing comparison logic using
old_meetings[structural_cols].equals(new_meetings[structural_cols]) and the else
branch that uses old_meetings.equals(new_meetings), and leave the exception
handler adding course_id to changed_course_ids unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ferry/database/sync_db_courses.py`:
- Around line 472-482: Move the structural_cols definition up next to sort_cols
to improve readability: declare structural_cols = ["start_time", "end_time",
"days_of_week"] alongside the existing sort_cols definition, then reuse that
variable in the comparison block that checks freeze_locations (the block that
currently sets structural_cols inside the if). Keep the existing comparison
logic using old_meetings[structural_cols].equals(new_meetings[structural_cols])
and the else branch that uses old_meetings.equals(new_meetings), and leave the
exception handler adding course_id to changed_course_ids unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78d5cf26-320c-4ef7-afcf-3cf32b016fa5

📥 Commits

Reviewing files that changed from the base of the PR and between fa1f52f and 9e69935.

📒 Files selected for processing (1)
  • ferry/database/sync_db_courses.py

@reybahl reybahl merged commit a3ea142 into master Mar 26, 2026
7 checks passed
@reybahl reybahl deleted the location-freeze-optimizations branch March 26, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant