218 lines
6.4 KiB
Python
218 lines
6.4 KiB
Python
"""ASV benchmarks for persistence test runtime baseline.
|
|
|
|
Measures action and plan repository operation latencies to track
|
|
performance regressions in persistence operations.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from cleveragents.domain.models.core.action import (
|
|
Action,
|
|
ActionArgument,
|
|
ActionState,
|
|
ArgumentRequirement,
|
|
ArgumentType,
|
|
)
|
|
from cleveragents.domain.models.core.plan import (
|
|
NamespacedName,
|
|
PlanIdentity,
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
)
|
|
from cleveragents.domain.models.core.plan import Plan as V3Plan
|
|
from cleveragents.infrastructure.database.models import Base
|
|
from cleveragents.infrastructure.database.repositories import (
|
|
ActionRepository,
|
|
LifecyclePlanRepository,
|
|
)
|
|
|
|
_CB32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
|
_bench_counter = 0
|
|
|
|
|
|
def _bench_ulid() -> str:
|
|
global _bench_counter
|
|
_bench_counter += 1
|
|
n = _bench_counter
|
|
suffix = ""
|
|
for _ in range(8):
|
|
suffix = _CB32[n % 32] + suffix
|
|
n //= 32
|
|
return f"01HGZPS10AQDYTR4PS{suffix}"
|
|
|
|
|
|
def _make_bench_action(name: str = "local/ps-bench-action") -> Action:
|
|
"""Create a benchmark action with arguments and invariants."""
|
|
return Action(
|
|
namespaced_name=NamespacedName.parse(name),
|
|
description="Persistence suite benchmark action",
|
|
long_description=None,
|
|
definition_of_done="Done",
|
|
strategy_actor="local/s",
|
|
execution_actor="local/e",
|
|
estimation_actor=None,
|
|
review_actor=None,
|
|
arguments=[
|
|
ActionArgument(
|
|
name="target",
|
|
arg_type=ArgumentType.STRING,
|
|
requirement=ArgumentRequirement.REQUIRED,
|
|
description="Target path",
|
|
),
|
|
],
|
|
invariants=["No breaking changes"],
|
|
reusable=True,
|
|
read_only=False,
|
|
state=ActionState.AVAILABLE,
|
|
created_at=datetime.now(),
|
|
updated_at=datetime.now(),
|
|
created_by=None,
|
|
tags=[],
|
|
)
|
|
|
|
|
|
def _make_bench_plan(plan_id: str | None = None) -> V3Plan:
|
|
now = datetime.now()
|
|
return V3Plan(
|
|
identity=PlanIdentity(plan_id=plan_id or _bench_ulid(), attempt=1),
|
|
namespaced_name=NamespacedName(namespace="local", name="ps-bench-plan"),
|
|
action_name="local/ps-bench-action",
|
|
description="Persistence suite benchmark plan",
|
|
phase=PlanPhase("action"),
|
|
processing_state=ProcessingState("queued"),
|
|
strategy_actor="local/s",
|
|
execution_actor="local/e",
|
|
timestamps=PlanTimestamps(created_at=now, updated_at=now),
|
|
created_by="bench",
|
|
tags=[],
|
|
reusable=True,
|
|
read_only=False,
|
|
)
|
|
|
|
|
|
class TimeActionCreate:
|
|
"""Benchmark action creation latency."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
session = sessionmaker(bind=engine)()
|
|
self.session = session
|
|
self.repo = ActionRepository(session_factory=lambda: session)
|
|
self._counter = 0
|
|
|
|
def time_create_action(self) -> None:
|
|
self._counter += 1
|
|
action = _make_bench_action(f"local/bench-act-{self._counter}")
|
|
self.repo.create(action)
|
|
self.session.commit()
|
|
|
|
|
|
class TimeActionGet:
|
|
"""Benchmark action retrieval latency."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
session = sessionmaker(bind=engine)()
|
|
self.session = session
|
|
self.repo = ActionRepository(session_factory=lambda: session)
|
|
action = _make_bench_action()
|
|
self.repo.create(action)
|
|
session.commit()
|
|
|
|
def time_get_by_name(self) -> None:
|
|
self.repo.get_by_name("local/ps-bench-action")
|
|
|
|
|
|
class TimePlanCreatePersistence:
|
|
"""Benchmark plan creation with FK action."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
session = sessionmaker(bind=engine)()
|
|
self.session = session
|
|
factory = lambda: session # noqa: E731
|
|
action_repo = ActionRepository(session_factory=factory)
|
|
action_repo.create(_make_bench_action())
|
|
session.commit()
|
|
self.repo = LifecyclePlanRepository(session_factory=factory)
|
|
|
|
def time_create_plan(self) -> None:
|
|
plan = _make_bench_plan()
|
|
self.repo.create(plan)
|
|
self.session.commit()
|
|
|
|
|
|
class TimePlanUpdatePhase:
|
|
"""Benchmark plan phase update latency."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
session = sessionmaker(bind=engine)()
|
|
self.session = session
|
|
factory = lambda: session # noqa: E731
|
|
action_repo = ActionRepository(session_factory=factory)
|
|
action_repo.create(_make_bench_action())
|
|
session.commit()
|
|
self.repo = LifecyclePlanRepository(session_factory=factory)
|
|
self.plan_id = _bench_ulid()
|
|
plan = _make_bench_plan(self.plan_id)
|
|
self.repo.create(plan)
|
|
session.commit()
|
|
|
|
def time_update_phase(self) -> None:
|
|
plan = self.repo.get(self.plan_id)
|
|
if plan is not None:
|
|
plan.phase = PlanPhase.STRATEGIZE
|
|
plan.processing_state = ProcessingState.PROCESSING
|
|
plan.timestamps.updated_at = datetime.now()
|
|
self.repo.update(plan)
|
|
self.session.commit()
|
|
|
|
|
|
class TimePlanListFiltered:
|
|
"""Benchmark plan listing with filters."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
engine = create_engine("sqlite:///:memory:")
|
|
Base.metadata.create_all(engine)
|
|
session = sessionmaker(bind=engine)()
|
|
self.session = session
|
|
factory = lambda: session # noqa: E731
|
|
action_repo = ActionRepository(session_factory=factory)
|
|
action_repo.create(_make_bench_action())
|
|
session.commit()
|
|
self.repo = LifecyclePlanRepository(session_factory=factory)
|
|
for _ in range(30):
|
|
plan = _make_bench_plan()
|
|
self.repo.create(plan)
|
|
session.commit()
|
|
|
|
def time_list_all(self) -> None:
|
|
self.repo.list_plans()
|
|
|
|
def time_list_by_phase(self) -> None:
|
|
self.repo.list_plans(phase="action")
|
|
|
|
def time_count(self) -> None:
|
|
self.repo.count()
|