Speed up single entity/response service calls (#96729)

* Significantly speed up single entity/response service calls

Since the majority of service calls are single entity, we can
avoid creating tasks in this case. Since the multi-entity
service calls always check the result and raise, we can switch
the asyncio.wait to asyncio.gather

* Significantly speed up single entity/response service calls

Since the majority of service calls are single entity, we can
avoid creating tasks in this case. Since the multi-entity
service calls always check the result and raise, we can switch
the asyncio.wait to asyncio.gather

* revert

* cannot be inside pytest.raises

* one more

* Update homeassistant/helpers/service.py
This commit is contained in:
J. Nick Koston 2023-07-16 21:33:13 -10:00 committed by GitHub
parent c76fac0633
commit 3a06659120
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 50 additions and 18 deletions

View file

@ -741,6 +741,8 @@ async def entity_service_call( # noqa: C901
Calls all platforms simultaneously.
"""
entity_perms: None | (Callable[[str, str], bool]) = None
return_response = call.return_response
if call.context.user_id:
user = await hass.auth.async_get_user(call.context.user_id)
if user is None:
@ -851,13 +853,27 @@ async def entity_service_call( # noqa: C901
entities.append(entity)
if not entities:
if call.return_response:
if return_response:
raise HomeAssistantError(
"Service call requested response data but did not match any entities"
)
return None
if call.return_response and len(entities) != 1:
if len(entities) == 1:
# Single entity case avoids creating tasks and allows returning
# ServiceResponse
entity = entities[0]
response_data = await _handle_entity_call(
hass, entity, func, data, call.context
)
if entity.should_poll:
# Context expires if the turn on commands took a long time.
# Set context again so it's there when we update
entity.async_set_context(call.context)
await entity.async_update_ha_state(True)
return response_data if return_response else None
if return_response:
raise HomeAssistantError(
"Service call requested response data but matched more than one entity"
)
@ -874,9 +890,8 @@ async def entity_service_call( # noqa: C901
)
assert not pending
response_data: ServiceResponse | None
for task in done:
response_data = task.result() # pop exception if have
task.result() # pop exception if have
tasks: list[asyncio.Task[None]] = []
@ -895,7 +910,7 @@ async def entity_service_call( # noqa: C901
for future in done:
future.result() # pop exception if have
return response_data if call.return_response else None
return None
async def _handle_entity_call(