Run benchmarks against gunicorn with dirty pool via HTTP. Uses wrk or ab for load testing, or falls back to Python requests.
| 368 | |
| 369 | |
| 370 | class IntegratedBenchmark: |
| 371 | """ |
| 372 | Run benchmarks against gunicorn with dirty pool via HTTP. |
| 373 | |
| 374 | Uses wrk or ab for load testing, or falls back to Python requests. |
| 375 | """ |
| 376 | |
| 377 | def __init__( |
| 378 | self, |
| 379 | url: str = "http://127.0.0.1:8000", |
| 380 | verbose: bool = False, |
| 381 | ): |
| 382 | self.url = url.rstrip('/') |
| 383 | self.verbose = verbose |
| 384 | self._tool = None |
| 385 | |
| 386 | def check_dependencies(self) -> str | None: |
| 387 | """Check for available load testing tools.""" |
| 388 | for tool in ['wrk', 'ab']: |
| 389 | try: |
| 390 | subprocess.run([tool, '--version'], capture_output=True, |
| 391 | check=False) |
| 392 | return tool |
| 393 | except FileNotFoundError: |
| 394 | continue |
| 395 | return None |
| 396 | |
| 397 | def warmup(self, requests: int = 10): |
| 398 | """Warm up the server.""" |
| 399 | import urllib.request |
| 400 | for _ in range(requests): |
| 401 | try: |
| 402 | urllib.request.urlopen(f"{self.url}/health", timeout=5) |
| 403 | except Exception: |
| 404 | pass |
| 405 | |
| 406 | def run_wrk( |
| 407 | self, |
| 408 | path: str, |
| 409 | duration: int = 10, |
| 410 | threads: int = 4, |
| 411 | connections: int = 100, |
| 412 | ) -> dict: |
| 413 | """Run wrk benchmark and parse results.""" |
| 414 | url = f"{self.url}{path}" |
| 415 | cmd = [ |
| 416 | 'wrk', |
| 417 | '-t', str(threads), |
| 418 | '-c', str(connections), |
| 419 | '-d', f'{duration}s', |
| 420 | '--latency', |
| 421 | url, |
| 422 | ] |
| 423 | |
| 424 | result = subprocess.run(cmd, capture_output=True, text=True, |
| 425 | check=False) |
| 426 | return self._parse_wrk_output(result.stdout) |
| 427 |