diff --git a/homeassistant/helpers/event.py b/homeassistant/helpers/event.py index e615a6422f0..daad994bbd4 100644 --- a/homeassistant/helpers/event.py +++ b/homeassistant/helpers/event.py @@ -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) diff --git a/tests/common.py b/tests/common.py index 0b63a9a2ef6..6ee38b72532 100644 --- a/tests/common.py +++ b/tests/common.py @@ -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)