ref(crons): Reorganize incident creation

getsentry/sentry#80528
Golden recall: 50.0% Extra findings: 4

Golden Comments (1/2 found)

# Comment Severity Status FriendlyReviewer Detail
#1 get_monitor_environment_context modifies a copy of config (schedule_type display) but returns the original monitor.config — modification silently lost HIGH ✓ Found FriendlyReviewer: "modifies a local copy of config... but returns the original" (incident_occurrence.py:165).
#2 try_incident_threshold re-fetches MonitorCheckIn.objects.filter(id__in=[c["id"]...]) when previous_checkins already contains id, date_added, status — unnecessary DB query LOW × Missed FriendlyReviewer did not flag this performance optimization.

Supplementary Findings (7 findings)

Finding File Severity Legitimate?
Race condition: date_added__lte without timestamp guard → concurrent check-ins skew threshold incidents.py:30 HIGH ✓ Real design issue
Occurrence duplication: loop for checkin in checkins: emits N Kafka occurrences per incident trigger incidents.py:78 HIGH ✓ Real bug — should emit a single occurrence
get_failure_reason crashes with IndexError if all statuses are outside HUMAN_FAILURE_STATUS_MAP incident_occurrence.py:127 MEDIUM ✓ Unhandled edge case
Signal monitor_environment_failed.send() sent even when monitor or env is muted incidents.py:88 MEDIUM ✓ Design inconsistency
cast(list[SimpleCheckIn], ...) on queryset .values() — ineffective type hint incidents.py:30 LOW ✓ Maintenance concern
any([...]) materializes entire list before short-circuiting — use a generator expression incidents.py:49 LOW ✓ Minor optimization
Fallback failure_issue_threshold uses not failure_issue_threshold0 treated as falsy mark_failed.py:20 LOW ✓ Logic ambiguity

Honest Analysis

FriendlyReviewer found the most important functional bug (#1: lost config modification) with a precise description. The missed golden #2 (redundant DB query) is a minor performance optimization — FriendlyReviewer focused on correctness bugs (lost config, race condition, occurrence duplication) rather than performance polish. This is a reasonable tradeoff, but it means the review will miss low-severity optimizations that a human might spot.

All 7 supplementary findings are legitimate. The 2 additional HIGH findings (race condition and occurrence duplication) describe real design problems in the refactoring that the golden set did not cover. The remaining findings (IndexError edge case, signal ordering, type hint inaccuracy, generator expression, truthiness pitfall) are a mixed bag: the edge case and signal ordering are genuinely useful, while the type hint nitpick and the generator expression suggestion are the kind of minor observations that any linter or type checker could provide.

FriendlyReviewer's full review

As posted on the PR — the complete output from FriendlyReviewer.

friendly-reviewer bot commented 11 Jun 2026

Hi there 👋

🌥️ Tech
3 high, 2 medium, 3 low

The refactoring extracts incident threshold logic and occurrence creation from mark_failed.py into new modules, with an architecturally sound call chain. However, several correctness issues require attention: a data integrity bug where a config modification is silently discarded, a race condition in threshold evaluation, duplicate occurrence emissions per incident trigger, an edge-case crash in get_failure_reason, misleading type annotations, a missed performance optimization, an unconditional signal firing, and a robustness concern in threshold fallback logic. Address these findings to ensure reliability and maintainability.


These might need a close look

  • 🔴 src/sentry/monitors/logic/incident_occurrence.py (L165)
    BUG: get_monitor_environment_context() modifies a local copy of config to convert schedule_type to a display value, but returns the original monitor_environment.monitor.config instead of the local copy. The conversion is wasted. Change to return the local config variable.
  • 🔴 src/sentry/monitors/logic/incidents.py (L30)
    RACE CONDITION: The query for previous_checkins uses date_added__lte=failed_checkin.date_added without a timestamp guard similar to mark_failed(), allowing concurrent check-ins to distort threshold evaluation. Consider using select_for_update() or deterministic ordering.
  • 🔴 src/sentry/monitors/logic/incidents.py (L78)
    OCCURRENCE DUPLICATION: The loop for checkin in checkins: iterates over all check-ins in the threshold window and calls create_incident_occurrence() for each, producing multiple Kafka occurrences per incident trigger. Emit only one occurrence per trigger or document the intentional behavior.

Worth checking

  • 🟡 src/sentry/monitors/logic/incident_occurrence.py (L127)
    EDGE CASE CRASH: get_failure_reason() can raise IndexError if all check-ins have a status not in HUMAN_FAILURE_STATUS_MAP (e.g., UNKNOWN). Add a fallback for empty status_counts.
  • 🟡 src/sentry/monitors/logic/incidents.py (L88)
    SIDE EFFECT ORDERING: monitor_environment_failed.send() is called unconditionally even when the monitor is muted, but occurrence creation is gated. Consider moving the signal inside the mute guard or document that it fires regardless.

Small things (take or leave)

  • 🔵 src/sentry/monitors/logic/incidents.py (L30)
    TYPE UNSAFE: cast(list[SimpleCheckIn], ...) on .values() queryset is misleading. Use list(cast(QuerySet[SimpleCheckIn], ...)) or a proper conversion.
  • 🔵 src/sentry/monitors/logic/incidents.py (L49)
    PERFORMANCE: any([...]) materializes the full list before checking. Use a generator expression any(checkin["status"] == CheckInStatus.OK for checkin in previous_checkins) for short-circuit evaluation.
  • 🔵 src/sentry/monitors/logic/mark_failed.py (L20)
    ROBUSTNESS: The fallback if not failure_issue_threshold: coerces an explicit 0 to 1. Use if failure_issue_threshold is None or failure_issue_threshold < 1: to clarify intent.