Files
cleveragents-core/benchmarks/decision_persistence_bench.py
brent.edwards 32b81793a2 refactor(test): rename decision persistence files to avoid conflict with master
Rename our serialization-focused decision persistence suites to
*_serialization to avoid add/add conflicts with the repository-based
decision persistence suites that landed on master independently:

- decision_persistence.feature -> decision_persistence_serialization.feature
- decision_persistence_steps.py -> decision_persistence_serialization_steps.py
- decision_persistence_bench.py -> decision_persistence_serialization_bench.py
- decision_persistence.robot -> decision_persistence_serialization.robot
- helper_decision_persistence.py -> helper_decision_persistence_serialization.py

Updated robot helper path, step docstring, and testing.md references.
2026-02-26 16:13:44 +00:00

247 lines
7.0 KiB
Python

"""ASV benchmarks for DecisionRepository persistence operations.
Measures create, get, get_by_plan, get_tree, and list_by_type performance.
"""
from __future__ import annotations
import sys
from datetime import UTC, datetime
from pathlib import Path
try:
from cleveragents.domain.models.core.action import Action, ActionState
from cleveragents.domain.models.core.decision import (
Decision,
DecisionType,
)
from cleveragents.domain.models.core.plan import (
NamespacedName,
Plan,
PlanIdentity,
PlanPhase,
PlanTimestamps,
ProcessingState,
)
from cleveragents.infrastructure.database.models import Base
from cleveragents.infrastructure.database.repositories import (
ActionRepository,
DecisionRepository,
LifecyclePlanRepository,
)
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
from cleveragents.domain.models.core.action import Action, ActionState
from cleveragents.domain.models.core.decision import (
Decision,
DecisionType,
)
from cleveragents.domain.models.core.plan import (
NamespacedName,
Plan,
PlanIdentity,
PlanPhase,
PlanTimestamps,
ProcessingState,
)
from cleveragents.infrastructure.database.models import Base
from cleveragents.infrastructure.database.repositories import (
ActionRepository,
DecisionRepository,
LifecyclePlanRepository,
)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
_PLAN_ID = "01HV00000000000000BENCH001"
def _make_db():
"""Create an in-memory SQLite DB with all tables and prerequisites."""
engine = create_engine("sqlite:///:memory:", echo=False)
Base.metadata.create_all(engine)
sm = sessionmaker(bind=engine)
session = sm()
factory = lambda: session # noqa: E731
# Action prerequisite
action_repo = ActionRepository(session_factory=factory)
action = Action(
namespaced_name=NamespacedName.parse("local/bench-decision-action"),
description="Benchmark action",
definition_of_done="Done",
strategy_actor="local/s",
execution_actor="local/e",
state=ActionState.AVAILABLE,
created_at=datetime(2026, 1, 1, tzinfo=UTC),
updated_at=datetime(2026, 1, 1, tzinfo=UTC),
)
action_repo.create(action)
session.commit()
# Plan prerequisite
plan_repo = LifecyclePlanRepository(session_factory=factory)
now = datetime(2026, 3, 1, tzinfo=UTC)
plan = Plan(
identity=PlanIdentity(plan_id=_PLAN_ID, attempt=1),
namespaced_name=NamespacedName(namespace="local", name="bench-plan"),
action_name="local/bench-decision-action",
description="Benchmark plan",
definition_of_done="Done",
phase=PlanPhase.STRATEGIZE,
processing_state=ProcessingState.PROCESSING,
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,
)
plan_repo.create(plan)
session.commit()
return session, factory
def _make_decision(seq=0, parent_id=None, dtype=DecisionType.PROMPT_DEFINITION):
return Decision(
plan_id=_PLAN_ID,
parent_decision_id=parent_id,
sequence_number=seq,
decision_type=dtype,
question="Benchmark question?",
chosen_option="Benchmark option",
)
class TimeDecisionCreate:
"""Benchmark decision creation."""
timeout = 60
def setup(self):
self.session, self.factory = _make_db()
self.repo = DecisionRepository(session_factory=self.factory)
self._seq = 100
def time_create_single(self):
self._seq += 1
d = _make_decision(seq=self._seq, dtype=DecisionType.STRATEGY_CHOICE)
self.repo.create(d)
self.session.commit()
class TimeDecisionGet:
"""Benchmark decision retrieval."""
timeout = 60
def setup(self):
self.session, self.factory = _make_db()
self.repo = DecisionRepository(session_factory=self.factory)
d = _make_decision()
self.repo.create(d)
self.session.commit()
self.decision_id = d.decision_id
def time_get_by_id(self):
self.repo.get(self.decision_id)
class TimeDecisionGetByPlan:
"""Benchmark get_by_plan with 50 pre-seeded decisions."""
timeout = 60
def setup(self):
self.session, self.factory = _make_db()
self.repo = DecisionRepository(session_factory=self.factory)
root = _make_decision(seq=0)
self.repo.create(root)
root_id = root.decision_id
for i in range(1, 50):
d = _make_decision(
seq=i,
parent_id=root_id,
dtype=DecisionType.STRATEGY_CHOICE,
)
self.repo.create(d)
self.session.commit()
def time_get_by_plan(self):
self.repo.get_by_plan(_PLAN_ID)
class TimeDecisionTree:
"""Benchmark get_tree with a 3-level tree."""
timeout = 60
def setup(self):
self.session, self.factory = _make_db()
self.repo = DecisionRepository(session_factory=self.factory)
root = _make_decision(seq=0)
self.repo.create(root)
self.root_id = root.decision_id
seq = 1
for _ in range(3):
child = _make_decision(
seq=seq,
parent_id=root.decision_id,
dtype=DecisionType.STRATEGY_CHOICE,
)
self.repo.create(child)
for _ in range(2):
seq += 1
gc = _make_decision(
seq=seq,
parent_id=child.decision_id,
dtype=DecisionType.IMPLEMENTATION_CHOICE,
)
self.repo.create(gc)
seq += 1
self.session.commit()
def time_get_tree(self):
self.repo.get_tree(self.root_id)
class TimeDecisionListByType:
"""Benchmark list_by_type with pre-seeded typed decisions."""
timeout = 60
def setup(self):
self.session, self.factory = _make_db()
self.repo = DecisionRepository(session_factory=self.factory)
root = _make_decision(seq=0)
self.repo.create(root)
root_id = root.decision_id
for i in range(1, 20):
d = _make_decision(
seq=i,
parent_id=root_id,
dtype=DecisionType.STRATEGY_CHOICE,
)
self.repo.create(d)
for i in range(20, 30):
d = _make_decision(
seq=i,
parent_id=root_id,
dtype=DecisionType.IMPLEMENTATION_CHOICE,
)
self.repo.create(d)
self.session.commit()
def time_list_strategy_choices(self):
self.repo.list_by_type(_PLAN_ID, "strategy_choice")
def time_list_implementation_choices(self):
self.repo.list_by_type(_PLAN_ID, "implementation_choice")