210 lines
6.4 KiB
Python
210 lines
6.4 KiB
Python
"""ASV benchmarks for UnitOfWork lifecycle repository wiring.
|
|
|
|
Measures overhead of creating UnitOfWorkContext and accessing
|
|
actions / lifecycle_plans repositories.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from sqlalchemy import create_engine, event
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
|
|
from cleveragents.domain.models.core.action import Action, ActionState
|
|
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.unit_of_work import UnitOfWorkContext
|
|
|
|
_CB32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
|
_bench_counter = 5000
|
|
|
|
|
|
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"01HGZ6FE0AQDYTR4BX{suffix}"
|
|
|
|
|
|
def _make_bench_action(name: str = "local/bench-uow-action") -> Action:
|
|
parts = name.split("/", 1)
|
|
namespace = parts[0] if len(parts) == 2 else "local"
|
|
short_name = parts[1] if len(parts) == 2 else parts[0]
|
|
now = datetime.now()
|
|
return Action(
|
|
namespaced_name=NamespacedName(namespace=namespace, name=short_name),
|
|
description=f"Bench action {short_name}",
|
|
long_description=None,
|
|
definition_of_done="Done",
|
|
strategy_actor="local/s",
|
|
execution_actor="local/e",
|
|
estimation_actor=None,
|
|
review_actor=None,
|
|
arguments=[],
|
|
reusable=True,
|
|
read_only=False,
|
|
state=ActionState("available"),
|
|
created_at=now,
|
|
updated_at=now,
|
|
created_by=None,
|
|
tags=[],
|
|
)
|
|
|
|
|
|
def _make_bench_plan(
|
|
plan_id: str | None = None,
|
|
action_name: str = "local/bench-uow-action",
|
|
plan_name: str | None = None,
|
|
) -> V3Plan:
|
|
now = datetime.now()
|
|
name = plan_name or f"bench-uow-plan-{_bench_ulid()}"
|
|
return V3Plan(
|
|
identity=PlanIdentity(plan_id=plan_id or _bench_ulid(), attempt=1),
|
|
namespaced_name=NamespacedName(namespace="local", name=name),
|
|
action_name=action_name,
|
|
description="Bench plan",
|
|
definition_of_done="Done",
|
|
strategy_actor="local/s",
|
|
execution_actor="local/e",
|
|
phase=PlanPhase("action"),
|
|
processing_state=ProcessingState("queued"),
|
|
timestamps=PlanTimestamps(created_at=now, updated_at=now),
|
|
project_links=[],
|
|
arguments={},
|
|
arguments_order=[],
|
|
invariants=[],
|
|
reusable=True,
|
|
read_only=False,
|
|
created_by=None,
|
|
tags=[],
|
|
)
|
|
|
|
|
|
class TimeUowContextCreation:
|
|
"""Benchmark UnitOfWorkContext instantiation."""
|
|
|
|
timeout = 30.0
|
|
|
|
def setup(self) -> None:
|
|
self.engine = create_engine(
|
|
"sqlite:///:memory:",
|
|
echo=False,
|
|
future=True,
|
|
connect_args={"check_same_thread": False},
|
|
)
|
|
|
|
@event.listens_for(self.engine, "connect")
|
|
def _fk(dbapi_conn, _rec): # type: ignore[no-untyped-def]
|
|
cursor = dbapi_conn.cursor()
|
|
cursor.execute("PRAGMA foreign_keys=ON")
|
|
cursor.close()
|
|
|
|
Base.metadata.create_all(self.engine)
|
|
self.sf: sessionmaker[Session] = sessionmaker(
|
|
bind=self.engine,
|
|
expire_on_commit=False,
|
|
autoflush=False,
|
|
autocommit=False,
|
|
class_=Session,
|
|
)
|
|
|
|
def time_create_context(self) -> None:
|
|
session = self.sf()
|
|
_ = UnitOfWorkContext(session)
|
|
session.close()
|
|
|
|
def time_access_actions_repo(self) -> None:
|
|
session = self.sf()
|
|
ctx = UnitOfWorkContext(session)
|
|
_ = ctx.actions
|
|
session.close()
|
|
|
|
def time_access_lifecycle_plans_repo(self) -> None:
|
|
session = self.sf()
|
|
ctx = UnitOfWorkContext(session)
|
|
_ = ctx.lifecycle_plans
|
|
session.close()
|
|
|
|
|
|
class TimeUowActionPlanRoundTrip:
|
|
"""Benchmark creating action + plan via UoW and retrieving."""
|
|
|
|
timeout = 30.0
|
|
|
|
def setup(self) -> None:
|
|
self.engine = create_engine(
|
|
"sqlite:///:memory:",
|
|
echo=False,
|
|
future=True,
|
|
connect_args={"check_same_thread": False},
|
|
)
|
|
|
|
@event.listens_for(self.engine, "connect")
|
|
def _fk(dbapi_conn, _rec): # type: ignore[no-untyped-def]
|
|
cursor = dbapi_conn.cursor()
|
|
cursor.execute("PRAGMA foreign_keys=ON")
|
|
cursor.close()
|
|
|
|
Base.metadata.create_all(self.engine)
|
|
self.sf: sessionmaker[Session] = sessionmaker(
|
|
bind=self.engine,
|
|
expire_on_commit=False,
|
|
autoflush=False,
|
|
autocommit=False,
|
|
class_=Session,
|
|
)
|
|
# Seed an action for plan creation benchmarks
|
|
session = self.sf()
|
|
ctx = UnitOfWorkContext(session)
|
|
ctx.actions.create(_make_bench_action("local/bench-uow-action"))
|
|
session.commit()
|
|
session.close()
|
|
|
|
def time_create_action_via_uow(self) -> None:
|
|
session = self.sf()
|
|
ctx = UnitOfWorkContext(session)
|
|
uid = _bench_ulid()
|
|
ctx.actions.create(_make_bench_action(f"local/bench-action-{uid}"))
|
|
session.commit()
|
|
session.close()
|
|
|
|
def time_create_plan_via_uow(self) -> None:
|
|
session = self.sf()
|
|
ctx = UnitOfWorkContext(session)
|
|
ctx.lifecycle_plans.create(_make_bench_plan())
|
|
session.commit()
|
|
session.close()
|
|
|
|
def time_create_action_and_plan_via_uow(self) -> None:
|
|
uid = _bench_ulid()
|
|
# NamespacedName.validate_name lowercases the name, so the action
|
|
# is stored with a lowercased namespaced_name PK. The plan's
|
|
# action_name (a plain-string FK) must match exactly.
|
|
action_name = f"local/bench-combo-{uid}".lower()
|
|
# Commit the action first so the FK reference is durable before
|
|
# the plan repository (which has its own retry/rollback logic)
|
|
# attempts the insert.
|
|
session = self.sf()
|
|
ctx = UnitOfWorkContext(session)
|
|
ctx.actions.create(_make_bench_action(action_name))
|
|
session.commit()
|
|
session.close()
|
|
|
|
session = self.sf()
|
|
ctx = UnitOfWorkContext(session)
|
|
ctx.lifecycle_plans.create(_make_bench_plan(action_name=action_name))
|
|
session.commit()
|
|
session.close()
|