Skip to content

Commit f78c3c0

Browse files
committed
monitors loader: load only updated monitors
1 parent 77deaec commit f78c3c0

File tree

2 files changed

+37
-24
lines changed

2 files changed

+37
-24
lines changed

src/components/monitors_loader/monitors_loader.py

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -238,43 +238,33 @@ async def _get_monitors_to_load(
238238
return monitors, code_modules
239239

240240

241-
async def _load_monitors() -> None:
241+
async def _load_monitors(last_load_time: datetime | None) -> None:
242242
"""Load all enabled monitors from the database and add them to the registry. If any of the
243243
monitor's modules fails to load, the monitor will not be added to the registry"""
244244
registry.monitors_ready.clear()
245245

246-
loaded_monitors = await Monitor.get_all(Monitor.enabled.is_(True))
247-
monitors_ids = [monitor.id for monitor in loaded_monitors]
248-
249-
code_modules = await CodeModule.get_all(CodeModule.monitor_id.in_(monitors_ids))
250-
code_modules_map = {code_module.monitor_id: code_module for code_module in code_modules}
251-
252-
_logger.info(f"Monitors found: {len(loaded_monitors)}")
246+
monitors, code_modules = await _get_monitors_to_load(last_load_time)
247+
_logger.info(f"Monitors to load: {len(code_modules)}")
253248

254249
# To load the monitors safely, first create all the files and then import them
255250
# Loading right after writing the files can result in an error where the Monitor module is not
256251
# found
257252
monitors_paths = {}
258-
for monitor in loaded_monitors:
253+
for code_module in code_modules:
259254
with catch_exceptions(_logger):
260-
code_module = code_modules_map.get(monitor.id)
261-
if code_module is None:
262-
await monitor.set_enabled(False)
263-
_logger.warning(f"Monitor '{monitor.name}' has no code module, it will be disabled")
264-
continue
255+
monitor = monitors[code_module.monitor_id]
265256

266257
monitors_paths[monitor.id] = module_loader.create_module_files(
267258
module_name=monitor.name,
268259
module_code=code_module.code,
269260
additional_files=code_module.additional_files,
270261
)
271262

272-
for monitor in loaded_monitors:
263+
for code_module in code_modules:
273264
with catch_exceptions(_logger):
274-
monitor_path = monitors_paths.get(monitor.id)
275-
if monitor_path is None:
276-
continue
265+
monitor = monitors[code_module.monitor_id]
277266

267+
monitor_path = monitors_paths[monitor.id]
278268
monitor_module = cast(MonitorModule, module_loader.load_module_from_file(monitor_path))
279269
_configure_monitor(monitor_module)
280270

@@ -286,11 +276,12 @@ async def _load_monitors() -> None:
286276

287277
async def _run() -> None:
288278
"""Monitors loading loop, loading them recurrently. Stops automatically when the app stops"""
289-
last_load_time: datetime
279+
last_load_time: datetime | None = None
290280

291281
while app.running():
292282
with catch_exceptions(_logger):
293-
await _load_monitors()
283+
await _disable_monitors_without_code_modules()
284+
await _load_monitors(last_load_time)
294285
last_load_time = now()
295286

296287
# The sleep task will start seconds earlier to try to load all monitors before the

tests/components/monitors_loader/test_monitors_loader.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -664,13 +664,35 @@ async def test_load_monitors(clear_database):
664664
"(9999457, 'def get_value(): return 12');"
665665
)
666666

667-
await monitors_loader._load_monitors()
667+
await monitors_loader._load_monitors(None)
668668

669669
assert len(registry._monitors) == 2
670670
assert isinstance(registry._monitors[9999123]["module"], ModuleType)
671671
assert isinstance(registry._monitors[9999456]["module"], ModuleType)
672672

673673

674+
async def test_load_monitors_only_updated(clear_database):
675+
"""'_load_monitors' should load all enabled monitors that were updated after the provided
676+
timestamp"""
677+
await databases.execute_application(
678+
'insert into "Monitors"(id, name, enabled) values'
679+
"(9999123, 'monitor_1', true),"
680+
"(9999456, 'internal.monitor_2', true),"
681+
"(9999457, 'disabled_monitor', false);"
682+
)
683+
await databases.execute_application(
684+
'insert into "CodeModules"(monitor_id, code, registered_at) values'
685+
"(9999123, 'def get_value(): return 10', '2025-01-10 00:00'),"
686+
"(9999456, 'def get_value(): return 11', '2025-01-20 00:00'),"
687+
"(9999457, 'def get_value(): return 12', '2025-01-30 00:00');"
688+
)
689+
690+
await monitors_loader._load_monitors(datetime(2025, 1, 15, tzinfo=timezone.utc))
691+
692+
assert len(registry._monitors) == 1
693+
assert isinstance(registry._monitors[9999456]["module"], ModuleType)
694+
695+
674696
async def test_load_monitors_monitors_ready_flag(monkeypatch, clear_database):
675697
"""'_load_monitors' should clear and set the registry's 'monitors_ready' while loading the
676698
monitors"""
@@ -695,7 +717,7 @@ async def slow_get_all(self, *args, **kwargs):
695717
assert registry.monitors_ready.is_set()
696718
assert registry.monitors_pending.is_set()
697719

698-
load_monitors_task = asyncio.create_task(monitors_loader._load_monitors())
720+
load_monitors_task = asyncio.create_task(monitors_loader._load_monitors(None))
699721

700722
await asyncio.sleep(0.1)
701723
assert not registry.monitors_ready.is_set()
@@ -720,7 +742,7 @@ async def slow_get_all(self, *args, **kwargs):
720742
"insert into \"Monitors\"(id, name, enabled) values (9999123, 'monitor_1', true);"
721743
)
722744

723-
await monitors_loader._load_monitors()
745+
await monitors_loader._load_monitors(None)
724746

725747
monitor = await Monitor.get_by_id(9999123)
726748
assert monitor is not None
@@ -745,7 +767,7 @@ async def test_load_monitors_error(caplog, clear_database):
745767
"(9999456, 'def get_value(): return 10');"
746768
)
747769

748-
await monitors_loader._load_monitors()
770+
await monitors_loader._load_monitors(None)
749771

750772
assert len(registry._monitors) == 1
751773
assert isinstance(registry._monitors[9999456]["module"], ModuleType)

0 commit comments

Comments
 (0)