Fix mqtt timer churn (#117885)

Borrows the same design from homeassistant.helpers.storage to avoid
rescheduling the timer every time async_schedule is called if a timer
is already running.

Instead of the timer fires too early it gets rescheduled for the time
we wanted it. This avoids 1000s of timer add/cancel during startup
This commit is contained in:
J. Nick Koston
2024-05-21 15:05:33 -10:00
committed by GitHub
parent 1800a60a6d
commit f429bfa903
2 changed files with 39 additions and 9 deletions

View File

@ -328,6 +328,7 @@ class EnsureJobAfterCooldown:
self._callback = callback_job
self._task: asyncio.Task | None = None
self._timer: asyncio.TimerHandle | None = None
self._next_execute_time = 0.0
def set_timeout(self, timeout: float) -> None:
"""Set a new timeout period."""
@ -371,8 +372,28 @@ class EnsureJobAfterCooldown:
"""Ensure we execute after a cooldown period."""
# We want to reschedule the timer in the future
# every time this is called.
self._async_cancel_timer()
self._timer = self._loop.call_later(self._timeout, self.async_execute)
next_when = self._loop.time() + self._timeout
if not self._timer:
self._timer = self._loop.call_at(next_when, self._async_timer_reached)
return
if self._timer.when() < next_when:
# Timer already running, set the next execute time
# if it fires too early, it will get rescheduled
self._next_execute_time = next_when
@callback
def _async_timer_reached(self) -> None:
"""Handle timer fire."""
self._timer = None
if self._loop.time() >= self._next_execute_time:
self.async_execute()
return
# Timer fired too early because there were multiple
# calls async_schedule. Reschedule the timer.
self._timer = self._loop.call_at(
self._next_execute_time, self._async_timer_reached
)
async def async_cleanup(self) -> None:
"""Cleanup any pending task."""