feat(metrics): each rolled-up hour keeps the level it held, its p95 (#5738)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m16s
CI / unit (push) Successful in 1m31s
CI / publish (push) Successful in 1m3s

plugin_metrics_hourly gains value_p95 beside value_avg and value_max. A
two-minute backup spike raises the hour's peak and leaves its p95 low; an
hour spent at that level raises both. Learned normals (milestone 513) need
that difference to call a recurring spike usual without calling the same
level held at another hour usual too. Hours rolled up before this keep a
NULL p95 and read as their average.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-14 12:45:42 -04:00
co-authored by Claude Opus 5.5
parent 2119d20f18
commit cbbf186f17
5 changed files with 114 additions and 3 deletions
+4
View File
@@ -70,6 +70,10 @@ class PluginMetricHourly(Base):
bucket: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
value_avg: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
value_max: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
# The level the hour held for all but its briefest 5% (#5738): a spike of
# a minute or two raises value_max and leaves this low. NULL on hours
# rolled up before it was kept; read those as value_avg.
value_p95: Mapped[float | None] = mapped_column(Float, nullable=True)
sample_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
__table_args__ = (