core icon indicating copy to clipboard operation
core copied to clipboard

Event loop blocking call in Azure Storage

Open MartinHjelmare opened this issue 7 months ago • 8 comments

The problem

Home Assistant detected a blocking call inside the event loop when instantiating ContainerClient, in async_setup_entry and in the config flow.

What version of Home Assistant Core has the issue?

core-2025.4.2

What was the last working version of Home Assistant Core?

No response

What type of installation are you running?

Home Assistant Container

Integration causing the issue

Azure Storage

Link to integration documentation on our website

https://www.home-assistant.io/integrations/azure_storage/

Diagnostics information

No response

Example YAML snippet


Anything in the logs that might be useful for us?

2025-04-13 21:51:30.642 WARNING (MainThread) [homeassistant.util.loop] Detected blocking call to open with args ('/usr/local/bin/python3.13', 'rb') inside the event loop by integration 'azure_storage' at homeassistant/components/azure_storage/__init__.py, line 42: container_client = ContainerClient( (offender: /usr/local/lib/python3.13/platform.py, line 204: with open(executable, 'rb') as f:), please create a bug report at https://github.com/home-assistant/core/issues?q=is%3Aopen+is%3Aissue+label%3A%22integration%3A+azure_storage%22
For developers, please see https://developers.home-assistant.io/docs/asyncio_blocking_operations/#open
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 227, in <module>
    sys.exit(main())
  File "/usr/src/homeassistant/homeassistant/__main__.py", line 213, in main
    exit_code = runner.run(runtime_conf)
  File "/usr/src/homeassistant/homeassistant/runner.py", line 154, in run
    return loop.run_until_complete(setup_and_run_hass(runtime_config))
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 712, in run_until_complete
    self.run_forever()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 683, in run_forever
    self._run_once()
  File "/usr/local/lib/python3.13/asyncio/base_events.py", line 2040, in _run_once
    handle._run()
  File "/usr/local/lib/python3.13/asyncio/events.py", line 89, in _run
    self._context.run(self._callback, *self._args)
  File "/usr/src/homeassistant/homeassistant/setup.py", line 171, in async_setup_component
    result = await _async_setup_component(hass, domain, config)
  File "/usr/src/homeassistant/homeassistant/setup.py", line 467, in _async_setup_component
    await asyncio.gather(
  File "/usr/src/homeassistant/homeassistant/setup.py", line 469, in <genexpr>
    create_eager_task(
  File "/usr/src/homeassistant/homeassistant/util/async_.py", line 45, in create_eager_task
    return Task(coro, loop=loop, name=name, eager_start=True)
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 896, in async_setup_locked
    await self.async_setup(hass, integration=integration)
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 662, in async_setup
    await self.__async_setup_with_context(hass, integration)
  File "/usr/src/homeassistant/homeassistant/config_entries.py", line 751, in __async_setup_with_context
    result = await component.async_setup_entry(hass, self)
  File "/usr/src/homeassistant/homeassistant/components/azure_storage/__init__.py", line 42, in async_setup_entry
    container_client = ContainerClient(

Additional information

No response

MartinHjelmare avatar Apr 13 '25 19:04 MartinHjelmare

Hey there @zweckj, mind taking a look at this issue as it has been labeled with an integration (azure_storage) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of azure_storage can trigger bot actions by commenting:

  • @home-assistant close Closes the issue.
  • @home-assistant rename Awesome new title Renames the issue.
  • @home-assistant reopen Reopen the issue.
  • @home-assistant unassign azure_storage Removes the current integration label and assignees on the issue, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component) to the issue.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component) on the issue.

(message by CodeOwnersMention)


azure_storage documentation azure_storage source (message by IssueLinks)

home-assistant[bot] avatar Apr 13 '25 19:04 home-assistant[bot]

I didn't find the actual source of the problem in the client library when browsing the code. It could be a late import, but I'm not sure.

MartinHjelmare avatar Apr 13 '25 20:04 MartinHjelmare

I don't get that one, neither on my dev installation, nor my prod...

zweckj avatar Apr 14 '25 18:04 zweckj

I'm getting the same warning, even after disabling the integration.

I'm definitely not a python expert (if I was, I'd try a PR here) but, taking the message at face value, it seems to be saying that you can't create a ContainerClient in the event loop, because of a call to open() somewhere down the chain (specifically platform.py), which will be out of your control. You have to create it in the executor.

I think you need to move everything from line 42 to 72 here into a method in AzureStorageBackupAgent and call it every time you need a client (having stored entry.data[CONF_ACCOUNT_NAME], entry.data[CONF_CONTAINER_NAME], entry.data[CONF_STORAGE_ACCOUNT_KEY]) in instance variables in the constructor.

This will have the added advantage of avoiding situations like someone deleting the container between startup and backup trigger.

If it's anything like the .NET client, it won't be notably slower. The create is the only thing that takes a moment and you'll only do that once either way.

paulriley avatar May 12 '25 14:05 paulriley

No, only moving it will not help, because ultimately you'll still call it from inside the event loop. We need to find what's causing it, but it only happens together with other integrations. I was trying to analyze this with blockbuster, but I cannot reproduce this on my dev

zweckj avatar May 12 '25 15:05 zweckj

Hmmm. I had assumed the backup controllers would be operating on an async context instead. If that's not true, can you hand client creation off to the executor with hass.async_add_executor_job(), and only create the listener when it's done?

paulriley avatar May 12 '25 16:05 paulriley

Everything should be running inside the event loop at the moment, so there should be no difference when moving. An executor job could work, but it feels dirty to put a constructor in one.

zweckj avatar May 12 '25 16:05 zweckj

After a quick search, I'm translating that to dotnet-speak as meaning "it's a bit dirty to mark something as bound-by-CPU when it's actually bound-by-comms but blocking". Which is a good point, though I can't say I've never done that where I couldn't have blocking code block a main thread (before almost all .NET packages removed their blocking code).

Does raise a thought in my head though: Is the reason you're not seeing this because you have another integration that's pulling in a more recent package (probably azure)? I don't know if that's even possible. But I can't think what a ContainerClient would be doing in the constructor that would require open().

I don't have that many integrations installed (I'm pretty new to HA). Certainly no others that would use Azure.

Anything I can do to help debug, do shout.

paulriley avatar May 12 '25 19:05 paulriley

I now know where it's coming from (https://github.com/python/cpython/blob/8cf4947b0f2d37f7ffeca136ac4f99cb4cb70e5c/Lib/platform.py#L206), but I don't think I can easily get rid of that, so I'll wrap it in an executor call.

zweckj avatar May 13 '25 08:05 zweckj