Probe cycle discrimination + scheduler health snapshot#50
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughImplements discriminated ISO probe cycle outcomes (CycleResult with SUCCESS/EMPTY/FAILED), adds a frozen SchedulerSnapshot published under a lock, updates Scheduler to record cycle status/error and publish snapshots after seed/poll, wires snapshot into /health extra fields, and updates tests/docs accordingly. ChangesDiscriminated Cycles and Health Snapshots
Sequence DiagramsequenceDiagram
participant Scheduler
participant ISOProber
participant HealthEndpoint
Scheduler->>ISOProber: run_cycle()
alt Probe Success
ISOProber->>ISOProber: perform concurrent HEAD probes, collect hits
ISOProber-->>Scheduler: CycleResult(SUCCESS, results=hits)
else Probe Empty
ISOProber->>ISOProber: probes return no hits
ISOProber-->>Scheduler: CycleResult(EMPTY)
else Probe Failed
ISOProber->>ISOProber: exception during cycle
ISOProber-->>Scheduler: CycleResult(FAILED, error=msg)
end
Scheduler->>Scheduler: _probe_hits_from_cycle() (record status/error)
Scheduler->>Scheduler: _record_probe_cycle_completion() (store probe_stats)
alt non-FAILED cycle
Scheduler->>Scheduler: _mark_poll_successful_if_probe_ok()
end
Scheduler->>Scheduler: _publish_health_snapshot()
HealthEndpoint->>Scheduler: health_snapshot()
Scheduler-->>HealthEndpoint: SchedulerSnapshot dict
HealthEndpoint->>HealthEndpoint: merge with mq/db metrics
HealthEndpoint-->>Client: /health response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/paperscout/monitor.py`:
- Around line 128-136: The health snapshot fallback uses
dict(_HEALTH_SNAPSHOT_DEFAULTS) which only performs a shallow copy and ends up
sharing the nested probe_stats dict; change the construction in the code that
creates the fallback (where dict(_HEALTH_SNAPSHOT_DEFAULTS) is used) to produce
a deep/independent copy of nested structures—either use copy.deepcopy on
_HEALTH_SNAPSHOT_DEFAULTS or rebuild the dict while explicitly creating a new
empty dict for "probe_stats" (e.g., {**_HEALTH_SNAPSHOT_DEFAULTS, "probe_stats":
{}}) so each consumer gets its own probe_stats instead of a shared mutable
object.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f831e1c1-21d8-46f5-b3dd-da55b443e852
📒 Files selected for processing (9)
CHANGELOG.mddocs/architecture.mdsrc/paperscout/__main__.pysrc/paperscout/models.pysrc/paperscout/monitor.pysrc/paperscout/sources.pytests/test_health.pytests/test_monitor.pytests/test_sources.py
|
@coderabbitai don't skip. full review |
|
Tip For best results, initiate chat on the files or code changes.
PR
|
| File | Location | Note |
|---|---|---|
sources.py |
run_cycle() |
The PROBE-CYCLE-SUMMARY JSON log is emitted after the PROBE-SUCCESS / PROBE-EMPTY log. Reversing the order would make it easier to correlate the summary line with the outcome line when grepping logs. |
monitor.py |
health_snapshot() |
copy.deepcopy(_HEALTH_SNAPSHOT_DEFAULTS) is called on every request when _health_snapshot is None. Since defaults are constant, a shallow dict(_HEALTH_SNAPSHOT_DEFAULTS) is sufficient (no nested mutable values that need deep-copying). |
models.py |
CycleResult.hits docstring |
"otherwise empty" — consider clarifying it also returns empty for EMPTY status, not just FAILED. |
Verdict
The approach is sound. The staleness-clock fix (not advancing _last_successful_poll on FAILED) and the lock-protected snapshot are the right patterns. The main actionable items are:
- Unreachable
return Nonein_probe_one(trivial). - Missing wildcard case in
_probe_hits_from_cycle(low-risk but important for forward safety). - Python version check for
match/case(must verify). - Redundant
_record_probe_cycle_completion()calls in seed paths (cleanup). CycleResult.__post_init__validation (defensive programming).
Summary
run_cycle()returnsCycleResult(success/empty/failed) instead of a list./healthreads scheduler state from a locked, immutable snapshot (last_updated,last_cycle_status, etc.).Why
Operators could not tell a real empty probe from a failed one, and the health thread read scheduler fields without synchronization.
Main changes
models.py—CycleStatus,CycleResultsources.py— discriminatedrun_cycle(), new log linesmonitor.py— handle all cycle outcomes; do not advance staleness onFAILED;SchedulerSnapshot+health_snapshot()__main__.py— health extras fromscheduler.health_snapshot()Tests
138 passed (includes empty/failed cycle, FAILED staleness, concurrent health snapshot).
Related issues
close #46
close #49
Summary by CodeRabbit
New Features
/healthnow exposes last-updated timestamp, poll count, probe stats, probe success rate, and last cycle status/error; scheduler snapshot is published atomically and merged with queue/db metricsDocumentation
Tests