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
123 lines
3.4 KiB
Python
123 lines
3.4 KiB
Python
"""ASV benchmarks for error recovery patterns.
|
|
|
|
Measures the performance of:
|
|
- Error classification
|
|
- Recovery hint generation
|
|
- ErrorRecord/ErrorHistory model operations
|
|
- ErrorRecoveryService.record_error() with mock lifecycle
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock
|
|
|
|
try:
|
|
from cleveragents.application.services.error_recovery_service import (
|
|
ErrorRecoveryService,
|
|
)
|
|
from cleveragents.domain.models.core.error_recovery import (
|
|
ErrorCategory,
|
|
classify_error,
|
|
get_recovery_hints,
|
|
)
|
|
from cleveragents.domain.models.core.plan import (
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
)
|
|
except ModuleNotFoundError:
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
|
from cleveragents.application.services.error_recovery_service import (
|
|
ErrorRecoveryService,
|
|
)
|
|
from cleveragents.domain.models.core.error_recovery import (
|
|
ErrorCategory,
|
|
classify_error,
|
|
get_recovery_hints,
|
|
)
|
|
from cleveragents.domain.models.core.plan import (
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
)
|
|
|
|
|
|
_PLAN_ID = "01BENCHERR0000000000001"
|
|
|
|
|
|
def _make_plan() -> MagicMock:
|
|
plan = MagicMock()
|
|
plan.identity.plan_id = _PLAN_ID
|
|
plan.phase = PlanPhase.EXECUTE
|
|
plan.processing_state = ProcessingState.ERRORED
|
|
plan.is_terminal = False
|
|
plan.error_message = None
|
|
plan.error_details = None
|
|
plan.timestamps = PlanTimestamps()
|
|
return plan
|
|
|
|
|
|
def _make_service(plan: MagicMock) -> ErrorRecoveryService:
|
|
lifecycle = MagicMock()
|
|
lifecycle.get_plan.return_value = plan
|
|
lifecycle._commit_plan = MagicMock()
|
|
return ErrorRecoveryService(
|
|
lifecycle_service=lifecycle,
|
|
auto_retry_threshold=0.0,
|
|
max_retries=3,
|
|
)
|
|
|
|
|
|
class ClassifySuite:
|
|
"""Benchmark error classification."""
|
|
|
|
def time_classify_transient(self) -> None:
|
|
classify_error("Connection timed out after 30s")
|
|
|
|
def time_classify_validation(self) -> None:
|
|
classify_error("Schema validation failed for field X")
|
|
|
|
def time_classify_unknown(self) -> None:
|
|
classify_error("Something completely novel happened")
|
|
|
|
def time_classify_with_exception_type(self) -> None:
|
|
classify_error("failed", "RateLimitError")
|
|
|
|
|
|
class HintSuite:
|
|
"""Benchmark recovery hint generation."""
|
|
|
|
def time_hints_transient(self) -> None:
|
|
get_recovery_hints(ErrorCategory.TRANSIENT, _PLAN_ID)
|
|
|
|
def time_hints_validation(self) -> None:
|
|
get_recovery_hints(ErrorCategory.VALIDATION, _PLAN_ID)
|
|
|
|
def time_hints_merge_conflict(self) -> None:
|
|
get_recovery_hints(ErrorCategory.MERGE_CONFLICT, _PLAN_ID)
|
|
|
|
|
|
class RecordSuite:
|
|
"""Benchmark error recording with mock service."""
|
|
|
|
def setup(self) -> None:
|
|
self.plan = _make_plan()
|
|
self.service = _make_service(self.plan)
|
|
self._counter = 0
|
|
|
|
def time_record_error(self) -> None:
|
|
self._counter += 1
|
|
self.service.record_error(
|
|
plan_id=_PLAN_ID,
|
|
phase="execute",
|
|
message=f"Timeout #{self._counter}",
|
|
)
|
|
|
|
def time_format_output_plain(self) -> None:
|
|
self.service.format_error_output(_PLAN_ID, fmt="plain")
|
|
|
|
def time_format_output_json(self) -> None:
|
|
self.service.format_error_output(_PLAN_ID, fmt="json")
|