Files
FabledSteward/plugins/traefik/migrations/versions/traefik_005_reload_at.py
T
bvandeusenandClaude Opus 5.5 7ce61a2099
CI / lint (push) Failing after 2s
CI / integration (push) Successful in 54s
CI / unit (push) Successful in 1m24s
CI / publish (push) Skipped
fix(traefik): grade routers on their last 15 minutes; the Config tile reads Traefik's reload time (#5355)
The operator: problem cards "can disappear so quickly". Each router was
graded on its latest scrape, so a 4xx share near the 1% cutoff crossed it
on one scrape and fell back on the next. recent_window reads every
router's last 15 minutes in one query, the error shares weighted by
traffic; grade_routers uses it on the page and the dashboard widget, and
the why says "over the last 15 min".

traefik_config_last_reload_success is the Unix time of the last good
reload, not a 0/1 flag, so the tile said "Reload failed" on every scrape.
traefik_005 stores it as config_last_reload_at, and the tile reads
"Config reloaded 3m ago". The router index also carries the 4xx share.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:26:10 -04:00

42 lines
1.9 KiB
Python

"""Traefik's last good config reload is a moment; routers are graded on a window
- traefik_config_last_reload_success is the Unix time of the last successful
reload. It was stored as a float and read as 1.0 = success, so the page said
"Reload failed" on every scrape (#5355). The column becomes the moment; the
old values were timestamps misread, so they go with it.
- Routers are now graded on their last 15 minutes (routes.recent_window),
which reads the 4xx share too: the covering router index carries it, so
that read stays index-only.
Revision ID: traefik_005_reload_at
Revises: traefik_004_covering_indexes
Create Date: 2026-10-08
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "traefik_005_reload_at"
down_revision: Union[str, None] = "traefik_004_covering_indexes"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
_CHARTED = ["request_rate", "latency_p95_ms", "error_rate_5xx_pct"]
def upgrade() -> None:
op.drop_column("traefik_global_stats", "config_last_reload_success")
op.add_column("traefik_global_stats",
sa.Column("config_last_reload_at", sa.DateTime(timezone=True), nullable=True))
op.drop_index("ix_traefik_metrics_router_scraped", "traefik_metrics")
op.create_index("ix_traefik_metrics_router_scraped", "traefik_metrics", ["router_name", "scraped_at"],
postgresql_include=_CHARTED + ["error_rate_4xx_pct"])
def downgrade() -> None:
op.drop_index("ix_traefik_metrics_router_scraped", "traefik_metrics")
op.create_index("ix_traefik_metrics_router_scraped", "traefik_metrics", ["router_name", "scraped_at"],
postgresql_include=_CHARTED)
op.drop_column("traefik_global_stats", "config_last_reload_at")
op.add_column("traefik_global_stats", sa.Column("config_last_reload_success", sa.Float, nullable=True))