7a298ede6e
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 30s
CI / build (pull_request) Successful in 24s
CI / security (pull_request) Successful in 40s
CI / typecheck (pull_request) Successful in 1m0s
CI / integration_tests (pull_request) Successful in 4m14s
CI / unit_tests (pull_request) Successful in 16m25s
CI / docker (pull_request) Successful in 55s
CI / benchmark-regression (pull_request) Successful in 22m55s
CI / coverage (pull_request) Successful in 38m4s
CI / lint (push) Successful in 13s
CI / build (push) Successful in 15s
CI / quality (push) Successful in 28s
CI / typecheck (push) Successful in 31s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 31s
CI / integration_tests (push) Successful in 3m19s
CI / benchmark-publish (push) Successful in 14m23s
CI / unit_tests (push) Successful in 14m44s
CI / docker (push) Successful in 38s
CI / coverage (push) Successful in 32m22s
Implemented plan-level and project-level locking with configurable timeouts. Added locks table via Alembic migration storing owner_id, resource_type, resource_id, acquired_at, and expires_at. Locks enforced in PlanLifecycleService transitions. Support for re-entrant acquisition, lock renewal, graceful shutdown release, and startup cleanup of expired locks. Added diagnostics check for stale lock reporting. ISSUES CLOSED: #327
194 lines
6.1 KiB
Python
194 lines
6.1 KiB
Python
"""ASV benchmarks for the validation pipeline.
|
|
|
|
Measures the performance of:
|
|
- Pipeline creation and command sorting
|
|
- Pipeline execution with passing validations
|
|
- Summary building and aggregation
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
try:
|
|
from cleveragents.application.services.validation_pipeline import (
|
|
ValidationCommand,
|
|
ValidationPipeline,
|
|
ValidationResult,
|
|
ValidationSummary,
|
|
)
|
|
from cleveragents.domain.models.core.tool import ValidationMode
|
|
except ModuleNotFoundError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
from cleveragents.application.services.validation_pipeline import (
|
|
ValidationCommand,
|
|
ValidationPipeline,
|
|
ValidationResult,
|
|
ValidationSummary,
|
|
)
|
|
from cleveragents.domain.models.core.tool import ValidationMode
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _mock_executor(validation_name: str, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
"""Trivial executor that always passes."""
|
|
return {"passed": True, "message": f"{validation_name} ok"}
|
|
|
|
|
|
def _build_commands(count: int) -> list[ValidationCommand]:
|
|
"""Build a list of N validation commands across multiple resources."""
|
|
commands: list[ValidationCommand] = []
|
|
for i in range(count):
|
|
resource_name = f"resource-{i % 5}"
|
|
mode = ValidationMode.REQUIRED if i % 3 else ValidationMode.INFORMATIONAL
|
|
commands.append(
|
|
ValidationCommand(
|
|
validation_name=f"check-{i:04d}",
|
|
resource_id=f"R{i:05d}",
|
|
resource_name=resource_name,
|
|
mode=mode,
|
|
arguments={"index": i},
|
|
timeout_seconds=30.0,
|
|
)
|
|
)
|
|
return commands
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Benchmark suites
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class ValidationPipelineCreationSuite:
|
|
"""Benchmarks for pipeline instantiation and command sorting."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self.commands_10 = _build_commands(10)
|
|
self.commands_100 = _build_commands(100)
|
|
|
|
def time_create_pipeline_10_commands(self) -> None:
|
|
"""Benchmark creating a pipeline with 10 commands."""
|
|
ValidationPipeline(
|
|
commands=self.commands_10,
|
|
executor=_mock_executor,
|
|
max_workers=4,
|
|
)
|
|
|
|
def time_create_pipeline_100_commands(self) -> None:
|
|
"""Benchmark creating a pipeline with 100 commands."""
|
|
ValidationPipeline(
|
|
commands=self.commands_100,
|
|
executor=_mock_executor,
|
|
max_workers=4,
|
|
)
|
|
|
|
def time_sort_100_commands(self) -> None:
|
|
"""Benchmark sorting 100 commands."""
|
|
ValidationPipeline._sort_commands(self.commands_100)
|
|
|
|
|
|
class ValidationPipelineExecutionSuite:
|
|
"""Benchmarks for pipeline execution with passing validations."""
|
|
|
|
timeout = 120
|
|
|
|
def setup(self) -> None:
|
|
self.commands_10 = _build_commands(10)
|
|
self.commands_50 = _build_commands(50)
|
|
|
|
def time_run_10_validations(self) -> None:
|
|
"""Benchmark running 10 validations."""
|
|
pipeline = ValidationPipeline(
|
|
commands=self.commands_10,
|
|
executor=_mock_executor,
|
|
max_workers=4,
|
|
)
|
|
pipeline.run()
|
|
|
|
def time_run_50_validations_sequential(self) -> None:
|
|
"""Benchmark running 50 validations with max_workers=1."""
|
|
pipeline = ValidationPipeline(
|
|
commands=self.commands_50,
|
|
executor=_mock_executor,
|
|
max_workers=1,
|
|
)
|
|
pipeline.run()
|
|
|
|
def time_run_10_validations_for_plan(self) -> None:
|
|
"""Benchmark run_for_plan with 10 validations."""
|
|
pipeline = ValidationPipeline(
|
|
commands=self.commands_10,
|
|
executor=_mock_executor,
|
|
max_workers=4,
|
|
)
|
|
metadata: dict[str, Any] = {}
|
|
pipeline.run_for_plan(plan_metadata=metadata)
|
|
|
|
|
|
class ValidationPipelineSummarySuite:
|
|
"""Benchmarks for summary construction and grouping."""
|
|
|
|
timeout = 60
|
|
|
|
def setup(self) -> None:
|
|
self.results: list[ValidationResult] = []
|
|
for i in range(50):
|
|
self.results.append(
|
|
ValidationResult(
|
|
validation_name=f"check-{i:04d}",
|
|
resource_id=f"R{i:05d}",
|
|
resource_name=f"resource-{i % 5}",
|
|
mode=(
|
|
ValidationMode.REQUIRED
|
|
if i % 3
|
|
else ValidationMode.INFORMATIONAL
|
|
),
|
|
passed=i % 2 == 0,
|
|
message=f"Result for check-{i:04d}",
|
|
data=None,
|
|
duration_ms=float(i),
|
|
error=None,
|
|
timed_out=False,
|
|
)
|
|
)
|
|
|
|
def time_group_by_resource_50_results(self) -> None:
|
|
"""Benchmark grouping 50 results by resource."""
|
|
ValidationPipeline.group_by_resource(self.results)
|
|
|
|
def time_build_summary_model(self) -> None:
|
|
"""Benchmark building a ValidationSummary from 50 results."""
|
|
req_p = sum(
|
|
1 for r in self.results if r.mode == ValidationMode.REQUIRED and r.passed
|
|
)
|
|
req_f = sum(
|
|
1
|
|
for r in self.results
|
|
if r.mode == ValidationMode.REQUIRED and not r.passed
|
|
)
|
|
info_p = sum(
|
|
1
|
|
for r in self.results
|
|
if r.mode == ValidationMode.INFORMATIONAL and r.passed
|
|
)
|
|
info_f = sum(
|
|
1
|
|
for r in self.results
|
|
if r.mode == ValidationMode.INFORMATIONAL and not r.passed
|
|
)
|
|
ValidationSummary(
|
|
total=len(self.results),
|
|
required_passed=req_p,
|
|
required_failed=req_f,
|
|
informational_passed=info_p,
|
|
informational_failed=info_f,
|
|
results=self.results,
|
|
)
|