Coverage for scripts / live_release_validation / aws_session.py: 100.00%
20 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
1"""Throttle-resilient boto3 session facade for the live-validation harness.
3Every inventory scanner fans out across all enabled Regions and issues one
4metadata read per resource (``ListTagsForResource`` on each CloudWatch log
5group, per-role IAM tag lookups, and so on). Under botocore's default retry
6budget a Regional TPS squeeze surfaces as a hard failure — a real
7``final-inventory`` run died with ``ThrottlingException … reached max
8retries: 4`` while scanning 17 Regions even though the account state was
9clean.
11The facade below gives every client the harness creates botocore's
12``adaptive`` retry mode: client-side rate limiting that paces request bursts
13before they trip the service, plus a much deeper retry budget for the
14throttling errors that still get through. Correctness is unchanged — after
15the budget is exhausted the original ``ClientError`` still propagates, so
16every fail-closed path behaves exactly as before; the run just no longer
17fails on a transient rate spike that a bounded wait absorbs.
19Callers that pass their own ``config`` keep every field they set — the
20retry defaults only fill the gaps (``botocore.config.Config.merge`` gives
21the *other* config precedence on conflicts).
22"""
24from __future__ import annotations
26from typing import Any
28import boto3
29from botocore.config import Config
31# ``adaptive`` includes everything ``standard`` retries (throttling errors,
32# transient 5xx, timeouts) and adds client-side rate limiting. The attempt
33# budget is deliberately deep: with exponential backoff it absorbs a
34# sustained Regional throttle window, while a genuine outage still fails
35# within a bounded, observable number of attempts.
36_RETRY_MAX_ATTEMPTS = 12
38_ADAPTIVE_RETRY_CONFIG = Config(
39 retries={
40 "mode": "adaptive",
41 "max_attempts": _RETRY_MAX_ATTEMPTS,
42 }
43)
46class ThrottleResilientSession:
47 """Delegate to a real ``boto3.Session``, injecting adaptive retries.
49 Only ``client`` and ``resource`` construction is intercepted; every other
50 attribute (``get_credentials``, ``get_available_regions``,
51 ``get_partition_for_region``, ``region_name``, …) resolves on the wrapped
52 session unchanged.
53 """
55 def __init__(self, session: Any | None = None) -> None:
56 self._session = session if session is not None else boto3.Session()
58 @staticmethod
59 def _merged_config(config: Any | None) -> Any:
60 if config is None:
61 return _ADAPTIVE_RETRY_CONFIG
62 # ``merge`` returns a new Config whose fields prefer ``config`` —
63 # a caller that sets its own ``retries`` wins over the default.
64 return _ADAPTIVE_RETRY_CONFIG.merge(config)
66 def client(self, *args: Any, config: Any | None = None, **kwargs: Any) -> Any:
67 return self._session.client(*args, config=self._merged_config(config), **kwargs)
69 def resource(self, *args: Any, config: Any | None = None, **kwargs: Any) -> Any:
70 return self._session.resource(*args, config=self._merged_config(config), **kwargs)
72 def __getattr__(self, name: str) -> Any:
73 return getattr(self._session, name)