File size: 7,640 Bytes
2978bba
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# MorphGuard API Utilities

The `api_utils.py` module provides a sophisticated and resilient API client system with intelligent fallbacks for the MorphGuard application. This system is designed to handle network instability, server errors, and other common issues that can affect API communication.

## Key Features

- **Smart Request Queuing**: Prioritize critical requests to ensure they are processed first
- **Comprehensive Error Handling**: Detailed error diagnostics and classification
- **Intelligent Caching**: Stale-while-revalidate pattern for improved performance and resilience
- **Circuit Breaker Pattern**: Prevent cascading failures by failing fast when services are down
- **Extended Metrics and Monitoring**: Track performance and reliability metrics
- **Context-Aware Retry Strategies**: Intelligent backoff and jitter for optimal retry behavior

## Core Components

### 1. CircuitBreaker

Implements the circuit breaker pattern with three states:
- **CLOSED**: Normal operation, requests are sent normally
- **OPEN**: Circuit is tripped, all requests fail fast
- **HALF-OPEN**: Testing if the service has recovered

```python
# Example usage
cb = CircuitBreaker(failure_threshold=5, recovery_timeout=30.0)

if cb.allow_request():
    try:
        # Make request
        result = make_api_call()
        cb.record_success()
        return result
    except Exception as e:
        cb.record_failure()
        raise
else:
    # Circuit is open, use fallback or fail fast
    return fallback_data
```

### 2. StaleWhileRevalidateCache

Enhanced cache with stale-while-revalidate strategy:
- Returns stale data immediately while asynchronously fetching fresh data
- Reduces perceived latency and improves availability
- Supports custom TTL for different cache entries

```python
# Example usage
cache = StaleWhileRevalidateCache(cache_dir=".cache", stale_timeout=300.0)

# Define refresh callback
def refresh_data():
    return fetch_fresh_data_from_api()

# Get data from cache, refresh in background if stale
data, is_fresh = cache.get("cache_key", refresh_callback=refresh_data)

# Use data immediately, even if stale
process_data(data)
```

### 3. APIUtils

The main API client with comprehensive resilience features:
- Configurable timeout, retries, and caching
- Circuit breaker pattern integration
- Priority-based request handling
- Detailed metrics and telemetry

```python
# Example usage
api = get_api_utils({
    "base_url": "https://api.example.com",
    "timeout": 30.0,
    "retries": 3,
    "cache_enabled": True
})

# Make a GET request with fallback
try:
    result = api.get("/users", {"limit": 10}, {
        "fallback": {"users": []},
        "priority": "high"
    })
    process_users(result)
except Exception as e:
    handle_error(e)
```

### 4. Decorator Functions

Utility decorators for applying resilience patterns to functions:

#### @retry

Automatically retry functions on failure:

```python
@retry(retries=3, retry_delay=1.0, fallback=default_value)
def fetch_user_data(user_id):
    # This function will be retried up to 3 times if it fails
    return api_call(f"/users/{user_id}")
```

#### @circuit_breaker

Apply circuit breaker pattern to functions:

```python
@circuit_breaker(failure_threshold=5, recovery_timeout=30.0, fallback=empty_list)
def search_products(query):
    # This function will fail fast if it fails 5 times in a row
    return api_call("/search", {"q": query})
```

#### @cacheable

Cache function results with TTL:

```python
@cacheable(ttl=60.0)  # Cache for 1 minute
def get_product_categories():
    # Results will be cached for 60 seconds
    return api_call("/categories")
```

## Usage Example

Here's a complete example of using the API utilities:

```python
from src.api_utils import get_api_utils, retry, circuit_breaker, cacheable

# Configure API utilities
api = get_api_utils({
    "base_url": "https://api.example.com",
    "timeout": 30.0,
    "retries": 3,
    "cache_enabled": True
})

# Make a simple GET request
users = api.get("/users")

# POST request with error handling
try:
    new_user = api.post("/users", {
        "name": "John Doe",
        "email": "john@example.com"
    }, {
        "critical": True,  # High priority
        "timeout": 60.0    # Longer timeout
    })
    print(f"Created user: {new_user}")
except Exception as e:
    print(f"Failed to create user: {e}")

# Function with retry decorator
@retry(retries=3, retry_delay=1.0)
def fetch_user_data(user_id):
    return api.get(f"/users/{user_id}")

# Function with circuit breaker
@circuit_breaker(failure_threshold=5, recovery_timeout=30.0)
def search_products(query):
    return api.get("/search", {"q": query})

# Function with caching
@cacheable(ttl=60.0)
def get_product_categories():
    return api.get("/categories")

# Get API metrics
metrics = api.get_metrics()
print(f"API metrics: {metrics}")
```

## Configuration Options

The API utilities can be configured with the following options:

| Option | Description | Default |
|--------|-------------|---------|
| `base_url` | Base URL for the API | `"http://localhost:5000/api"` |
| `timeout` | Request timeout in seconds | `30.0` |
| `retries` | Number of retries for failed requests | `3` |
| `retry_delay` | Base delay between retries in seconds | `1.0` |
| `retry_status_codes` | HTTP status codes to retry | `[408, 429, 500, 502, 503, 504]` |
| `cache_enabled` | Whether to enable caching | `True` |
| `cache_dir` | Directory to store cache files | `".mg_api_cache"` |
| `max_cache_entries` | Maximum number of in-memory cache entries | `100` |
| `stale_timeout` | Time in seconds before data is considered stale | `300.0` |
| `revalidation_timeout` | Maximum age for using stale data | `600.0` |
| `circuit_breaker_enabled` | Whether to enable circuit breaker | `True` |
| `failure_threshold` | Number of failures before circuit opens | `5` |
| `recovery_timeout` | Time in seconds before circuit half-opens | `30.0` |
| `priority_queue_enabled` | Whether to enable priority-based request queue | `True` |
| `max_queue_size` | Maximum size of request queue | `100` |
| `debug` | Whether to enable debug logging | `False` |

## Error Handling

The API utilities integrate with MorphGuard's error handling system to provide detailed error information:

```python
try:
    result = api.get("/users/123")
except APIError as e:
    if e.code == ErrorCode.NOT_FOUND:
        print("User not found")
    elif e.code == ErrorCode.UNAUTHORIZED:
        print("Authentication required")
    else:
        print(f"API error: {e.message}")
except NetworkError as e:
    print(f"Network error: {e.message}")
except Exception as e:
    print(f"Unexpected error: {e}")
```

## Metrics and Monitoring

The API utilities track various metrics for monitoring:

```python
metrics = api.get_metrics()
print(f"Total requests: {metrics['total_requests']}")
print(f"Successful requests: {metrics['successful_requests']}")
print(f"Failed requests: {metrics['failed_requests']}")
print(f"Cached responses: {metrics['cached_responses']}")
print(f"Stale responses: {metrics['stale_responses']}")
print(f"Retries: {metrics['retries']}")
print(f"Circuit breaks: {metrics['circuit_breaks']}")
print(f"Average response time: {metrics['total_time'] / max(1, metrics['total_requests']):.2f}s")
```

## Testing

The API utilities include comprehensive unit tests to ensure reliability:

```bash
# Run all tests
python -m unittest tests.test_api_utils

# Run specific test class
python -m unittest tests.test_api_utils.TestCircuitBreaker

# Run specific test method
python -m unittest tests.test_api_utils.TestAPIUtils.test_circuit_breaker
```