From d8655e3b3d856af32a0102782aa55db01df845bc Mon Sep 17 00:00:00 2001 From: Elzbieta Kotulska Date: Thu, 28 Nov 2024 12:44:09 +0100 Subject: [PATCH] Fix ConfigManagingActor to handle missing config files gracefully Eliminate recursive actor crashes when config files are missing. The previous implementation suffered from a critical error pattern: - The `_read_config` method is called at the start of the `_run` method and on FileWatcher event. - When no config files existed, an exception would be raised - This exception caused the actor to crash and immediately restart - Restarting triggered the same `_read_config` method - The cycle repeated, creating a persistent crash loop This fix introduces a more robust approach: - Detect missing config files without throwing exceptions - Set up a FileWatcher to monitor for future config file creation - call `_read_config` method as soon as any config file is crated. Signed-off-by: Elzbieta Kotulska --- src/frequenz/sdk/config/_config_managing.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/frequenz/sdk/config/_config_managing.py b/src/frequenz/sdk/config/_config_managing.py index e66e2e461..b54d07890 100644 --- a/src/frequenz/sdk/config/_config_managing.py +++ b/src/frequenz/sdk/config/_config_managing.py @@ -107,14 +107,11 @@ def __init__( self._force_polling: bool = force_polling self._polling_interval: timedelta = polling_interval - def _read_config(self) -> abc.Mapping[str, Any]: + def _read_config(self) -> abc.Mapping[str, Any] | None: """Read the contents of the configuration file. Returns: A dictionary containing configuration variables. - - Raises: - ValueError: If config file cannot be read. """ error_count = 0 config: dict[str, Any] = {} @@ -138,14 +135,18 @@ def _read_config(self) -> abc.Mapping[str, Any]: error_count += 1 if error_count == len(self._config_paths): - raise ValueError(f"{self}: Can't read any of the config files") + _logger.error( + "%s: Can't read any of the config files, ignoring config update.", self + ) + return None return config async def send_config(self) -> None: """Send the configuration to the output sender.""" config = self._read_config() - await self._output.send(config) + if config is not None: + await self._output.send(config) async def _run(self) -> None: """Monitor for and send configuration file updates.