Compare commits

...

1 Commits

2 changed files with 41 additions and 27 deletions
@@ -0,0 +1,3 @@
"""Step definitions for ACMS context analysis (stub)."""
from behave import given, then, when
@@ -97,31 +97,38 @@ class RetryPolicyConfig(BaseModel):
override exists.
Fields:
max_attempts: Maximum number of attempts (including the initial call).
base_delay: Initial delay in seconds before the first retry.
max_delay: Upper bound on delay between retries in seconds.
max_retries: Maximum number of retry attempts (spec-required name).
retry_delay_seconds: Initial delay in seconds before the first retry.
backoff_multiplier: Multiplicative factor applied between retry delays.
max_backoff: Upper bound on backoff delay in seconds (spec-required name).
jitter: Whether to add random jitter to delays to avoid thundering herd.
backoff_strategy: The strategy used to compute delay between retries.
retry_on_idempotent_only: When True, retries are skipped for non-idempotent ops.
"""
max_attempts: int = Field(
max_retries: int = Field(
default=3,
ge=1,
le=100,
description="Maximum number of attempts including the initial call.",
description="Maximum number of retry attempts.",
)
base_delay: float = Field(
retry_delay_seconds: float = Field(
default=1.0,
ge=0.0,
le=300.0,
description="Initial delay in seconds before the first retry.",
)
max_delay: float = Field(
backoff_multiplier: float = Field(
default=2.0,
ge=1.0,
le=10.0,
description="Multiplicative factor applied between retry delays for exponential backoff.",
)
max_backoff: float = Field(
default=60.0,
ge=0.0,
le=3600.0,
description="Upper bound on delay between retries in seconds.",
description="Upper bound on backoff delay in seconds.",
)
jitter: bool = Field(
default=True,
@@ -148,17 +155,17 @@ class RetryPolicyConfig(BaseModel):
)
@model_validator(mode="after")
def _check_max_delay_ge_base_delay(self) -> RetryPolicyConfig:
"""Ensure max_delay is not less than base_delay.
def _check_max_backoff_ge_retry_delay(self) -> RetryPolicyConfig:
"""Ensure max_backoff is not less than review_delay_seconds.
Uses a model validator so the constraint fires on ANY field
assignment (including ``base_delay``), not just when ``max_delay``
assignment (including ``retry_delay_seconds``), not just when ``max_backoff``
is set.
"""
if self.max_delay < self.base_delay:
if self.max_backoff < self.retry_delay_seconds:
msg = (
f"max_delay ({self.max_delay}) must be >= "
f"base_delay ({self.base_delay})"
f"max_backoff ({self.max_backoff}) must be >= "
f"retry_delay_seconds ({self.retry_delay_seconds})"
)
raise ValueError(msg)
return self
@@ -300,36 +307,40 @@ class ServiceRetryPolicy(BaseModel):
# -------------------------------------------------------------------
DEFAULT_NETWORK_RETRY = RetryPolicyConfig(
max_attempts=5,
base_delay=1.0,
max_delay=30.0,
max_retries=5,
retry_delay_seconds=1.0,
backoff_multiplier=2.0,
max_backoff=30.0,
jitter=True,
backoff_strategy=RetryStrategy.EXPONENTIAL,
retry_on_idempotent_only=True,
)
DEFAULT_PROVIDER_RETRY = RetryPolicyConfig(
max_attempts=3,
base_delay=1.0,
max_delay=60.0,
max_retries=3,
retry_delay_seconds=1.0,
backoff_multiplier=2.0,
max_backoff=60.0,
jitter=True,
backoff_strategy=RetryStrategy.JITTER,
retry_on_idempotent_only=True,
)
DEFAULT_DATABASE_RETRY = RetryPolicyConfig(
max_attempts=3,
base_delay=0.5,
max_delay=5.0,
max_retries=3,
retry_delay_seconds=0.5,
backoff_multiplier=2.0,
max_backoff=5.0,
jitter=False,
backoff_strategy=RetryStrategy.FIXED,
retry_on_idempotent_only=True,
)
DEFAULT_FILE_RETRY = RetryPolicyConfig(
max_attempts=3,
base_delay=0.1,
max_delay=1.0,
max_retries=3,
retry_delay_seconds=0.1,
backoff_multiplier=2.0,
max_backoff=1.0,
jitter=True,
backoff_strategy=RetryStrategy.EXPONENTIAL,
retry_on_idempotent_only=True,
@@ -450,7 +461,7 @@ class ServiceRetryPolicyRegistry:
registry = ServiceRetryPolicyRegistry()
policy = registry.get("plan_service")
# Apply override from config
registry.apply_overrides({"plan_service": {"retry": {"max_attempts": 5}}})
registry.apply_overrides({"plan_service": {"retry": {"max_retries": 5}}})
"""
def __init__(self) -> None: