test(core): add ASV performance benchmarks for core module

Added three new ASV benchmark suites for the core module:

1. core_circuit_breaker_bench.py - Benchmarks for CircuitBreaker class:
   - Closed state call overhead
   - Open state fast-fail latency
   - State transition overhead (closed→open, open→half-open, half-open→closed)
   - Async operations
   - Initialization overhead

2. core_retry_patterns_bench.py - Benchmarks for retry decorators:
   - Decorator construction overhead for exponential backoff, jitter, timeout, and result-based retry
   - Happy-path invocation overhead
   - Async decorator operations
   - Decorator usage with function arguments
   - Factory function performance
   - Various configuration scenarios

3. core_retry_service_patterns_bench.py - Benchmarks for service-level retry:
   - retry_service_operation decorator construction and invocation
   - retry_auto_debug decorator performance
   - RetryContext operations
   - is_read_only_plan_operation utility
   - Different wait strategies (exponential, linear, fixed, jitter)

These benchmarks ensure latency regressions in core resilience primitives are caught early.

ISSUES CLOSED: #1921
This commit is contained in:
2026-05-03 01:14:59 +00:00
committed by Forgejo
parent 3f8f8eb0bf
commit ffd83e8712
3 changed files with 868 additions and 0 deletions
+252
View File
@@ -0,0 +1,252 @@
"""ASV benchmarks for circuit breaker pattern.
Measures the overhead of circuit breaker state management,
state transitions, and fast-fail latency in open state.
"""
from __future__ import annotations
import asyncio
import time
from typing import Any
from cleveragents.core.circuit_breaker import (
CircuitBreaker,
CircuitBreakerOpen,
CircuitBreakerState,
)
class CircuitBreakerClosedStateBench:
"""Benchmark circuit breaker overhead in closed state."""
timeout = 60
def setup(self) -> None:
"""Set up circuit breaker in closed state."""
self.breaker = CircuitBreaker(
failure_threshold=5,
recovery_timeout=60.0,
expected_exception=Exception,
half_open_max_successes=2,
cooldown_seconds=30.0,
name="test_service",
)
def time_call_success(self) -> None:
"""Benchmark successful call overhead in closed state."""
def dummy_func() -> str:
return "success"
self.breaker.call(dummy_func)
def time_call_with_args(self) -> None:
"""Benchmark call with arguments in closed state."""
def dummy_func(x: int, y: str) -> str:
return f"{x}:{y}"
self.breaker.call(dummy_func, 42, "test")
def time_state_check(self) -> None:
"""Benchmark state property access."""
_ = self.breaker.state
def time_failure_count_check(self) -> None:
"""Benchmark failure count property access."""
_ = self.breaker.failure_count
class CircuitBreakerOpenStateBench:
"""Benchmark circuit breaker fast-fail latency in open state."""
timeout = 60
def setup(self) -> None:
"""Set up circuit breaker in open state."""
self.breaker = CircuitBreaker(
failure_threshold=2,
recovery_timeout=60.0,
expected_exception=Exception,
half_open_max_successes=2,
cooldown_seconds=30.0,
name="test_service",
)
# Force the breaker into open state
for _ in range(3):
try:
self.breaker.call(lambda: 1 / 0) # Raise ZeroDivisionError
except ZeroDivisionError:
pass
def time_fast_fail(self) -> None:
"""Benchmark fast-fail latency when circuit is open."""
try:
self.breaker.call(lambda: "should not execute")
except CircuitBreakerOpen:
pass
def time_open_state_check(self) -> None:
"""Benchmark state check when open."""
assert self.breaker.state == CircuitBreakerState.OPEN
class CircuitBreakerStateTransitionBench:
"""Benchmark state transition overhead."""
timeout = 60
def setup(self) -> None:
"""Set up circuit breaker."""
self.breaker = CircuitBreaker(
failure_threshold=2,
recovery_timeout=0.1, # Short timeout for testing
expected_exception=Exception,
half_open_max_successes=1,
cooldown_seconds=0.05,
name="test_service",
)
def time_closed_to_open_transition(self) -> None:
"""Benchmark transition from closed to open state."""
breaker = CircuitBreaker(
failure_threshold=2,
recovery_timeout=60.0,
expected_exception=Exception,
half_open_max_successes=1,
cooldown_seconds=30.0,
name="test_service",
)
# Trigger failures to transition to open
for _ in range(3):
try:
breaker.call(lambda: 1 / 0)
except ZeroDivisionError:
pass
def time_open_to_half_open_transition(self) -> None:
"""Benchmark transition from open to half-open state."""
breaker = CircuitBreaker(
failure_threshold=2,
recovery_timeout=0.05, # Very short timeout
expected_exception=Exception,
half_open_max_successes=1,
cooldown_seconds=0.01,
name="test_service",
)
# Force to open
for _ in range(3):
try:
breaker.call(lambda: 1 / 0)
except ZeroDivisionError:
pass
# Wait for recovery timeout
time.sleep(0.1)
# Attempt call to trigger half-open transition
try:
breaker.call(lambda: "success")
except CircuitBreakerOpen:
pass
def time_half_open_to_closed_transition(self) -> None:
"""Benchmark transition from half-open to closed state."""
breaker = CircuitBreaker(
failure_threshold=2,
recovery_timeout=0.05,
expected_exception=Exception,
half_open_max_successes=1,
cooldown_seconds=0.01,
name="test_service",
)
# Force to open
for _ in range(3):
try:
breaker.call(lambda: 1 / 0)
except ZeroDivisionError:
pass
# Wait for recovery timeout
time.sleep(0.1)
# Successful call in half-open should close
breaker.call(lambda: "success")
class CircuitBreakerAsyncBench:
"""Benchmark async circuit breaker operations."""
timeout = 60
def setup(self) -> None:
"""Set up async circuit breaker."""
self.breaker = CircuitBreaker(
failure_threshold=5,
recovery_timeout=60.0,
expected_exception=Exception,
half_open_max_successes=2,
cooldown_seconds=30.0,
name="test_service",
)
def time_async_call_success(self) -> None:
"""Benchmark successful async call overhead."""
async def dummy_async() -> str:
return "success"
asyncio.run(self.breaker.async_call(dummy_async))
def time_async_call_with_args(self) -> None:
"""Benchmark async call with arguments."""
async def dummy_async(x: int, y: str) -> str:
return f"{x}:{y}"
asyncio.run(self.breaker.async_call(dummy_async, 42, "test"))
def time_async_fast_fail(self) -> None:
"""Benchmark async fast-fail when open."""
# First set up breaker in open state
breaker = CircuitBreaker(
failure_threshold=2,
recovery_timeout=60.0,
expected_exception=Exception,
half_open_max_successes=2,
cooldown_seconds=30.0,
name="test_service",
)
# Force to open
for _ in range(3):
try:
breaker.call(lambda: 1 / 0)
except ZeroDivisionError:
pass
async def dummy_async() -> str:
return "should not execute"
try:
asyncio.run(breaker.async_call(dummy_async))
except CircuitBreakerOpen:
pass
class CircuitBreakerInitializationBench:
"""Benchmark circuit breaker initialization overhead."""
timeout = 60
def time_default_initialization(self) -> None:
"""Benchmark default initialization."""
CircuitBreaker()
def time_custom_initialization(self) -> None:
"""Benchmark initialization with custom parameters."""
CircuitBreaker(
failure_threshold=10,
recovery_timeout=120.0,
expected_exception=ValueError,
half_open_max_successes=3,
cooldown_seconds=45.0,
name="custom_service",
)
def time_multiple_breakers(self) -> None:
"""Benchmark creating multiple breakers."""
for i in range(10):
CircuitBreaker(name=f"service_{i}")