Add and improve comments about staggering of event listeners (#99058)

This commit is contained in:
Erik Montnemery 2023-08-25 19:47:13 +02:00 committed by GitHub
parent 8768c39021
commit 3a71e21d6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 12 additions and 6 deletions

View file

@ -68,6 +68,10 @@ _ENTITIES_LISTENER = "entities"
_LOGGER = logging.getLogger(__name__)
# Used to spread async_track_utc_time_change listeners and DataUpdateCoordinator
# refresh cycles between RANDOM_MICROSECOND_MIN..RANDOM_MICROSECOND_MAX.
# The values have been determined experimentally in production testing, background
# in PR https://github.com/home-assistant/core/pull/82233
RANDOM_MICROSECOND_MIN = 50000
RANDOM_MICROSECOND_MAX = 500000
@ -1640,7 +1644,7 @@ def async_track_utc_time_change(
matching_seconds = dt_util.parse_time_expression(second, 0, 59)
matching_minutes = dt_util.parse_time_expression(minute, 0, 59)
matching_hours = dt_util.parse_time_expression(hour, 0, 23)
# Avoid aligning all time trackers to the same second
# Avoid aligning all time trackers to the same fraction of a second
# since it can create a thundering herd problem
# https://github.com/home-assistant/core/issues/82231
microsecond = randint(RANDOM_MICROSECOND_MIN, RANDOM_MICROSECOND_MAX)

View file

@ -59,6 +59,7 @@ from homeassistant.helpers import (
entity,
entity_platform,
entity_registry as er,
event,
intent,
issue_registry as ir,
recorder as recorder_helper,
@ -397,9 +398,10 @@ def async_fire_time_changed(
) -> None:
"""Fire a time changed event.
This function will add up to 0.5 seconds to the time to ensure that
it accounts for the accidental synchronization avoidance code in repeating
listeners.
If called within the first 500 ms of a second, time will be bumped to exactly
500 ms to match the async_track_utc_time_change event listeners and
DataUpdateCoordinator which spreads all updates between 0.05..0.50.
Background in PR https://github.com/home-assistant/core/pull/82233
As asyncio is cooperative, we can't guarantee that the event loop will
run an event at the exact time we want. If you need to fire time changed
@ -410,12 +412,12 @@ def async_fire_time_changed(
else:
utc_datetime = dt_util.as_utc(datetime_)
if utc_datetime.microsecond < 500000:
if utc_datetime.microsecond < event.RANDOM_MICROSECOND_MAX:
# Allow up to 500000 microseconds to be added to the time
# to handle update_coordinator's and
# async_track_time_interval's
# staggering to avoid thundering herd.
utc_datetime = utc_datetime.replace(microsecond=500000)
utc_datetime = utc_datetime.replace(microsecond=event.RANDOM_MICROSECOND_MAX)
_async_fire_time_changed(hass, utc_datetime, fire_all)