Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unloading an entry can leave states around #17786

Merged
merged 5 commits into from
Oct 25, 2018
Merged

Conversation

balloob
Copy link
Member

@balloob balloob commented Oct 25, 2018

Description:

Fix for #17370.

The issue is caused by the following (analysis by @amelchio):

  • Load a config entry that forwards to a platform that adds at least 1 entity that has a unique ID and so is entered into the entity registry
  • Remove config entry
  • Component unload will forward unload to platform
  • Entity Platform correctly unloads the entities from the state machine
  • When unload succeeds, config entry is removed
  • Removal of config entry triggers a cleanup on the entity registry to reset the config entry ID of any registered entity with that config entry ID (because the ID will no longer exist). Because this update mutates the entity registry, the attached entity is notified by calling Entity.async_registry_updated.
  • Entity.async_registry_updated will cause async_update_ha_state to be called, writing the state of a removed entity to the state machine.

Related issue (if applicable): fixes #17370

Pull request in home-assistant.io with documentation (if applicable): home-assistant/home-assistant.io#<home-assistant.io PR number goes here>

Example entry for configuration.yaml (if applicable):

Checklist:

  • The code change is tested and works locally.
  • Local tests pass with tox. Your PR cannot be merged unless tests pass

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • New dependencies have been added to the REQUIREMENTS variable (example).
  • New dependencies are only imported inside functions that use them (example).
  • New or updated dependencies have been added to requirements_all.txt by running script/gen_requirements_all.py.
  • New files were added to .coveragerc.

If the code does not interact with devices:

  • Tests have been added to verify that the new code works.

@ghost ghost assigned balloob Oct 25, 2018
@ghost ghost added the in progress label Oct 25, 2018
@balloob balloob requested review from bachya, Kane610 and a team as code owners October 25, 2018 10:14
@balloob balloob changed the title WIP: Fix unloading an entry can leave states around Fix unloading an entry can leave states around Oct 25, 2018
@balloob
Copy link
Member Author

balloob commented Oct 25, 2018

CC @amelchio @bachya - could you guys try to repro the bug with the fix from this PR applied ?

I was unable to repro the bug with the extended test case (see diff), but I do have a solution. The problem was that we were not calling the on remove listeners for an entity when it was removed via a platform, because there was some duplicate code. I have fixed that by making the Entity.async_remove method unaware of entity platform and have entity platform get notified of removal via async_on_remove listener.

@balloob
Copy link
Member Author

balloob commented Oct 25, 2018

The cleanup will make sure that the entity will no longer be registered for updates on the entity reigstry when the config entry ID is cleared inside the entity registry.

Still bugs me that I was unable to repro it with a test.

Copy link
Member

@MartinHjelmare MartinHjelmare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a better suggestion at the moment.

@@ -96,6 +96,8 @@ def update():

async def async_will_remove_from_hass(self) -> None:
"""Disconnect dispatcher listener when removed."""
await super.async_will_remove_from_hass()
Copy link
Member

@MartinHjelmare MartinHjelmare Oct 25, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this change. It puts a burden on each platform to make sure the core operates correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is super.bla correct syntax? I thought it should be super().bla.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. will fix.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about c4a52dd

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as one opinion: calling super() on methods that subclasses override is just part of Python that needs to be implemented correctly. Just like we have to trust that async platforms aren't doing IO on the main thread, it's OK to trust that subclasses need to be implemented correctly, or they won't work properly.

@balloob balloob force-pushed the entry-unload-stale-state branch from 689da70 to c4a52dd Compare October 25, 2018 10:35
Copy link
Contributor

@bachya bachya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@balloob I tested both of my config entry-enabled components (OpenUV and SimpliSafe) against this PR: in both cases, the entities were correctly removed when the config entry was deleted.

From my vantage point, this PR is good to go. 👍

@amelchio
Copy link
Contributor

This works with my work-in-progress LIFX unloading as well.

The entities are not removed from group.all_lights but that is probably a separate issue.

@ghost ghost assigned amelchio Oct 25, 2018
@amelchio
Copy link
Contributor

Your test never reaches Entity.async_registry_updated because the listener weakref is broken during unloading. I added a commit that makes the test fail without your fix.

@balloob balloob added this to the 0.81 milestone Oct 25, 2018
@balloob balloob merged commit 77bf10e into dev Oct 25, 2018
@ghost ghost removed the in progress label Oct 25, 2018
@balloob
Copy link
Member Author

balloob commented Oct 25, 2018

Awesome detective work guys 👍 Good catch on the test too @amelchio

❤️

@balloob balloob deleted the entry-unload-stale-state branch October 25, 2018 17:58
balloob added a commit that referenced this pull request Oct 26, 2018
* Add test that tests unloading on remove

* Add more test things

* Untangle entity remove code from entity platform

* Don't add default implementation of async_will_remove

* Keep entity weakref alive
@balloob balloob mentioned this pull request Oct 26, 2018
@home-assistant home-assistant locked and limited conversation to collaborators Feb 5, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Entities remain after deletion of config entry
6 participants