#5755 "Traefik latency was a percentile of every response since Traefik started, over unsummed label sets". _percentile_ms read the duration histogram as it stood, which counts from Traefik's start, so p50/p95/p99 were lifetime figures that barely moved. It also walked every code/method label set's buckets as one unsummed list, so equal counts from two sets returned an edge exactly: nextcloud's flat 100.0 ms p95. service_buckets() now takes each bucket's rise since the previous scrape, summed per le over every label set. A scrape with no responses, and the first scrape, record no latency: traefik_007_latency_nullable lets the columns be NULL, the batch skips the figure, and the page shows "—". Found while preparing #5741 "Traefik routers are graded against their own normal", which grades p95 against a learned normal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
31 lines
993 B
Python
31 lines
993 B
Python
"""A router's latency may be absent
|
|
|
|
Latency is now each scrape's own percentile (#5741), and a scrape in which a
|
|
router answered nothing has none: those rows carry NULL rather than a
|
|
made-up 0 or the old lifetime figure.
|
|
|
|
Revision ID: traefik_007_latency_nullable
|
|
Revises: traefik_006_status_codes
|
|
Create Date: 2026-10-14
|
|
"""
|
|
from typing import Sequence, Union
|
|
from alembic import op
|
|
|
|
revision: str = "traefik_007_latency_nullable"
|
|
down_revision: Union[str, None] = "traefik_006_status_codes"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
_COLUMNS = ("latency_p50_ms", "latency_p95_ms", "latency_p99_ms")
|
|
|
|
|
|
def upgrade() -> None:
|
|
for column in _COLUMNS:
|
|
op.alter_column("traefik_metrics", column, nullable=True)
|
|
|
|
|
|
def downgrade() -> None:
|
|
for column in _COLUMNS:
|
|
op.execute(f"UPDATE traefik_metrics SET {column} = 0 WHERE {column} IS NULL")
|
|
op.alter_column("traefik_metrics", column, nullable=False)
|