Overcome group concurrent setup limitation (#39483)

With a lot of groups the limitation that groups
had to be setup one at a time could account for
the bulk of startup time.
This commit is contained in:
J. Nick Koston 2020-09-02 03:14:16 -05:00 committed by GitHub
parent 557684c3ce
commit 3e9963a216
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 12 deletions

View File

@ -334,18 +334,39 @@ async def async_setup(hass, config):
async def _async_process_config(hass, config, component):
"""Process group configuration."""
hass.data.setdefault(GROUP_ORDER, 0)
tasks = []
for object_id, conf in config.get(DOMAIN, {}).items():
name = conf.get(CONF_NAME, object_id)
entity_ids = conf.get(CONF_ENTITIES) or []
icon = conf.get(CONF_ICON)
mode = conf.get(CONF_ALL)
# Don't create tasks and await them all. The order is important as
# groups get a number based on creation order.
await Group.async_create_group(
hass, name, entity_ids, icon=icon, object_id=object_id, mode=mode
# We keep track of the order when we are creating the tasks
# in the same way that async_create_group does to make
# sure we use the same ordering system. This overcomes
# the problem with concurrently creating the groups
tasks.append(
Group.async_create_group(
hass,
name,
entity_ids,
icon=icon,
object_id=object_id,
mode=mode,
order=hass.data[GROUP_ORDER],
)
)
# Keep track of the group order without iterating
# every state in the state machine every time
# we setup a new group
hass.data[GROUP_ORDER] += 1
await asyncio.gather(*tasks)
class GroupEntity(Entity):
"""Representation of a Group of entities."""
@ -418,11 +439,12 @@ class Group(Entity):
icon=None,
object_id=None,
mode=None,
order=None,
):
"""Initialize a group."""
return asyncio.run_coroutine_threadsafe(
Group.async_create_group(
hass, name, entity_ids, user_defined, icon, object_id, mode
hass, name, entity_ids, user_defined, icon, object_id, mode, order
),
hass.loop,
).result()
@ -436,28 +458,30 @@ class Group(Entity):
icon=None,
object_id=None,
mode=None,
order=None,
):
"""Initialize a group.
This method must be run in the event loop.
"""
hass.data.setdefault(GROUP_ORDER, 0)
if order is None:
hass.data.setdefault(GROUP_ORDER, 0)
order = hass.data[GROUP_ORDER]
# Keep track of the group order without iterating
# every state in the state machine every time
# we setup a new group
hass.data[GROUP_ORDER] += 1
group = Group(
hass,
name,
order=hass.data[GROUP_ORDER],
order=order,
icon=icon,
user_defined=user_defined,
entity_ids=entity_ids,
mode=mode,
)
# Keep track of the group order without iterating
# every state in the state machine every time
# we setup a new group
hass.data[GROUP_ORDER] += 1
group.entity_id = async_generate_entity_id(
ENTITY_ID_FORMAT, object_id or name, hass=hass
)

View File

@ -512,3 +512,54 @@ async def test_group_order(hass):
assert hass.states.get("group.group_zero").attributes["order"] == 0
assert hass.states.get("group.group_one").attributes["order"] == 1
assert hass.states.get("group.group_two").attributes["order"] == 2
async def test_group_order_with_dynamic_creation(hass):
"""Test that order gets incremented when creating a new group."""
hass.states.async_set("light.bowl", STATE_ON)
assert await async_setup_component(
hass,
"group",
{
"group": {
"group_zero": {"entities": "light.Bowl", "icon": "mdi:work"},
"group_one": {"entities": "light.Bowl", "icon": "mdi:work"},
"group_two": {"entities": "light.Bowl", "icon": "mdi:work"},
}
},
)
await hass.async_block_till_done()
assert hass.states.get("group.group_zero").attributes["order"] == 0
assert hass.states.get("group.group_one").attributes["order"] == 1
assert hass.states.get("group.group_two").attributes["order"] == 2
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_SET,
{"object_id": "new_group", "name": "New Group", "entities": "light.bowl"},
)
await hass.async_block_till_done()
assert hass.states.get("group.new_group").attributes["order"] == 3
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_REMOVE,
{
"object_id": "new_group",
},
)
await hass.async_block_till_done()
assert not hass.states.get("group.new_group")
await hass.services.async_call(
group.DOMAIN,
group.SERVICE_SET,
{"object_id": "new_group2", "name": "New Group 2", "entities": "light.bowl"},
)
await hass.async_block_till_done()
assert hass.states.get("group.new_group2").attributes["order"] == 4