mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-19 17:39:06 +00:00
fix: address review findings on the low-memory work
Nine CodeRabbit findings, five in code. **Health state (the one that matters).** The non-dict guard did not cover a dict missing fields the callers index directly, which is the shape actually seen in the wild: a record carrying only circuit_state produced `plugin clock-simple operation failed: 'circuit_state'` about fifty times a minute with the panel frozen. The record is now completed against the defaults per field rather than trusted or discarded wholesale. Per field matters: a first pass rejected any incomplete record outright, which reset a tripped breaker and real failure counts to healthy because one optional field was absent -- an existing test caught it. Values of the wrong type (a counter persisted as a string, an unknown circuit_state) fall back individually, valid neighbours survive, and newer fields the schema has grown since (degraded, degraded_reason) are carried through untouched. **Cache ceiling.** MemoryCache.set() accepted entries without bound between cleanup sweeps, which run every 300s by default, so a burst could take the cache far past max_size -- the unbounded growth the limit exists to stop. Eviction now runs under the same lock on every write, sharing one helper with the periodic sweep so the two cannot drift. **Installer, cgroups.** Only cgroup_enable=memory was checked, so a board carrying that without cgroup_memory=1 reported success and got no change, leaving MemoryMax= inert. Each parameter is now checked and appended independently; verified against all four combinations, single line preserved. **Installer, journald.** Persistence was inferred from /var/log/journal being non-empty, which proves neither Storage=persistent nor a size cap -- the directory survives a switch back to volatile. The effective configuration is read instead (systemd-analyze cat-config, falling back to the conf files), and an explicitly configured SystemMaxUse is preserved rather than overwritten. Verified across volatile, persistent-without-cap, persistent-with-user-cap, cap-without-storage, and commented-only configs. **Dependency extras.** _extras_are_satisfied stopped at one level, so a gated dependency that itself requests an extra (requests[socks]) passed on the base distribution's version while the extra's own dependency was missing, and pip was skipped. It now recurses, with a visited (distribution, extras) set so a cycle terminates. Docs: both kernel command-line paths documented (the installer falls back to /boot/cmdline.txt), daemon-reload and restart added after the systemd override example, memory exhaustion added to the SSH summary with its power-cycle-only recovery, and a language on the fenced block for MD040. Tests: five for the health-state repair including the exact wild shape and that record_failure/record_success no longer raise against it, and one for the cache ceiling. Both mutation-checked. Full suite 2927 passed, with the one pre-existing tmpfs failure that also fails on main. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STMbQE4YctTacQXfbYqKuW
This commit is contained in:
co-authored by
Claude Opus 5
parent
ef1e9e0eee
commit
34a7414275
@@ -91,3 +91,77 @@ def test_force_reload_refreshes_stale_in_memory_snapshot():
|
||||
|
||||
# and it asked the cache to bypass the in-memory tier (memory_ttl=0).
|
||||
assert any(c.kwargs.get("memory_ttl") == 0 for c in cache.get.call_args_list)
|
||||
|
||||
|
||||
# --- persisted state that does not match the current schema -------------------
|
||||
#
|
||||
# A record on disk can be missing fields the callers index directly: a partial
|
||||
# write, a restored backup, or a state written by an older schema. Returning it
|
||||
# verbatim raises KeyError inside record_success / record_failure, which takes
|
||||
# the display down in a restart loop that survives reboots, because the bad
|
||||
# entry is on disk and gets read again on the way back up. Observed in the wild
|
||||
# as `plugin clock-simple operation failed: 'circuit_state'`, repeating ~50x a
|
||||
# minute with the panel frozen.
|
||||
|
||||
_INDEXED_FIELDS = (
|
||||
"consecutive_failures", "total_failures", "total_successes",
|
||||
"last_success_time", "last_failure_time", "circuit_state",
|
||||
"circuit_opened_time", "half_open_start_time", "last_error",
|
||||
)
|
||||
|
||||
|
||||
def _tracker_reading(persisted):
|
||||
cache = _cache()
|
||||
cache.get.return_value = persisted
|
||||
return PluginHealthTracker(cache)
|
||||
|
||||
|
||||
def test_partial_state_is_completed_not_returned_raw():
|
||||
"""The shape seen in the wild: one field, everything else absent."""
|
||||
state = _tracker_reading({"circuit_state": "closed"}).get_health_state("p")
|
||||
for field in _INDEXED_FIELDS:
|
||||
assert field in state, f"{field} missing; callers index it directly"
|
||||
|
||||
|
||||
def test_repair_keeps_real_failure_history():
|
||||
"""A record with genuine counts must not be reset to healthy just because
|
||||
an optional field is absent -- that would clear a tripped breaker."""
|
||||
state = _tracker_reading({
|
||||
"consecutive_failures": 5,
|
||||
"total_failures": 5,
|
||||
"circuit_state": "open",
|
||||
}).get_health_state("p")
|
||||
assert state["consecutive_failures"] == 5
|
||||
assert state["total_failures"] == 5
|
||||
assert state["circuit_state"] == "open"
|
||||
|
||||
|
||||
def test_wrong_types_fall_back_per_field():
|
||||
"""A counter persisted as a string would pass a membership check and then
|
||||
fail on the first += 1; an unknown circuit_state would take a branch the
|
||||
breaker has no handling for."""
|
||||
state = _tracker_reading({
|
||||
"consecutive_failures": "3",
|
||||
"circuit_state": "melted",
|
||||
"total_failures": 7,
|
||||
}).get_health_state("p")
|
||||
assert state["consecutive_failures"] == 0
|
||||
assert state["circuit_state"] == CircuitState.CLOSED.value
|
||||
assert state["total_failures"] == 7, "valid neighbours must survive"
|
||||
|
||||
|
||||
def test_newer_fields_are_carried_through():
|
||||
"""degraded/degraded_reason are read with .get() and are not part of the
|
||||
indexed set; repairing must not drop them."""
|
||||
state = _tracker_reading({
|
||||
"circuit_state": "closed", "degraded": True, "degraded_reason": "x",
|
||||
}).get_health_state("p")
|
||||
assert state["degraded"] is True
|
||||
assert state["degraded_reason"] == "x"
|
||||
|
||||
|
||||
def test_recording_against_a_repaired_state_does_not_raise():
|
||||
"""The actual failure: record_failure indexing a field that was not there."""
|
||||
tracker = _tracker_reading({"circuit_state": "closed"})
|
||||
tracker.record_failure("p", Exception("boom"))
|
||||
tracker.record_success("p")
|
||||
|
||||
Reference in New Issue
Block a user