mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-21 18:39:06 +00:00
fix: use a monotonic clock and only mark metrics persisted once written
Two review findings on the throttle, both right. The interval compared wall-clock timestamps. These devices have no RTC, so the clock jumps by however far off boot-time was the moment NTP first syncs -- a forward jump would allow an early write, a backward one would stall the snapshot well past the interval. time.monotonic() is not subject to either. The timestamp was also recorded before cache_manager.set(). A set() that raised would buy the next interval's silence without leaving a snapshot behind, which is the one case where skipping the write is least affordable. Recorded after the write lands instead, so a failure is retried on the next call. Verified by restoring the original ordering: the new test then reports one write where two are expected. 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
0fd2bfae99
commit
dab4b3ea57
@@ -377,11 +377,14 @@ class PluginResourceMonitor:
|
|||||||
|
|
||||||
Caller must hold ``self._lock``.
|
Caller must hold ``self._lock``.
|
||||||
"""
|
"""
|
||||||
now = time.time()
|
# Monotonic, not wall clock: these devices have no RTC, so the clock
|
||||||
|
# jumps by however far off boot-time was the moment NTP first syncs.
|
||||||
|
# A forward jump would allow an early write, a backward one would
|
||||||
|
# stall the snapshot well past the interval.
|
||||||
|
now = time.monotonic()
|
||||||
if not force and now - self._metrics_persisted_at.get(plugin_id, 0.0) \
|
if not force and now - self._metrics_persisted_at.get(plugin_id, 0.0) \
|
||||||
< _METRICS_PERSIST_INTERVAL:
|
< _METRICS_PERSIST_INTERVAL:
|
||||||
return
|
return
|
||||||
self._metrics_persisted_at[plugin_id] = now
|
|
||||||
cache_key = self._get_metrics_key(plugin_id)
|
cache_key = self._get_metrics_key(plugin_id)
|
||||||
self.cache_manager.set(cache_key, {
|
self.cache_manager.set(cache_key, {
|
||||||
'memory_mb': metrics.memory_mb,
|
'memory_mb': metrics.memory_mb,
|
||||||
@@ -395,6 +398,9 @@ class PluginResourceMonitor:
|
|||||||
else 0.0),
|
else 0.0),
|
||||||
'last_update_time': metrics.last_update_time,
|
'last_update_time': metrics.last_update_time,
|
||||||
})
|
})
|
||||||
|
# Only after the write lands. Marking it first would mean a failed
|
||||||
|
# set() bought the next interval's silence without leaving a snapshot.
|
||||||
|
self._metrics_persisted_at[plugin_id] = now
|
||||||
|
|
||||||
def reset_metrics(self, plugin_id: str) -> None:
|
def reset_metrics(self, plugin_id: str) -> None:
|
||||||
"""Reset metrics for a plugin."""
|
"""Reset metrics for a plugin."""
|
||||||
|
|||||||
@@ -174,3 +174,20 @@ class TestMetricsPersistenceChurn:
|
|||||||
writes = [c for c in cache.set.call_args_list
|
writes = [c for c in cache.set.call_args_list
|
||||||
if c.args and str(c.args[0]).startswith("plugin_metrics:")]
|
if c.args and str(c.args[0]).startswith("plugin_metrics:")]
|
||||||
assert len(writes) == 2, "reset should clear the throttle timestamp"
|
assert len(writes) == 2, "reset should clear the throttle timestamp"
|
||||||
|
|
||||||
|
def test_a_failed_write_does_not_buy_the_next_interval_of_silence(self):
|
||||||
|
"""A set() that raises must not count as having persisted.
|
||||||
|
|
||||||
|
Marking the timestamp before the write would leave no snapshot in the
|
||||||
|
cache and still suppress the next 30 seconds of attempts.
|
||||||
|
"""
|
||||||
|
cache = _cache()
|
||||||
|
cache.set.side_effect = [OSError("disk full"), None]
|
||||||
|
mon = PluginResourceMonitor(cache, enable_monitoring=False)
|
||||||
|
with pytest.raises(OSError):
|
||||||
|
mon.monitor_call("p", lambda: None)
|
||||||
|
# the very next call must try again rather than skip the interval
|
||||||
|
mon.monitor_call("p", lambda: None)
|
||||||
|
writes = [c for c in cache.set.call_args_list
|
||||||
|
if c.args and str(c.args[0]).startswith("plugin_metrics:")]
|
||||||
|
assert len(writes) == 2, "a failed write should be retried, not skipped"
|
||||||
|
|||||||
Reference in New Issue
Block a user