Files
FabledSteward/plugins/docker/migrations/versions/docker_006_metric_rollup.py
T
bvandeusen faecac3ec6
CI / lint (push) Successful in 3s
CI / unit (push) Successful in 47s
CI / integration (push) Successful in 2m19s
CI / publish (push) Successful in 1m6s
feat(docker): retention + hourly rollup for metrics/events with Settings windows
Bounds Docker time-series growth (the main scaling concern). New
docker_metrics_hourly table + docker_006 migration; a plugin retention module
(docker.run_retention capability) rolls raw docker_metrics older than the raw
window into hourly averages (idempotent upsert), deletes the rolled raw rows,
then prunes stale rollups + lifecycle events. Core cleanup.py drives it each
hourly run via the capability (no plugin-model import), reading the three
retention windows fresh from settings so changes apply without restart (rule 25).

Settings → "Thresholds & Retention" gains a Docker retention card (raw /
rolled-up / events windows, working defaults 7/90/30 days). Unit tests cover the
hour-aligned cutoff/bucketing helpers; integration test exercises the real
rollup-average + prune across both windows.

Milestone 77 task #941.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Jg27rgypiW2efULXJDtMC
2026-06-18 21:40:57 -04:00

46 lines
1.9 KiB
Python

"""Docker hourly metric rollup table
Adds docker_metrics_hourly — the coarse series that retention rolls raw
docker_metrics into before pruning them, so multi-day history stays cheap.
One row per (host, container, hour bucket); the unique constraint is the
conflict target for the idempotent rollup upsert. Additive create_table.
Revision ID: docker_006_metric_rollup
Revises: docker_005_swarm_placement
Create Date: 2026-06-19
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
revision: str = "docker_006_metric_rollup"
down_revision: Union[str, None] = "docker_005_swarm_placement"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"docker_metrics_hourly",
sa.Column("id", sa.String(length=36), nullable=False),
sa.Column("host_id", sa.String(length=36), nullable=False),
sa.Column("container_name", sa.String(length=255), nullable=False),
sa.Column("bucket", sa.DateTime(timezone=True), nullable=False),
sa.Column("cpu_pct", sa.Float(), nullable=False, server_default="0"),
sa.Column("mem_pct", sa.Float(), nullable=False, server_default="0"),
sa.Column("mem_usage_bytes", sa.BigInteger(), nullable=False, server_default="0"),
sa.Column("sample_count", sa.Integer(), nullable=False, server_default="0"),
sa.ForeignKeyConstraint(["host_id"], ["hosts.id"], ondelete="CASCADE"),
sa.PrimaryKeyConstraint("id"),
sa.UniqueConstraint("host_id", "container_name", "bucket",
name="uq_docker_metrics_hourly_bucket"),
)
op.create_index("ix_docker_metrics_hourly_bucket",
"docker_metrics_hourly", ["bucket"])
def downgrade() -> None:
op.drop_index("ix_docker_metrics_hourly_bucket",
table_name="docker_metrics_hourly")
op.drop_table("docker_metrics_hourly")