🐛 Report throttled update loops as unhandled exceptions

The throttled update loop report used the handled-exception event, which
only lands in the audit log: error reports and alerts read
unhandled-exception and exception-page. A throttled loop is an
application defect (its cause has :type :internal), and the point of the
report is to find those loops, so it is now an unhandled exception.

AI-assisted-by: claude-opus-5-5
This commit is contained in:
Eva Marco 2026-10-27 14:25:13 +01:00
parent eeb89aecab
commit 4816293bff
2 changed files with 12 additions and 8 deletions

View File

@ -438,10 +438,10 @@
(volatile! 0))
(defn on-update-loop
"Handler for the rumext update scheduler trips: logs and reports the
throttled update loop with the events being processed, at most once per
`report-window-ms`. Runs inside store watchers and effect bodies, so it
never throws."
"Handler for the rumext update scheduler trips: logs the throttled
update loop and reports it as an unhandled exception with the events
being processed, at most once per `report-window-ms`. Runs inside store
watchers and effect bodies, so it never throws."
[{:keys [rounds]}]
(try
(let [now (inst-ms (ct/now))]
@ -456,9 +456,11 @@
(into-array (map ptk/repr-event events)))
(when (seq events)
(.set in-flight-by-cause cause (in-flight-lines events)))
;; Report outside the loop that tripped the scheduler
;; Report outside the loop that tripped the scheduler, as an
;; unhandled exception: a throttled loop is an application defect,
;; so it must reach the error reports and alerts, not only audit.
(ts/schedule
#(submit-report :event-name "handled-exception"
#(submit-report :event-name "unhandled-exception"
:hint (ex-message cause)
:report (fn [] (generate-report cause))
:cause cause)))))

View File

@ -15,7 +15,7 @@
store was processing when it caught the exception; a failing stream
pipeline lists the emitters of the value, not its own event
- on-update-loop – never throws into the scheduler's caller;
reports the throttled loop with the events in flight
reports the throttled loop as unhandled, with the events in flight
- flash schedules async report and toast – neither the report nor
ntf/show is emitted synchronously
- organization SSO recovery – expired SSO sessions go back to the provider
@ -572,7 +572,9 @@
(errors/on-update-loop {:rounds 26})
(t/is (= 1 (count @events)))
(let [props (deref (first @events))]
(t/is (= "handled-exception" (::ev/name props)))
;; An unhandled report: it reaches the error reports and alerts,
;; not only the audit log.
(t/is (= "unhandled-exception" (::ev/name props)))
(t/is (= "update loop throttled" (:hint props)))
(t/is (str/includes? (in-flight-section (:report props))
"test-update-loop-source")))))))