Fix Monitor metrics history never being trimmed to max_size#1474
Fix Monitor metrics history never being trimmed to max_size#1474JSap0914 wants to merge 1 commit into
Conversation
The trim step in Monitor._monitor_fn rebound the loop variable with a sliced copy (metrics = metrics[-self.max_size:]) instead of mutating the list stored in metrics_dict, so the histories grew without bound and max_size had no effect. Extract the logic into Monitor._trim_metrics and trim in place via slice assignment so the stored lists are actually capped. Add unit tests for the trimming behavior.
ErenAta16
left a comment
There was a problem hiding this comment.
Textbook version of the "rebinding a loop variable doesn't mutate the container" bug: metrics = metrics[-self.max_size:] only repoints the local name at a new list, self.metrics_dict[key] still holds the original untrimmed one. The fix uses metrics[:] = ... (slice assignment), which mutates the existing list object in place, so anyone else holding a reference to that same list (like metrics_dict) sees the trim too.
The two tests are the right pair: one checks the actual trimming behavior (keeps the most recent max_size entries), the other checks identity (monitor.metrics_dict[cpu] is stored) specifically to catch a regression back to rebind-instead-of-mutate, which the first test alone wouldn't necessarily catch if someone "fixed" it by reassigning self.metrics_dict[key] = ... instead (that would also produce a correct value, just not the same object, and would matter if anything else was holding a reference to the pre-trim list). Good fix, good tests.
ErenAta16
left a comment
There was a problem hiding this comment.
Classic Python trap and a real bug: metrics = metrics[-self.max_size:] inside the loop only rebinds the local metrics name to a new list — it never assigns back into self.metrics_dict[key], so the dict keeps referencing the original, ever-growing list. Ran the new test_metrics.py — 2 passed, and pulling the trim into a standalone _trim_metrics helper is a good call since it lets the test exercise the actual bug without needing to spin up the async monitoring loop.
The bug
Monitor._monitor_fnis supposed to cap each metric history atmax_sizeentries:
metrics = metrics[-self.max_size:]just rebinds the local loop variable to anew sliced list; it never writes back into
self.metrics_dict. The storedlists are therefore never trimmed, so
CPU_USAGE/MEM_USAGE/GPU histories growwithout bound for the lifetime of the monitor and
max_sizehas no effect.The fix
Trim in place with slice assignment so the list object referenced by
metrics_dictis actually shortened, and pull the logic into a smallMonitor._trim_metricshelper so it can be unit-tested directly withoutspinning up the async monitoring loop.
Verification
Added
tests/unit/test_metrics.pycovering both that over-long lists are cappedto the most-recent
max_sizeentries and that the stored list object is mutatedin place.
Before the fix these tests fail (the lists keep all appended values).