"""ASV benchmarks for plan phase rebaseline migration (a5_005). Measures: - Schema creation time with rebaselined constraints - Plan insert throughput with Action phase - Plan insert throughput with Apply terminal states """ from __future__ import annotations import threading from datetime import UTC, datetime from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from cleveragents.infrastructure.database.models import ( Base, LifecycleActionModel, LifecyclePlanModel, ) # Thread-safe counter so repeated ASV iterations never collide on IDs. _iter_lock = threading.Lock() _iter_counter = 0 def _next_iter() -> int: """Return a monotonically increasing iteration number.""" global _iter_counter with _iter_lock: _iter_counter += 1 return _iter_counter def _now_iso() -> str: return datetime.now(tz=UTC).isoformat() def _make_ulid(n: int) -> str: return str(n).zfill(26) class SchemaCreationWithRebaseline: """Benchmark schema creation including rebaselined v3_plans constraints.""" def time_create_all_tables(self) -> None: engine = create_engine("sqlite:///:memory:") Base.metadata.create_all(engine) engine.dispose() class PlanInsertActionPhase: """Benchmark inserting plans with phase='action' (new phase).""" def setup(self) -> None: self.engine = create_engine("sqlite:///:memory:") Base.metadata.create_all(self.engine) self.session_factory = sessionmaker(bind=self.engine) # Create parent action session = self.session_factory() action = LifecycleActionModel() action.namespaced_name = "bench/phase-action" action.namespace = "bench" action.name = "phase-action" action.description = "Benchmark action" action.definition_of_done = "Done" action.strategy_actor = "bench/s" action.execution_actor = "bench/e" action.state = "available" action.tags_json = "[]" action.created_at = _now_iso() action.updated_at = _now_iso() session.add(action) session.commit() session.close() def teardown(self) -> None: self.engine.dispose() def time_insert_100_action_phase_plans(self) -> None: it = _next_iter() base = it * 10000 session = self.session_factory() now = _now_iso() for i in range(100): plan = LifecyclePlanModel() pid = _make_ulid(base + i) plan.plan_id = pid plan.root_plan_id = pid plan.action_name = "bench/phase-action" plan.namespaced_name = f"bench/plan-{it}-{i}" plan.namespace = "bench" plan.phase = "action" plan.processing_state = "queued" plan.description = f"Benchmark plan {it}-{i}" plan.tags_json = "[]" plan.effective_profile_snapshot = "{}" plan.created_at = now plan.updated_at = now session.add(plan) session.commit() session.close() class PlanInsertApplyTerminalStates: """Benchmark inserting plans with Apply terminal states.""" def setup(self) -> None: self.engine = create_engine("sqlite:///:memory:") Base.metadata.create_all(self.engine) self.session_factory = sessionmaker(bind=self.engine) session = self.session_factory() action = LifecycleActionModel() action.namespaced_name = "bench/terminal-action" action.namespace = "bench" action.name = "terminal-action" action.description = "Benchmark terminal action" action.definition_of_done = "Done" action.strategy_actor = "bench/s" action.execution_actor = "bench/e" action.state = "available" action.tags_json = "[]" action.created_at = _now_iso() action.updated_at = _now_iso() session.add(action) session.commit() session.close() def teardown(self) -> None: self.engine.dispose() def time_insert_50_applied_plans(self) -> None: it = _next_iter() base = it * 10000 session = self.session_factory() now = _now_iso() for i in range(50): plan = LifecyclePlanModel() pid = _make_ulid(base + i) plan.plan_id = pid plan.root_plan_id = pid plan.action_name = "bench/terminal-action" plan.namespaced_name = f"bench/applied-{it}-{i}" plan.namespace = "bench" plan.phase = "apply" plan.processing_state = "applied" plan.description = f"Applied plan {it}-{i}" plan.tags_json = "[]" plan.effective_profile_snapshot = "{}" plan.created_at = now plan.updated_at = now session.add(plan) session.commit() session.close() def time_insert_50_constrained_plans(self) -> None: it = _next_iter() base = it * 10000 session = self.session_factory() now = _now_iso() for i in range(50): plan = LifecyclePlanModel() pid = _make_ulid(base + i) plan.plan_id = pid plan.root_plan_id = pid plan.action_name = "bench/terminal-action" plan.namespaced_name = f"bench/constrained-{it}-{i}" plan.namespace = "bench" plan.phase = "apply" plan.processing_state = "constrained" plan.description = f"Constrained plan {it}-{i}" plan.tags_json = "[]" plan.effective_profile_snapshot = "{}" plan.created_at = now plan.updated_at = now session.add(plan) session.commit() session.close()