From: Marcel van der Veldt Date: Mon, 23 Feb 2026 00:23:04 +0000 (+0100) Subject: Speedup core controller startup X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=f287033cce85a498c546a135383c692f451c5743;p=music-assistant-server.git Speedup core controller startup --- diff --git a/music_assistant/controllers/metadata.py b/music_assistant/controllers/metadata.py index ac6d0065..39295885 100644 --- a/music_assistant/controllers/metadata.py +++ b/music_assistant/controllers/metadata.py @@ -174,6 +174,10 @@ class MetaDataController(CoreController): async def setup(self, config: CoreConfig) -> None: """Async initialize of module.""" + # wait for dependencies to be ready (streams and music) + await self.mass.streams.initialized.wait() + await self.mass.music.initialized.wait() + self.config = config if not self.logger.isEnabledFor(VERBOSE_LOG_LEVEL): # silence PIL logger diff --git a/music_assistant/mass.py b/music_assistant/mass.py index b4c98462..00ccf9a8 100644 --- a/music_assistant/mass.py +++ b/music_assistant/mass.py @@ -195,28 +195,35 @@ class MusicAssistant: for controller_name in CONFIGURABLE_CORE_CONTROLLERS: controller: CoreController = getattr(self, controller_name) self._provider_manifests[controller.domain] = controller.manifest - # load webserver/api first so the api/frontend is available as soon as possible, - # other controllers are not yet available while we're starting (or performing migrations) + + # setup all core controllers in parallel + async def setup_controller(controller: CoreController) -> None: + await controller.setup(await self.config.get_core_config(controller.domain)) + controller.initialized.set() + + async with asyncio.TaskGroup() as tg: + tg.create_task(setup_controller(self.cache)) + tg.create_task(setup_controller(self.streams)) + tg.create_task(setup_controller(self.music)) + tg.create_task(setup_controller(self.metadata)) + tg.create_task(setup_controller(self.players)) + tg.create_task(setup_controller(self.player_queues)) + + # load webserver/api now that the core controllers are setup and ready to be used self._register_api_commands() await self.webserver.setup(await self.config.get_core_config("webserver")) - await self.cache.setup(await self.config.get_core_config("cache")) - await self.streams.setup(await self.config.get_core_config("streams")) - await self.music.setup(await self.config.get_core_config("music")) - await self.metadata.setup(await self.config.get_core_config("metadata")) - await self.players.setup(await self.config.get_core_config("players")) - await self.player_queues.setup(await self.config.get_core_config("player_queues")) # load builtin providers (always needed, also in safe mode) await self._load_builtin_providers() # setup discovery await self._setup_discovery() - # at this point we are fully up and running, - # set state to running to signal we're ready - self._state = CoreState.RUNNING # load regular providers (skip when in safe mode) # providers are loaded in background tasks so they won't block # the startup if they fail or take a long time to load if not self.safe_mode: await self._load_providers() + # at this point we are fully up and running, + # set state to running to signal we're ready + self._state = CoreState.RUNNING async def stop(self) -> None: """Stop running the music assistant server.""" @@ -840,9 +847,9 @@ class MusicAssistant: ] # load builtin providers and wait for them to complete - await asyncio.gather( - *[self.load_provider(conf.instance_id, allow_retry=True) for conf in builtin_configs] - ) + async with asyncio.TaskGroup() as tg: + for conf in builtin_configs: + tg.create_task(self.load_provider(conf.instance_id, allow_retry=True)) async def _load_providers(self) -> None: """ diff --git a/music_assistant/models/core_controller.py b/music_assistant/models/core_controller.py index e8b937e0..cd0f2ebd 100644 --- a/music_assistant/models/core_controller.py +++ b/music_assistant/models/core_controller.py @@ -2,6 +2,7 @@ from __future__ import annotations +import asyncio import logging from typing import TYPE_CHECKING @@ -23,8 +24,9 @@ class CoreController: manifest: ProviderManifest # some info for the UI only def __init__(self, mass: MusicAssistant) -> None: - """Initialize MusicProvider.""" + """Initialize core controller.""" self.mass = mass + self.initialized = asyncio.Event() self._set_logger() self.manifest = ProviderManifest( type=ProviderType.CORE,