snikhilesh commited on
Commit
62a32d3
·
verified ·
1 Parent(s): 85c570a

Deploy load_test_monitoring.py to backend/ directory

Browse files
Files changed (1) hide show
  1. backend/load_test_monitoring.py +380 -0
backend/load_test_monitoring.py ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Load Testing Script for Medical AI Platform Monitoring Infrastructure
3
+ Tests system performance, monitoring accuracy, and error handling under stress
4
+
5
+ Requirements:
6
+ - Tests monitoring middleware performance impact
7
+ - Validates cache effectiveness under load
8
+ - Verifies error rate tracking accuracy
9
+ - Confirms alert system responsiveness
10
+ - Measures latency tracking precision
11
+ """
12
+
13
+ import asyncio
14
+ import aiohttp
15
+ import time
16
+ import json
17
+ from typing import List, Dict, Any
18
+ from dataclasses import dataclass
19
+ from datetime import datetime
20
+ import statistics
21
+
22
+ @dataclass
23
+ class LoadTestResult:
24
+ """Result from a single request"""
25
+ success: bool
26
+ latency_ms: float
27
+ status_code: int
28
+ endpoint: str
29
+ timestamp: float
30
+ error_message: str = None
31
+
32
+ class MonitoringLoadTester:
33
+ """Load tester for monitoring infrastructure"""
34
+
35
+ def __init__(self, base_url: str = "http://localhost:7860"):
36
+ self.base_url = base_url
37
+ self.results: List[LoadTestResult] = []
38
+
39
+ async def make_request(
40
+ self,
41
+ session: aiohttp.ClientSession,
42
+ endpoint: str,
43
+ method: str = "GET",
44
+ data: Dict = None
45
+ ) -> LoadTestResult:
46
+ """Make a single HTTP request and measure performance"""
47
+ start_time = time.time()
48
+ url = f"{self.base_url}{endpoint}"
49
+
50
+ try:
51
+ if method == "GET":
52
+ async with session.get(url) as response:
53
+ await response.text()
54
+ latency_ms = (time.time() - start_time) * 1000
55
+ return LoadTestResult(
56
+ success=response.status == 200,
57
+ latency_ms=latency_ms,
58
+ status_code=response.status,
59
+ endpoint=endpoint,
60
+ timestamp=time.time()
61
+ )
62
+ elif method == "POST":
63
+ async with session.post(url, json=data) as response:
64
+ await response.text()
65
+ latency_ms = (time.time() - start_time) * 1000
66
+ return LoadTestResult(
67
+ success=response.status == 200,
68
+ latency_ms=latency_ms,
69
+ status_code=response.status,
70
+ endpoint=endpoint,
71
+ timestamp=time.time()
72
+ )
73
+ except Exception as e:
74
+ latency_ms = (time.time() - start_time) * 1000
75
+ return LoadTestResult(
76
+ success=False,
77
+ latency_ms=latency_ms,
78
+ status_code=0,
79
+ endpoint=endpoint,
80
+ timestamp=time.time(),
81
+ error_message=str(e)
82
+ )
83
+
84
+ async def run_concurrent_requests(
85
+ self,
86
+ endpoint: str,
87
+ num_requests: int,
88
+ concurrent_workers: int = 10
89
+ ):
90
+ """Run multiple concurrent requests to an endpoint"""
91
+ print(f"\n{'='*60}")
92
+ print(f"Testing: {endpoint}")
93
+ print(f"Requests: {num_requests}, Concurrent Workers: {concurrent_workers}")
94
+ print(f"{'='*60}")
95
+
96
+ async with aiohttp.ClientSession() as session:
97
+ tasks = []
98
+ for i in range(num_requests):
99
+ task = self.make_request(session, endpoint)
100
+ tasks.append(task)
101
+
102
+ # Limit concurrency
103
+ if len(tasks) >= concurrent_workers or i == num_requests - 1:
104
+ results = await asyncio.gather(*tasks)
105
+ self.results.extend(results)
106
+ tasks = []
107
+
108
+ # Small delay to avoid overwhelming the server
109
+ await asyncio.sleep(0.1)
110
+
111
+ # Analyze results for this endpoint
112
+ self.analyze_endpoint_results(endpoint)
113
+
114
+ def analyze_endpoint_results(self, endpoint: str):
115
+ """Analyze results for a specific endpoint"""
116
+ endpoint_results = [r for r in self.results if r.endpoint == endpoint]
117
+
118
+ if not endpoint_results:
119
+ print(f"No results for {endpoint}")
120
+ return
121
+
122
+ successes = [r for r in endpoint_results if r.success]
123
+ failures = [r for r in endpoint_results if not r.success]
124
+
125
+ latencies = [r.latency_ms for r in successes]
126
+
127
+ print(f"\n📊 Results for {endpoint}:")
128
+ print(f" Total Requests: {len(endpoint_results)}")
129
+ print(f" ✓ Successful: {len(successes)} ({len(successes)/len(endpoint_results)*100:.1f}%)")
130
+ print(f" ✗ Failed: {len(failures)} ({len(failures)/len(endpoint_results)*100:.1f}%)")
131
+
132
+ if latencies:
133
+ print(f"\n⏱ Latency Statistics:")
134
+ print(f" Mean: {statistics.mean(latencies):.2f} ms")
135
+ print(f" Median: {statistics.median(latencies):.2f} ms")
136
+ print(f" Min: {min(latencies):.2f} ms")
137
+ print(f" Max: {max(latencies):.2f} ms")
138
+ print(f" Std Dev: {statistics.stdev(latencies) if len(latencies) > 1 else 0:.2f} ms")
139
+
140
+ if len(latencies) >= 10:
141
+ sorted_latencies = sorted(latencies)
142
+ p95_index = int(len(sorted_latencies) * 0.95)
143
+ p99_index = int(len(sorted_latencies) * 0.99)
144
+ print(f" P95: {sorted_latencies[p95_index]:.2f} ms")
145
+ print(f" P99: {sorted_latencies[p99_index]:.2f} ms")
146
+
147
+ if failures:
148
+ print(f"\n⚠ Sample Errors:")
149
+ for failure in failures[:3]:
150
+ print(f" Status: {failure.status_code}, Error: {failure.error_message}")
151
+
152
+ async def test_health_endpoint(self, num_requests: int = 100):
153
+ """Test health check endpoint"""
154
+ await self.run_concurrent_requests("/health", num_requests, concurrent_workers=20)
155
+
156
+ async def test_dashboard_endpoint(self, num_requests: int = 50):
157
+ """Test dashboard endpoint (more intensive)"""
158
+ await self.run_concurrent_requests("/health/dashboard", num_requests, concurrent_workers=10)
159
+
160
+ async def test_admin_endpoints(self):
161
+ """Test admin endpoints"""
162
+ # Test cache statistics
163
+ await self.run_concurrent_requests("/admin/cache/statistics", num_requests=30, concurrent_workers=5)
164
+
165
+ # Test metrics
166
+ await self.run_concurrent_requests("/admin/metrics", num_requests=30, concurrent_workers=5)
167
+
168
+ async def verify_monitoring_accuracy(self):
169
+ """Verify that monitoring system accurately tracks requests"""
170
+ print(f"\n{'='*60}")
171
+ print("VERIFYING MONITORING ACCURACY")
172
+ print(f"{'='*60}")
173
+
174
+ # Get initial dashboard state
175
+ async with aiohttp.ClientSession() as session:
176
+ async with session.get(f"{self.base_url}/health/dashboard") as response:
177
+ initial_data = await response.json()
178
+ initial_requests = initial_data['system']['total_requests']
179
+ print(f"Initial request count: {initial_requests}")
180
+
181
+ # Make exactly 50 requests
182
+ print(f"\nMaking 50 test requests...")
183
+ await self.run_concurrent_requests("/health", num_requests=50, concurrent_workers=10)
184
+
185
+ # Wait for monitoring to update
186
+ await asyncio.sleep(2)
187
+
188
+ # Check final dashboard state
189
+ async with aiohttp.ClientSession() as session:
190
+ async with session.get(f"{self.base_url}/health/dashboard") as response:
191
+ final_data = await response.json()
192
+ final_requests = final_data['system']['total_requests']
193
+ print(f"Final request count: {final_requests}")
194
+
195
+ actual_increase = final_requests - initial_requests
196
+ expected_increase = 50
197
+
198
+ print(f"\n📈 Monitoring Accuracy:")
199
+ print(f" Expected increase: {expected_increase}")
200
+ print(f" Actual increase: {actual_increase}")
201
+ print(f" Accuracy: {(actual_increase/expected_increase*100):.1f}%")
202
+
203
+ if actual_increase >= expected_increase * 0.95:
204
+ print(f" ✓ Monitoring is accurately tracking requests")
205
+ else:
206
+ print(f" ⚠ Monitoring may have tracking issues")
207
+
208
+ async def test_cache_effectiveness(self):
209
+ """Test cache effectiveness under repeated requests"""
210
+ print(f"\n{'='*60}")
211
+ print("TESTING CACHE EFFECTIVENESS")
212
+ print(f"{'='*60}")
213
+
214
+ # Get initial cache stats
215
+ async with aiohttp.ClientSession() as session:
216
+ async with session.get(f"{self.base_url}/health/dashboard") as response:
217
+ initial_data = await response.json()
218
+ initial_hits = initial_data['cache']['hits']
219
+ initial_misses = initial_data['cache']['misses']
220
+ initial_hit_rate = initial_data['cache']['hit_rate']
221
+
222
+ print(f"Initial cache state:")
223
+ print(f" Hits: {initial_hits}")
224
+ print(f" Misses: {initial_misses}")
225
+ print(f" Hit Rate: {(initial_hit_rate * 100):.1f}%")
226
+
227
+ # Make repeated requests to same endpoint (should benefit from caching)
228
+ print(f"\nMaking 100 requests to test caching...")
229
+ await self.run_concurrent_requests("/health/dashboard", num_requests=100, concurrent_workers=10)
230
+
231
+ # Wait for cache to update
232
+ await asyncio.sleep(2)
233
+
234
+ # Check final cache stats
235
+ async with aiohttp.ClientSession() as session:
236
+ async with session.get(f"{self.base_url}/health/dashboard") as response:
237
+ final_data = await response.json()
238
+ final_hits = final_data['cache']['hits']
239
+ final_misses = final_data['cache']['misses']
240
+ final_hit_rate = final_data['cache']['hit_rate']
241
+
242
+ print(f"\nFinal cache state:")
243
+ print(f" Hits: {final_hits}")
244
+ print(f" Misses: {final_misses}")
245
+ print(f" Hit Rate: {(final_hit_rate * 100):.1f}%")
246
+
247
+ print(f"\n📊 Cache Performance:")
248
+ print(f" Hit increase: {final_hits - initial_hits}")
249
+ print(f" Miss increase: {final_misses - initial_misses}")
250
+ print(f" Current hit rate: {(final_hit_rate * 100):.1f}%")
251
+
252
+ async def stress_test(self, duration_seconds: int = 30):
253
+ """Run sustained load test"""
254
+ print(f"\n{'='*60}")
255
+ print(f"STRESS TEST - {duration_seconds} seconds")
256
+ print(f"{'='*60}")
257
+
258
+ start_time = time.time()
259
+ request_count = 0
260
+
261
+ async with aiohttp.ClientSession() as session:
262
+ while time.time() - start_time < duration_seconds:
263
+ tasks = []
264
+ for _ in range(10): # 10 concurrent requests per batch
265
+ task = self.make_request(session, "/health")
266
+ tasks.append(task)
267
+
268
+ results = await asyncio.gather(*tasks)
269
+ self.results.extend(results)
270
+ request_count += len(tasks)
271
+
272
+ await asyncio.sleep(0.5) # 0.5s between batches
273
+
274
+ total_time = time.time() - start_time
275
+ requests_per_second = request_count / total_time
276
+
277
+ print(f"\n⚡ Stress Test Results:")
278
+ print(f" Duration: {total_time:.2f} seconds")
279
+ print(f" Total Requests: {request_count}")
280
+ print(f" Requests/Second: {requests_per_second:.2f}")
281
+
282
+ # Analyze stress test results
283
+ recent_results = self.results[-request_count:]
284
+ successes = [r for r in recent_results if r.success]
285
+ print(f" Success Rate: {len(successes)/len(recent_results)*100:.1f}%")
286
+
287
+ def generate_report(self):
288
+ """Generate comprehensive test report"""
289
+ print(f"\n{'='*60}")
290
+ print("COMPREHENSIVE LOAD TEST REPORT")
291
+ print(f"{'='*60}")
292
+ print(f"Generated: {datetime.now().isoformat()}")
293
+
294
+ if not self.results:
295
+ print("No test results available")
296
+ return
297
+
298
+ total_requests = len(self.results)
299
+ successes = [r for r in self.results if r.success]
300
+ failures = [r for r in self.results if not r.success]
301
+
302
+ print(f"\n📊 Overall Statistics:")
303
+ print(f" Total Requests: {total_requests}")
304
+ print(f" ✓ Successful: {len(successes)} ({len(successes)/total_requests*100:.1f}%)")
305
+ print(f" ✗ Failed: {len(failures)} ({len(failures)/total_requests*100:.1f}%)")
306
+
307
+ all_latencies = [r.latency_ms for r in successes]
308
+ if all_latencies:
309
+ print(f"\n⏱ Global Latency Statistics:")
310
+ print(f" Mean: {statistics.mean(all_latencies):.2f} ms")
311
+ print(f" Median: {statistics.median(all_latencies):.2f} ms")
312
+ print(f" Min: {min(all_latencies):.2f} ms")
313
+ print(f" Max: {max(all_latencies):.2f} ms")
314
+
315
+ # Breakdown by endpoint
316
+ endpoints = set(r.endpoint for r in self.results)
317
+ print(f"\n📍 Breakdown by Endpoint:")
318
+ for endpoint in sorted(endpoints):
319
+ endpoint_results = [r for r in self.results if r.endpoint == endpoint]
320
+ endpoint_successes = [r for r in endpoint_results if r.success]
321
+ print(f" {endpoint}:")
322
+ print(f" Requests: {len(endpoint_results)}")
323
+ print(f" Success Rate: {len(endpoint_successes)/len(endpoint_results)*100:.1f}%")
324
+ if endpoint_successes:
325
+ latencies = [r.latency_ms for r in endpoint_successes]
326
+ print(f" Avg Latency: {statistics.mean(latencies):.2f} ms")
327
+
328
+ print(f"\n✅ Load testing complete!")
329
+
330
+ async def run_comprehensive_load_test(base_url: str = "http://localhost:7860"):
331
+ """Run comprehensive load testing suite"""
332
+ tester = MonitoringLoadTester(base_url)
333
+
334
+ print(f"{'='*60}")
335
+ print("MEDICAL AI PLATFORM - MONITORING LOAD TEST")
336
+ print(f"{'='*60}")
337
+ print(f"Target: {base_url}")
338
+ print(f"Started: {datetime.now().isoformat()}")
339
+
340
+ try:
341
+ # Test 1: Health endpoint load
342
+ await tester.test_health_endpoint(num_requests=100)
343
+
344
+ # Test 2: Dashboard endpoint load
345
+ await tester.test_dashboard_endpoint(num_requests=50)
346
+
347
+ # Test 3: Admin endpoints
348
+ # await tester.test_admin_endpoints() # Comment out if admin auth is required
349
+
350
+ # Test 4: Monitoring accuracy
351
+ await tester.verify_monitoring_accuracy()
352
+
353
+ # Test 5: Cache effectiveness
354
+ await tester.test_cache_effectiveness()
355
+
356
+ # Test 6: Stress test
357
+ await tester.stress_test(duration_seconds=30)
358
+
359
+ # Generate final report
360
+ tester.generate_report()
361
+
362
+ print(f"\n{'='*60}")
363
+ print("ALL TESTS COMPLETED SUCCESSFULLY")
364
+ print(f"{'='*60}")
365
+
366
+ except Exception as e:
367
+ print(f"\n❌ Test failed with error: {str(e)}")
368
+ raise
369
+
370
+ if __name__ == "__main__":
371
+ import sys
372
+
373
+ # Get base URL from command line or use default
374
+ base_url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:7860"
375
+
376
+ print(f"Starting load tests against: {base_url}")
377
+ print(f"Ensure the server is running before continuing...\n")
378
+
379
+ # Run the tests
380
+ asyncio.run(run_comprehensive_load_test(base_url))