Skip to content

Debounce timeline calendar refresh so it stops pegging the event loop#674

Open
moshemalawach wants to merge 1 commit into
valentinfrlch:v1.8.0-betafrom
moshemalawach:fix/timeline-calendar-event-loop
Open

Debounce timeline calendar refresh so it stops pegging the event loop#674
moshemalawach wants to merge 1 commit into
valentinfrlch:v1.8.0-betafrom
moshemalawach:fix/timeline-calendar-event-loop

Conversation

@moshemalawach

Copy link
Copy Markdown

Fixes #673.

The timeline calendar reloads the whole events DB every time SIGNAL_TIMELINE_UPDATED fires, and that signal fires once per analysed camera event. On a busy setup this keeps the event loop busy enough that HA logs "Update of calendar.llm_vision_timeline is taking over 10 seconds" over and over, other integrations start timing out (the Frigate API in my case, even though Frigate itself answers fine), and one CPU core sits at 100%. It happens even with a small DB (about 1800 rows here), so it's the refresh frequency and the SQLite contention, not the amount of data.

I confirmed it by sampling per-thread CPU from the host: the main thread (the event loop) is the busy one, and disabling the calendar entity drops it straight back to idle.

What this PR changes:

  1. calendar.py: the timeline-updated signal now goes through a Debouncer with a 1.5s cooldown, so a burst of events triggers a single reload instead of one full DB reload per event. I also added async_will_remove_from_hass to cancel a pending refresh when the entity is removed.

  2. timeline.py: create_event() called await self.load_events() and then threw the result away (nothing reads self.events before the insert, and _insert_event() reloads right after), so I removed that extra full reload.

The calendar still reloads after writes, the reloads just get coalesced.

Tests:

  • Full suite passes (415 passed, 4 integration tests deselected). That includes 3 new tests in tests/test_calendar.py for the debounce wiring: the signal goes through the debouncer, the debounced refresh does a force refresh, and a pending refresh is cancelled on removal.
  • The existing create_event tests in tests/test_timeline.py still pass unchanged.
  • Ran the patched integration on a live HA instance (2026.6.3, llmvision 1.7.0) and the slow-update warnings and the Frigate timeouts went away.

A couple of things I noticed but left out to keep this focused:

  • _purge_expired_events() runs a DB query on every load_events()/get_events_json() call. Since retention is measured in days, it could be throttled.
  • async_get_events() rebuilds every CalendarEvent on each call. A range-filtered query would help for large timelines.

The LLM Vision Timeline calendar reloaded the entire events DB on every
SIGNAL_TIMELINE_UPDATED (one per analysed camera event), pinning the asyncio
event loop ('Update of calendar.llm_vision_timeline is taking over 10 seconds')
and starving other integrations.

- calendar.py: route timeline-updated signals through a Debouncer so a burst of
  events triggers a single reload; cancel a pending refresh on entity removal.
- timeline.py: drop the redundant load_events() in create_event() whose result
  was discarded before _insert_event() reloads.

Adds debounce-wiring tests. Fixes valentinfrlch#673.
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.

2 participants