Shield async httpx call in generic#47852
Conversation
|
If cancellation is the problem. Can't we asyncio.shield a subtask from cancellation instead? Seems better than switching to sync. |
|
Good idea, we can try that and see if that fixes the issue instead. |
41d7dc3 to
7b377d3
Compare
|
The user reported that the current version (using asyncio.shield around the async api) works |
| return await asyncio.shield(self._async_camera_image()) | ||
|
|
||
| async def _async_camera_image(self): | ||
| """Return a still image response from the camera.""" |
There was a problem hiding this comment.
I think it was better when you only shielded the httpx call. Anything inside the shield will continue to execute, but the surrounding code is cancelled. That mean that the setting of last image/url will still end up being set sometime later even though the command was cancelled.
So just shield the "buggy" code and let the rest be cancelled as expected.
There was a problem hiding this comment.
That way, we avoid setting last url/image if the httpx stuff ends up completing even though caller wanted it cancelled.
There was a problem hiding this comment.
The reason I was concerned about doing it that way was in the event of a cancellation event, the call would continue inside the shield but the response resources might not get freed up afterwards since the response is never read.
If we don't want to set the last_url/image I can still change that. I do think that does make more sense technically (the caller requested a cancel, so cancel), but practically it might make more sense to just update the last_url/image since we have it anyway.
There was a problem hiding this comment.
Well the cancel can occur on any await. So there is a risk of introducing a later await that changes behavior.
Being as specific as possible what it is we are solving is better. If cancelled we should throw away anything we get..a new request may have been started.
There was a problem hiding this comment.
Ok, the change that was pushed last week does avoid setting the last_url/image. I agree we should only shield one await, but we also need to make sure the response gets closed, and this way satisfies both of those requirements.
There was a problem hiding this comment.
@elupus Does what I said make sense? If we only shield the call then we risk not cleaning up the response.
It looks like another user had the same problem, so it would be good to merge a fix before another release.
| return await asyncio.shield(self._async_camera_image()) | ||
|
|
||
| async def _async_camera_image(self): | ||
| """Return a still image response from the camera.""" |
| return self._last_url, self._last_image | ||
| finally: | ||
| if response: | ||
| response.close() |
There was a problem hiding this comment.
Wait. Isn't close async here for an async httpx instance? Ie it should await response.aclose() also this is generally not needed unless it's a stream=true as mentioned here
https://www.python-httpx.org/async/#opening-and-closing-clients
There was a problem hiding this comment.
You are right, I think it should be async close..I didn't change it when we switched back from the sync API.
Let me check into the close thing. I think when I was looking at the issues over on httpx there were other issues with not closing and freeing up resources...
There was a problem hiding this comment.
I think issue 116/147 and pr 117 over there mean we should try to close it ourselves
There was a problem hiding this comment.
If you don't have any objections, I'm going to go ahead and merge this as is. Hopefully the underlying httpx issue gets taken care of soon and we can roll back the shielding/closing code.
BTW, in #47624 someone noted that there may be similar issues with rest. It is possible that there are similar problems in other components that use httpx.
|
@uvjustin - So far at 16 hours uptime, this has solved all my generic cam issues. But it also seems to have solved the REST call issues for the remaining four sensors I didn't move over to NodeRed; perhaps the correction has left more tasks open for the REST processing (since I pared it down to these four still running native in HA). Thanks for pointing me to it! I know it's been merged already but wanted to give you some more data and thank you. This was driving me crazy for months. |
Proposed change
#46576 changed
genericto use thehttpxlibrary. However, there are a few outstanding issues withhttpx's async api (e.g. https://github.com/encode/httpx/issues/1461) which seem to be causing #47624. This PR usesasyncio.shieldas a workaround for thehttpxissue.Type of change
Example entry for
configuration.yaml:# Example configuration.yamlAdditional information
Checklist
black --fast homeassistant tests)If user exposed functionality or configuration variables are added/changed:
If the code communicates with devices, web services, or third-party tools:
Updated and included derived files by running:
python3 -m script.hassfest.requirements_all.txt.Updated by running
python3 -m script.gen_requirements_all..coveragerc.The integration reached or maintains the following Integration Quality Scale:
To help with the load of incoming pull requests: