perf(host_agent): the sensor judgment reads the week once, then only new hours (#5786)
CI / lint (push) Successful in 3s
CI / integration (push) Failing after 1m23s
CI / unit (push) Successful in 1m31s
CI / publish (push) Skipped

On the live instance the hourly refresh took 3 min 8 s, averaging a week of
raw temp_c samples each time. The hourly averages are now kept in memory.
The first refresh after startup reads the whole window. Each later one reads
raw samples from the start of the hour the last read ended in, so the hour
that was still filling is read again whole. merge_hours folds the new rows
in and drops hours older than the window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 21:42:11 -04:00
co-authored by Claude Opus 5.5
parent 2ade4ebc46
commit ec13f3797d
3 changed files with 59 additions and 10 deletions
+10
View File
@@ -208,6 +208,16 @@ def test_sensors_are_judged_from_raw_hours_and_rollups_together(app):
metric_name="temp_c", value=f(99), recorded_at=now))
return await sensors.refresh(s, now)
sensors._hours.clear()
sensors._read_from = None
got = asyncio.run(_go())
assert list(got[host]) == ["SYSTIN"]
assert got[host]["SYSTIN"].startswith("falls as the CPU warms (r -1.00 over 100 h")
async def _again(): # the next hour reads only the hour it ended in
async with app.db_sessionmaker() as s:
return await sensors.refresh(s, now + timedelta(hours=1))
assert sensors._read_from == now.replace(minute=0)
again = asyncio.run(_again())
assert again[host]["SYSTIN"].startswith("falls as the CPU warms (r -1.00 over 100 h")
+10
View File
@@ -72,3 +72,13 @@ def test_a_judged_sensor_is_left_out_of_the_host_temperature_and_listed_with_why
real, unwired = split_temps("util", {"temp3": 62.0, "Package id 0": 55.0})
assert list(real) == ["Package id 0"]
assert unwired == {"temp3": (62.0, "falls as the CPU warms (r -0.81 over 168 h)")}
def test_a_later_read_replaces_the_hour_that_was_filling_and_old_hours_drop():
hours = {"h:temp:a": {T0: 50.0, T0 + timedelta(hours=1): 51.0}}
s.merge_hours(hours, [("h:temp:a", T0 + timedelta(hours=1), 55.0), # the hour, read whole
("h:temp:a", T0 + timedelta(hours=2), 56.0),
("h:temp:b", T0 + timedelta(hours=2), None)], since=T0 + timedelta(hours=1))
assert hours == {"h:temp:a": {T0 + timedelta(hours=1): 55.0, T0 + timedelta(hours=2): 56.0}}
s.merge_hours(hours, [], since=T0 + timedelta(hours=9)) # a sensor gone quiet
assert hours == {}