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>
42 lines
1.9 KiB
Python
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))
|