Files
LEDMatrix/test/test_cache_manager_lazy_config.py
T
ChuckandClaude Opus 5.5 7bb85c0356 perf(cache): skip rewriting unchanged CacheManager.set() records (#730)
The disk cache's unchanged-payload skip now ignores a CacheManager.set() record's timestamp, so unchanged re-saves are skipped; a skip moves the file's mtime to the new timestamp instead, and readers take a record's age from the newer of the two (never more than an hour past the embedded timestamp). Per-plugin plugin_metrics:<id> records become one plugin_metrics_snapshot written at most once a minute, and CacheManager builds its ConfigManager on first use. On hdpi, cache file writes went from ~37 to 8.6 a minute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 14:14:08 -04:00

99 lines
2.9 KiB
Python

"""CacheManager builds its ConfigManager on first use, not in __init__.
Every CacheManager built a ConfigManager and loaded the whole config for a
cache strategy that no longer reads it. The attribute stays public -- the
sports plugins resolve the global timezone through
``cache_manager.config_manager`` -- so it is now built on first access.
"""
from unittest.mock import MagicMock, patch
import pytest
import src.config_manager as config_manager_module
from src.cache_manager import CacheManager
@pytest.fixture
def built(monkeypatch):
"""Count ConfigManager constructions and load_config calls."""
made = []
class CountingConfigManager:
def __init__(self):
made.append(self)
self.loads = 0
def load_config(self):
self.loads += 1
return {}
monkeypatch.setattr(config_manager_module, "ConfigManager", CountingConfigManager)
return made
@pytest.fixture
def manager(tmp_path):
with patch('src.cache_manager.CacheManager._get_writable_cache_dir',
return_value=str(tmp_path)):
cm = CacheManager()
cm.stop_cleanup_thread()
return cm
def test_construction_does_not_load_the_config(built, manager):
manager.set("k", {"v": 1})
assert manager.get("k") == {"v": 1}
assert manager.get_cache_strategy("sports_live")["max_age"] > 0
assert built == []
def test_first_access_builds_and_loads_it_once(built, manager):
first = manager.config_manager
assert manager.config_manager is first
assert getattr(manager, "config_manager", None) is first
assert len(built) == 1 and first.loads == 1
def test_assignment_still_wins(built, manager):
replacement = MagicMock()
manager.config_manager = replacement
assert manager.config_manager is replacement
assert built == []
def test_an_unimportable_config_manager_is_none(manager, monkeypatch):
import builtins
real_import = builtins.__import__
def refuse(name, *args, **kwargs):
if name == "src.config_manager":
raise ImportError("no config manager here")
return real_import(name, *args, **kwargs)
monkeypatch.setattr(builtins, "__import__", refuse)
assert manager.config_manager is None
def test_a_failed_load_is_retried_on_the_next_access(manager, monkeypatch):
attempts = []
class Flaky:
def load_config(self):
attempts.append(1)
if len(attempts) == 1:
raise RuntimeError("config.json unreadable")
return {}
monkeypatch.setattr(config_manager_module, "ConfigManager", Flaky)
with pytest.raises(RuntimeError):
manager.config_manager
assert isinstance(manager.config_manager, Flaky)
assert len(attempts) == 2
def test_a_manager_made_without_init_still_answers(built):
bare = CacheManager.__new__(CacheManager)
bare.logger = MagicMock()
assert bare.config_manager is built[0]