Coverage for src / lexigram / contracts / infra / resilience / models.py: 0%
34 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
« prev ^ index » next coverage.py v7.13.5, created at 2026-08-19 05:41 +0800
1"""Shared configuration models for resilience patterns.
3These live in contracts so ANY package (lexigram-events, lexigram-web, etc.)
4can reference retry/circuit breaker configs without depending on
5the lexigram-resilience implementation package.
6"""
8from __future__ import annotations
10from dataclasses import dataclass, field
11from typing import TYPE_CHECKING, Any, Literal
13if TYPE_CHECKING:
14 from collections.abc import Callable
17@dataclass(frozen=True)
18class RetryConfig:
19 """Configuration for retry policy."""
21 # Total attempts including the initial try. max_attempts=3 means one
22 # initial call plus up to 2 retries — NOT 3 retries on top of the
23 # first try. Minimum useful value is 1 (no retries).
24 max_attempts: int = 3
25 base_delay: float = 1.0
26 max_delay: float = 60.0
27 backoff_factor: float = 2.0
28 jitter: bool | float = True
29 retry_on: tuple[type[Exception], ...] = field(default=(Exception,))
30 retry_if: Callable[[Exception], bool] | None = None
31 on_retry: Callable[[int, Exception | None], None] | None = None
32 retry_on_result: Callable[[Any], bool] | None = None
33 abort_on: tuple[type[Exception], ...] = field(default=())
34 abort_if: Callable[[Any], bool] | None = None
35 retry_sync: bool = False
36 # Retry only requests whose HTTP method is idempotent (GET/HEAD/OPTIONS).
37 # Defaults to True (safe by default).
38 idempotent_methods_only: bool = True
41@dataclass(frozen=True)
42class CircuitBreakerConfig:
43 """Configuration for circuit breaker.
45 Attributes:
46 failure_threshold: Number of failures before opening the circuit.
47 recovery_timeout: Seconds in open state before attempting half-open.
48 expected_exception: Exception types that count as failures.
49 success_threshold: Consecutive successes required to close from half-open.
50 timeout: Per-call timeout in seconds.
51 name: Human-readable identifier for this breaker instance.
52 sliding_window_seconds: Window for computing the failure rate.
53 failure_rate_threshold: Fraction of calls that must fail to trip.
54 backend: State store backend for distributed circuit breaker coordination.
55 ``"memory"`` (default) — in-process state, no coordination.
56 ``"redis"`` — uses a :class:`~lexigram.contracts.cache.CacheBackendProtocol`
57 resolved from the DI container.
58 ``"consul"`` — uses Consul KV for cross-datacenter coordination.
59 """
61 failure_threshold: int = 5
62 recovery_timeout: float = 60.0
63 expected_exception: tuple[type[Exception], ...] = field(default=(Exception,))
64 success_threshold: int = 3
65 timeout: float = 30.0
66 name: str = ""
67 sliding_window_seconds: float = 60.0
68 failure_rate_threshold: float = 0.5
69 backend: Literal["memory", "redis", "consul"] = "memory"
72@dataclass(frozen=True)
73class TimeoutConfig:
74 """Configuration for timeout handling."""
76 timeout: float = 30.0
77 timeout_message: str = "Operation timed out"
80__all__ = [
81 "CircuitBreakerConfig",
82 "RetryConfig",
83 "TimeoutConfig",
84]