From 89fa5c9c7a6f1be75312d4a92c4c2ed9a1e55bed Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Wed, 21 May 2025 15:06:46 +0200 Subject: [PATCH] Avoid initializing Blockbuster on Supervisor info call (#5901) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Avoid initializing Blockbuster on Supervisor info call Instead of creating an instance of Blockbuster to simply check if Bluckbuster is enabled, use a global variable to store the instance of Blockbuster and only initialize it when needed. This avoids unnecessary initialization of Blockbuster when it is not required. * Update supervisor/utils/blockbuster.py Co-authored-by: Jan Čermák * Fix merge and rename singleton class to BlockBusterManager * Fix pytest --------- Co-authored-by: Jan Čermák --- supervisor/__main__.py | 4 +-- supervisor/api/supervisor.py | 12 +++------ supervisor/utils/blockbuster.py | 46 +++++++++++++++++---------------- 3 files changed, 30 insertions(+), 32 deletions(-) diff --git a/supervisor/__main__.py b/supervisor/__main__.py index 17dc292e3..6e2417690 100644 --- a/supervisor/__main__.py +++ b/supervisor/__main__.py @@ -13,7 +13,7 @@ zlib_fast.enable() # pylint: disable=wrong-import-position from supervisor import bootstrap # noqa: E402 -from supervisor.utils.blockbuster import activate_blockbuster # noqa: E402 +from supervisor.utils.blockbuster import BlockBusterManager # noqa: E402 from supervisor.utils.logging import activate_log_queue_handler # noqa: E402 # pylint: enable=wrong-import-position @@ -55,7 +55,7 @@ if __name__ == "__main__": coresys = loop.run_until_complete(bootstrap.initialize_coresys()) loop.set_debug(coresys.config.debug) if coresys.config.detect_blocking_io: - activate_blockbuster() + BlockBusterManager.activate() loop.run_until_complete(coresys.core.connect()) loop.run_until_complete(bootstrap.supervisor_debugger(coresys)) diff --git a/supervisor/api/supervisor.py b/supervisor/api/supervisor.py index 294451507..c79f88f42 100644 --- a/supervisor/api/supervisor.py +++ b/supervisor/api/supervisor.py @@ -49,11 +49,7 @@ from ..const import ( from ..coresys import CoreSysAttributes from ..exceptions import APIError from ..store.validate import repositories -from ..utils.blockbuster import ( - activate_blockbuster, - blockbuster_enabled, - deactivate_blockbuster, -) +from ..utils.blockbuster import BlockBusterManager from ..utils.sentry import close_sentry, init_sentry from ..utils.validate import validate_timezone from ..validate import version_tag, wait_boot @@ -110,7 +106,7 @@ class APISupervisor(CoreSysAttributes): ATTR_DEBUG_BLOCK: self.sys_config.debug_block, ATTR_DIAGNOSTICS: self.sys_config.diagnostics, ATTR_AUTO_UPDATE: self.sys_updater.auto_update, - ATTR_DETECT_BLOCKING_IO: blockbuster_enabled(), + ATTR_DETECT_BLOCKING_IO: BlockBusterManager.is_enabled(), ATTR_COUNTRY: self.sys_config.country, # Depricated ATTR_WAIT_BOOT: self.sys_config.wait_boot, @@ -180,10 +176,10 @@ class APISupervisor(CoreSysAttributes): detect_blocking_io = DetectBlockingIO.ON if detect_blocking_io == DetectBlockingIO.ON: - activate_blockbuster() + BlockBusterManager.activate() elif detect_blocking_io == DetectBlockingIO.OFF: self.sys_config.detect_blocking_io = False - deactivate_blockbuster() + BlockBusterManager.deactivate() # Deprecated if ATTR_WAIT_BOOT in body: diff --git a/supervisor/utils/blockbuster.py b/supervisor/utils/blockbuster.py index 2657c3176..655bd32ad 100644 --- a/supervisor/utils/blockbuster.py +++ b/supervisor/utils/blockbuster.py @@ -1,6 +1,5 @@ """Activate and deactivate blockbuster for finding blocking I/O.""" -from functools import cache import logging from blockbuster import BlockBuster @@ -8,28 +7,31 @@ from blockbuster import BlockBuster _LOGGER: logging.Logger = logging.getLogger(__name__) -@cache -def _get_blockbuster() -> BlockBuster: - """Get blockbuster instance.""" - return BlockBuster() +class BlockBusterManager: + """Manage BlockBuster instance.""" + _instance: BlockBuster | None = None -def blockbuster_enabled() -> bool: - """Return true if blockbuster detection is enabled.""" - blockbuster = _get_blockbuster() - # We activate all or none so just check the first one - for _, fn in blockbuster.functions.items(): - return fn.activated - return False + @classmethod + def is_enabled(cls): + """Return true if blockbuster detection is enabled.""" + if cls._instance is None: + return False + for _, fn in cls._instance.functions.items(): + return fn.activated + return False + @classmethod + def activate(cls): + """Activate blockbuster detection.""" + _LOGGER.info("Activating BlockBuster blocking I/O detection") + if cls._instance is None: + cls._instance = BlockBuster() + cls._instance.activate() -def activate_blockbuster() -> None: - """Activate blockbuster detection.""" - _LOGGER.info("Activating BlockBuster blocking I/O detection") - _get_blockbuster().activate() - - -def deactivate_blockbuster() -> None: - """Deactivate blockbuster detection.""" - _LOGGER.info("Deactivating BlockBuster blocking I/O detection") - _get_blockbuster().deactivate() + @classmethod + def deactivate(cls): + """Deactivate blockbuster detection.""" + _LOGGER.info("Deactivating BlockBuster blocking I/O detection") + if cls._instance: + cls._instance.deactivate()