"""ASV benchmarks for project repository operations. Measures: - NamespacedProjectRepository create/get/list throughput - ProjectResourceLinkRepository create_link/list_links throughput """ from __future__ import annotations from datetime import UTC, datetime from sqlalchemy import create_engine, event, text from sqlalchemy.orm import sessionmaker from cleveragents.domain.models.core.project import NamespacedProject from cleveragents.infrastructure.database.models import ( Base, ResourceModel, ResourceTypeModel, ) from cleveragents.infrastructure.database.repositories import ( NamespacedProjectRepository, ProjectResourceLinkRepository, ) def _now_iso() -> str: return datetime.now(tz=UTC).isoformat() def _set_sqlite_pragma(dbapi_conn: object, _connection_record: object) -> None: cursor = dbapi_conn.cursor() # type: ignore[union-attr] cursor.execute("PRAGMA foreign_keys = ON") cursor.close() class ProjectRepositoryCRUD: """Benchmark project repository CRUD operations.""" def setup(self) -> None: self.engine = create_engine("sqlite:///:memory:") event.listen(self.engine, "connect", _set_sqlite_pragma) with self.engine.connect() as conn: conn.execute(text("PRAGMA foreign_keys = ON")) conn.commit() Base.metadata.create_all(self.engine) self.sf = sessionmaker(bind=self.engine) def teardown(self) -> None: self.engine.dispose() def time_create_and_get_project(self) -> None: repo = NamespacedProjectRepository(session_factory=self.sf) session = self.sf() project = NamespacedProject(name="bench-proj", namespace="bench") repo.create(project) session.commit() repo.get("bench/bench-proj") repo.delete("bench/bench-proj") session.commit() def time_list_50_projects(self) -> None: repo = NamespacedProjectRepository(session_factory=self.sf) session = self.sf() for i in range(50): repo.create(NamespacedProject(name=f"proj-{i}", namespace="bench")) session.commit() repo.list_projects(namespace="bench", limit=50) for i in range(50): repo.delete(f"bench/proj-{i}") session.commit() class LinkRepositoryCRUD: """Benchmark link repository operations.""" def setup(self) -> None: self.engine = create_engine("sqlite:///:memory:") event.listen(self.engine, "connect", _set_sqlite_pragma) with self.engine.connect() as conn: conn.execute(text("PRAGMA foreign_keys = ON")) conn.commit() Base.metadata.create_all(self.engine) # Use a single session for all setup operations so FK # relationships are visible within the same transaction. self._setup_session = sessionmaker(bind=self.engine)() self.sf = sessionmaker(bind=self.engine) session = self._setup_session # Resource type rt = ResourceTypeModel() rt.name = "bench/git-checkout" # type: ignore[assignment] rt.namespace = "bench" # type: ignore[assignment] rt.resource_kind = "physical" # type: ignore[assignment] rt.user_addable = True # type: ignore[assignment] rt.created_at = _now_iso() # type: ignore[assignment] rt.updated_at = _now_iso() # type: ignore[assignment] session.add(rt) session.commit() # Project — use the same session so the commit is on the same # connection that created the resource type. project_repo = NamespacedProjectRepository( session_factory=lambda: session, ) project_repo.create(NamespacedProject(name="link-bench", namespace="bench")) session.commit() # Resources for i in range(30): r = ResourceModel() r.resource_id = str(i).zfill(26) # type: ignore[assignment] r.type_name = "bench/git-checkout" # type: ignore[assignment] r.resource_kind = "physical" # type: ignore[assignment] r.created_at = _now_iso() # type: ignore[assignment] r.updated_at = _now_iso() # type: ignore[assignment] session.add(r) session.commit() def teardown(self) -> None: self.engine.dispose() def time_create_and_list_30_links(self) -> None: # Use a scoped session so create_link and remove_link share state. session = self.sf() link_repo = ProjectResourceLinkRepository(session_factory=lambda: session) link_ids: list[str] = [] for i in range(30): link = link_repo.create_link( project_name="bench/link-bench", resource_id=str(i).zfill(26), ) session.commit() link_ids.append(str(link.link_id)) link_repo.list_links("bench/link-bench") for lid in link_ids: link_repo.remove_link(lid) session.commit() session.close()