diff --git a/backend/app/sync/runner.py b/backend/app/sync/runner.py index be7cef0b..695977cc 100644 --- a/backend/app/sync/runner.py +++ b/backend/app/sync/runner.py @@ -152,16 +152,22 @@ def run_rss_poll(db: Session, channels: list[Channel] | None = None) -> dict[str progress.report(done, total, "rss_poll") fallback = run_feed_fallback(db, unread) if failed: + # The fallback's count rides on this line because this is the one the stored log keeps (an + # INFO line outside a request never reaches `log_events`): the console should show whether + # the uploads came in another way. log.warning( - "RSS poll: %d/%d channels could not be polled", + "RSS poll: %d/%d channels could not be polled; %d read through the API instead", failed, total, + fallback["read"], extra=logs.event( "sync.rss.failed", failed=failed, total=total, reasons=dict(reasons), examples=examples, + fallback_read=fallback["read"], + fallback_new=fallback["new"], ), ) if total and failed == total: diff --git a/backend/tests/test_sync_guards.py b/backend/tests/test_sync_guards.py index a43dbafd..824f62d9 100644 --- a/backend/tests/test_sync_guards.py +++ b/backend/tests/test_sync_guards.py @@ -235,7 +235,9 @@ class TestRssPollResult: with pytest.raises(RuntimeError, match="read-only transaction"): self._run(monkeypatch, fetch=lambda cid: [{"id": "v"}], apply_=boom) - def test_only_the_unreadable_feeds_go_to_the_api_and_what_it_reads_counts(self, monkeypatch): + def test_only_the_unreadable_feeds_go_to_the_api_and_what_it_reads_counts( + self, monkeypatch, caplog + ): # A feed that answered 404 is the fallback's; a channel whose rows failed to STORE is not — # the API would hand the same rows to the same broken write. seen = [] @@ -262,6 +264,9 @@ class TestRssPollResult: ) assert seen == ["UC_dark"] assert out == {"new": 4, "failed": 2, "fallback": 1} + # ...and the one line the stored log keeps says so (an INFO line outside a request is not). + (warning,) = [r for r in caplog.records if getattr(r, "event", None) == "sync.rss.failed"] + assert warning.attrs["fallback_read"] == 1 and warning.attrs["fallback_new"] == 3 def test_no_channels_at_all_is_not_an_outage(self, monkeypatch): # A fresh instance with nothing subscribed must not report an error every poll.