ca1c341b18
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 19s
CI / security (pull_request) Successful in 28s
CI / typecheck (pull_request) Successful in 32s
CI / integration_tests (pull_request) Successful in 2m39s
CI / unit_tests (pull_request) Successful in 5m22s
CI / docker (pull_request) Successful in 38s
CI / benchmark-regression (pull_request) Successful in 17m47s
CI / coverage (pull_request) Successful in 19m6s
CI / lint (push) Successful in 14s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 20s
CI / security (push) Successful in 29s
CI / typecheck (push) Successful in 36s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 3m23s
CI / unit_tests (push) Successful in 9m12s
CI / docker (push) Successful in 16s
CI / benchmark-publish (push) Successful in 10m4s
CI / coverage (push) Successful in 19m27s
141 lines
4.9 KiB
Python
141 lines
4.9 KiB
Python
"""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()
|