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

1"""Throttle-resilient boto3 session facade for the live-validation harness. 

2 

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. 

10 

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. 

18 

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""" 

23 

24from __future__ import annotations 

25 

26from typing import Any 

27 

28import boto3 

29from botocore.config import Config 

30 

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 

37 

38_ADAPTIVE_RETRY_CONFIG = Config( 

39 retries={ 

40 "mode": "adaptive", 

41 "max_attempts": _RETRY_MAX_ATTEMPTS, 

42 } 

43) 

44 

45 

46class ThrottleResilientSession: 

47 """Delegate to a real ``boto3.Session``, injecting adaptive retries. 

48 

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 """ 

54 

55 def __init__(self, session: Any | None = None) -> None: 

56 self._session = session if session is not None else boto3.Session() 

57 

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) 

65 

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) 

68 

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) 

71 

72 def __getattr__(self, name: str) -> Any: 

73 return getattr(self._session, name)