"""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")