- 039-dashboard-scenario-ui: agent workspace (WorkspaceModel, scenario views, parameters/baselines, artifact preview, HITL save/approval, evidence/VLM review, pipeline verification views) + contextVersion=2 scenario intent + prototype. - 040-dashboard-load-testing: capacity/matrix/profile, runner pool, timing, circuit breaker, PROD gate, cache/bounded/consistency, comparison/schedule, API + frontend + prototype. - 041-dataset-lineage-blast-radius: usage index, severity/schema-diff, propagation, deprecation, fanout, API + frontend + prototype (R9 labels/metrics/recreate). - 036/037 amendments: dataset_updated trigger + fanout_plan_id, lineage deprecation gate, 040 read-model traceability. - Includes pre-existing 042-rls-management-workspace and 043-idm-account-integration work.
224 lines
12 KiB
Python
224 lines
12 KiB
Python
# #region Alembic.AddLoadTestingTables [C:3] [TYPE Module] [SEMANTICS alembic,load-testing,profile,variation,run,execution,aggregate,finding]
|
|
# @ingroup Alembic
|
|
# @BRIEF Add 040 dashboard-load-testing tables: load_profiles, load_variations, load_runs,
|
|
# load_executions, load_run_aggregates, consistency_findings.
|
|
# @LAYER Database
|
|
# @RELATION DEPENDS_ON -> [Models.LoadTesting]
|
|
# @INVARIANT load_variations composite PK (run_id, variation_id) matches the ORM — a variation is
|
|
# immutable and bound to exactly one run.
|
|
# @INVARIANT load_run_aggregates unique (run_id, chart_id, variation_id) matches the ORM
|
|
# UniqueConstraint — one aggregate per chart/variation cell.
|
|
# @INVARIANT load_runs.profile_id FK is nullable with ON DELETE SET NULL — a run survives profile
|
|
# deletion for audit retention (mirrors verification_runs.repository_id semantics).
|
|
# @INVARIANT execution/cascade tables use ON DELETE CASCADE — partial results are queryable only
|
|
# while the run exists; the run row itself is never cascade-deleted by these tables.
|
|
# @RATIONALE JSON columns (variation_axes, circuit_breaker, blast_radius_report, sample_rows,
|
|
# cache_counts, execution_ids) hold bounded structures read whole — matching the 041
|
|
# lineage JSON convention and R6 persistence decisions. Statuses are plain String
|
|
# columns so terminal-immutability is enforced in the ORM layer, not by a DB enum.
|
|
# @REJECTED A DB enum for run status was rejected — the ORM set_status guard owns immutability and
|
|
# additive future statuses must not require a migration. Storing full response blobs was
|
|
# rejected (R6) — the schema keeps digest/count/sample only.
|
|
|
|
"""add load testing tables
|
|
|
|
Revision ID: n1o2p3q4r5s6
|
|
Revises: m7n8o9p1q2r3
|
|
Create Date: 2026-08-04 22:30:00.000000
|
|
"""
|
|
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
|
|
from alembic import op
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "n1o2p3q4r5s6"
|
|
down_revision: str | Sequence[str] | None = "m7n8o9p1q2r3"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Create the load-testing tables in dependency order."""
|
|
op.create_table(
|
|
"load_profiles",
|
|
sa.Column("id", sa.String(), nullable=False),
|
|
sa.Column("dashboard_id", sa.Integer(), nullable=False),
|
|
sa.Column("environment_id", sa.String(), nullable=False),
|
|
sa.Column("revision", sa.Integer(), nullable=False),
|
|
sa.Column("concurrency_requested", sa.Integer(), nullable=False),
|
|
sa.Column("execution_mode", sa.String(length=16), nullable=False),
|
|
sa.Column("iterations", sa.Integer(), nullable=True),
|
|
sa.Column("duration_seconds", sa.Integer(), nullable=True),
|
|
sa.Column("ramp_steps", sa.JSON(), nullable=False),
|
|
sa.Column("variation_axes", sa.JSON(), nullable=False),
|
|
sa.Column("circuit_breaker", sa.JSON(), nullable=False),
|
|
sa.Column("matrix_seed", sa.Integer(), nullable=False),
|
|
sa.Column("max_variations", sa.Integer(), nullable=False),
|
|
sa.Column("enabled", sa.Boolean(), nullable=False),
|
|
sa.Column("created_by", sa.String(), nullable=False),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.Column("updated_at", sa.DateTime(), nullable=False),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index("ix_load_profiles_dashboard", "load_profiles", ["dashboard_id"])
|
|
op.create_index("ix_load_profiles_environment", "load_profiles", ["environment_id"])
|
|
|
|
op.create_table(
|
|
"load_runs",
|
|
sa.Column("id", sa.String(), nullable=False),
|
|
sa.Column(
|
|
"profile_id",
|
|
sa.String(),
|
|
sa.ForeignKey("load_profiles.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
),
|
|
sa.Column("profile_revision", sa.Integer(), nullable=False),
|
|
sa.Column("environment_id", sa.String(), nullable=False),
|
|
sa.Column("dashboard_id", sa.Integer(), nullable=False),
|
|
sa.Column("status", sa.String(length=24), nullable=False),
|
|
sa.Column("phase", sa.String(length=16), nullable=False),
|
|
sa.Column("effective_concurrency", sa.Integer(), nullable=False),
|
|
sa.Column("matrix_seed", sa.Integer(), nullable=False),
|
|
sa.Column("theoretical_variations", sa.Integer(), nullable=False),
|
|
sa.Column("selected_variations", sa.Integer(), nullable=False),
|
|
sa.Column("total_executions", sa.Integer(), nullable=False),
|
|
sa.Column("blast_radius_fingerprint", sa.String(length=64), nullable=True),
|
|
sa.Column("blast_radius_report", sa.JSON(), nullable=True),
|
|
sa.Column("approval_gate_id", sa.String(), nullable=True),
|
|
sa.Column("task_id", sa.String(), nullable=True),
|
|
sa.Column("circuit_state", sa.String(length=16), nullable=False),
|
|
sa.Column("started_at", sa.DateTime(), nullable=True),
|
|
sa.Column("finished_at", sa.DateTime(), nullable=True),
|
|
sa.Column("stop_reason", sa.String(), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index("ix_load_runs_profile", "load_runs", ["profile_id"])
|
|
op.create_index("ix_load_runs_environment", "load_runs", ["environment_id"])
|
|
op.create_index("ix_load_runs_task", "load_runs", ["task_id"])
|
|
op.create_index("ix_load_runs_created", "load_runs", ["created_at"])
|
|
|
|
op.create_table(
|
|
"load_variations",
|
|
sa.Column("run_id", sa.String(), nullable=False),
|
|
sa.Column("variation_id", sa.String(length=64), nullable=False),
|
|
sa.Column("filters", sa.JSON(), nullable=False),
|
|
sa.Column("viewport", sa.JSON(), nullable=False),
|
|
sa.Column("role", sa.String(length=32), nullable=False),
|
|
sa.Column("time_range", sa.JSON(), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.ForeignKeyConstraint(["run_id"], ["load_runs.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("run_id", "variation_id"),
|
|
)
|
|
op.create_index("ix_load_variations_run", "load_variations", ["run_id"])
|
|
|
|
op.create_table(
|
|
"load_executions",
|
|
sa.Column("id", sa.String(), nullable=False),
|
|
sa.Column("run_id", sa.String(), nullable=False),
|
|
sa.Column("variation_id", sa.String(length=64), nullable=False),
|
|
sa.Column("chart_id", sa.Integer(), nullable=False),
|
|
sa.Column("filters_hash", sa.String(length=64), nullable=False),
|
|
sa.Column("outcome", sa.String(length=16), nullable=False),
|
|
sa.Column("error_taxonomy", sa.String(length=64), nullable=True),
|
|
sa.Column("queue_wait_ms", sa.Integer(), nullable=False),
|
|
sa.Column("resource_wait_ms", sa.Integer(), nullable=False),
|
|
sa.Column("upstream_latency_ms", sa.Integer(), nullable=False),
|
|
sa.Column("end_to_end_ms", sa.Integer(), nullable=False),
|
|
sa.Column("response_sha256", sa.String(length=64), nullable=True),
|
|
sa.Column("row_count", sa.Integer(), nullable=True),
|
|
sa.Column("sample_rows", sa.JSON(), nullable=True),
|
|
sa.Column("cache_state", sa.String(length=16), nullable=False),
|
|
sa.Column("is_cached", sa.Boolean(), nullable=True),
|
|
sa.Column("cache_key", sa.String(), nullable=True),
|
|
sa.Column("cached_dttm", sa.DateTime(), nullable=True),
|
|
sa.Column("queried_dttm", sa.DateTime(), nullable=True),
|
|
sa.Column("cache_timeout", sa.Integer(), nullable=True),
|
|
sa.Column("cache_state_source", sa.String(length=32), nullable=False),
|
|
sa.Column("worker_id", sa.String(length=64), nullable=False),
|
|
sa.Column("started_at", sa.DateTime(), nullable=False),
|
|
sa.Column("finished_at", sa.DateTime(), nullable=False),
|
|
sa.ForeignKeyConstraint(["run_id"], ["load_runs.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index("ix_load_executions_run", "load_executions", ["run_id"])
|
|
op.create_index(
|
|
"ix_load_executions_run_chart_var",
|
|
"load_executions",
|
|
["run_id", "chart_id", "variation_id"],
|
|
)
|
|
|
|
op.create_table(
|
|
"load_run_aggregates",
|
|
sa.Column("id", sa.String(), nullable=False),
|
|
sa.Column("run_id", sa.String(), nullable=False),
|
|
sa.Column("chart_id", sa.Integer(), nullable=False),
|
|
sa.Column("variation_id", sa.String(length=64), nullable=False),
|
|
sa.Column("success_count", sa.Integer(), nullable=False),
|
|
sa.Column("error_count", sa.Integer(), nullable=False),
|
|
sa.Column("p50_upstream_ms", sa.Integer(), nullable=False),
|
|
sa.Column("p90_upstream_ms", sa.Integer(), nullable=False),
|
|
sa.Column("p95_upstream_ms", sa.Integer(), nullable=False),
|
|
sa.Column("p99_upstream_ms", sa.Integer(), nullable=False),
|
|
sa.Column("p95_queue_wait_ms", sa.Integer(), nullable=False),
|
|
sa.Column("p95_resource_wait_ms", sa.Integer(), nullable=False),
|
|
sa.Column("throughput_per_second", sa.Float(), nullable=False),
|
|
sa.Column("cache_counts", sa.JSON(), nullable=False),
|
|
sa.ForeignKeyConstraint(["run_id"], ["load_runs.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
sa.UniqueConstraint(
|
|
"run_id",
|
|
"chart_id",
|
|
"variation_id",
|
|
name="uq_load_run_aggregate_run_chart_var",
|
|
),
|
|
)
|
|
|
|
op.create_table(
|
|
"consistency_findings",
|
|
sa.Column("id", sa.String(), nullable=False),
|
|
sa.Column("run_id", sa.String(), nullable=False),
|
|
sa.Column("chart_id", sa.Integer(), nullable=False),
|
|
sa.Column("coordinate_key", sa.String(length=64), nullable=False),
|
|
sa.Column("first_response_sha256", sa.String(length=64), nullable=False),
|
|
sa.Column("divergent_response_sha256", sa.String(length=64), nullable=False),
|
|
sa.Column("execution_ids", sa.JSON(), nullable=False),
|
|
sa.Column("severity", sa.String(length=16), nullable=False),
|
|
sa.Column("classification", sa.String(length=16), nullable=False),
|
|
sa.Column("created_at", sa.DateTime(), nullable=False),
|
|
sa.ForeignKeyConstraint(["run_id"], ["load_runs.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index("ix_consistency_findings_run", "consistency_findings", ["run_id"])
|
|
op.create_index("ix_consistency_findings_chart", "consistency_findings", ["chart_id"])
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Drop the load-testing tables in reverse dependency order."""
|
|
op.drop_index("ix_consistency_findings_chart", table_name="consistency_findings")
|
|
op.drop_index("ix_consistency_findings_run", table_name="consistency_findings")
|
|
op.drop_table("consistency_findings")
|
|
|
|
op.drop_table("load_run_aggregates")
|
|
|
|
op.drop_index("ix_load_executions_run_chart_var", table_name="load_executions")
|
|
op.drop_index("ix_load_executions_run", table_name="load_executions")
|
|
op.drop_table("load_executions")
|
|
|
|
op.drop_index("ix_load_variations_run", table_name="load_variations")
|
|
op.drop_table("load_variations")
|
|
|
|
op.drop_index("ix_load_runs_created", table_name="load_runs")
|
|
op.drop_index("ix_load_runs_task", table_name="load_runs")
|
|
op.drop_index("ix_load_runs_environment", table_name="load_runs")
|
|
op.drop_index("ix_load_runs_profile", table_name="load_runs")
|
|
op.drop_table("load_runs")
|
|
|
|
op.drop_index("ix_load_profiles_environment", table_name="load_profiles")
|
|
op.drop_index("ix_load_profiles_dashboard", table_name="load_profiles")
|
|
op.drop_table("load_profiles")
|
|
# #endregion Alembic.AddLoadTestingTables
|