yusufcalisir's picture
deploy: Hugging Face space upload
73ba4f5
Raw History Blame Contribute Delete
6.77 kB
"""Locust Load Testing Suite for Real-Time Inference Gateway & API Layer.
Simulates concurrent banking client nodes, automated payment streams,
and compliance investigators to benchmark SLA adherence (<100ms p99 latency),
throughput capacity (TPS), and error rates.
Usage:
# Headless 60-second load test with 50 concurrent users
locust -f scripts/locustfile.py --headless -u 50 -r 10 --run-time 60s --host http://localhost:8000
# Web UI mode
locust -f scripts/locustfile.py --host http://localhost:8000
"""
from __future__ import annotations
# gevent monkey-patching: ensure socket and ssl are safely patched before standard networking modules
# to prevent RecursionError on Python 3.12 under high-concurrency load tests.
try:
import gevent.monkey
if not gevent.monkey.is_module_patched("socket"):
gevent.monkey.patch_all()
except (ImportError, Exception):
pass
import random
import uuid
from typing import Any
try:
from locust import FastHttpUser, between, task
except ImportError:
class FastHttpUser: # type: ignore[no-redef]
pass
def between(a: float, b: float) -> Any: # type: ignore[no-redef]
return None
def task(weight: int = 1) -> Any: # type: ignore[no-redef]
def decorator(f: Any) -> Any:
return f
return decorator
_BANK_IDS = ["bank_alpha", "bank_beta", "bank_gamma"]
_CURRENCIES = ["EUR", "USD", "GBP", "CHF"]
_MERCHANTS = ["crypto_exchange", "electronics", "wire_transfer", "gambling", "retail", "jewelry"]
class BankingNodeUser(FastHttpUser):
"""Simulates active banking nodes streaming real-time transactions for scoring."""
wait_time = between(0.01, 0.05) # High frequency payment stream (20-100 req/s per user)
@task(60)
def score_realtime_transaction(self) -> None:
"""Benchmark /api/v1/predict endpoint for real-time <100ms inference SLA."""
tx_id = f"txn_{uuid.uuid4().hex[:12]}"
bank_id = random.choice(_BANK_IDS)
amount = round(random.uniform(5.0, 50000.0), 2)
merchant = random.choice(_MERCHANTS)
payload: dict[str, Any] = {
"transaction_id": tx_id,
"bank_id": bank_id,
"source_bank_id": bank_id,
"amount": amount,
"currency": random.choice(_CURRENCIES),
"merchant_category": merchant,
"sender_account": f"DE893704004405320130{random.randint(10, 99)}",
"receiver_account": f"GB29NWBK601613319268{random.randint(10, 99)}",
"device_fingerprint": f"dev_fp_{uuid.uuid4().hex[:8]}",
"country_code": random.choice(["DE", "FR", "US", "GB", "NL", "TR"]),
"timestamp": "2026-09-02T05:00:00Z",
}
headers = {
"X-Tenant-ID": bank_id,
"X-Bank-ID": bank_id,
"Content-Type": "application/json",
}
with self.client.post(
"/api/v1/predict",
json=payload,
headers=headers,
name="POST /api/v1/predict",
catch_response=True,
) as response:
if response.status_code == 200:
data = response.json()
if "risk_score" in data or "composite_risk_score" in data:
response.success()
else:
response.failure(f"Missing risk score in payload: {response.text}")
elif response.status_code == 429:
# Rate limited under extreme burst (expected at high concurrency)
response.success()
else:
response.failure(f"Unexpected status code: {response.status_code}")
@task(20)
def score_v1_inference_endpoint(self) -> None:
"""Benchmark /v1/inference/score endpoint."""
bank_id = random.choice(_BANK_IDS)
payload = {
"transaction_id": f"txn_{uuid.uuid4().hex[:12]}",
"source_bank_id": bank_id,
"amount": round(random.uniform(10.0, 150000.0), 2),
"currency": "EUR",
"sender_account": f"DE893704004405320130{random.randint(10, 99)}",
"receiver_account": f"GB29NWBK601613319268{random.randint(10, 99)}",
"merchant_category": random.choice(_MERCHANTS),
"device_fingerprint": f"dev_fp_{uuid.uuid4().hex[:8]}",
}
self.client.post(
"/v1/inference/score",
json=payload,
headers={"X-Tenant-ID": bank_id, "X-Bank-ID": bank_id},
name="POST /v1/inference/score",
)
@task(10)
def check_health_readiness(self) -> None:
"""Verify low-overhead liveness & readiness probes."""
self.client.get("/health", name="GET /health")
self.client.get("/ready", name="GET /ready")
class ComplianceAnalystUser(FastHttpUser):
"""Simulates compliance officers reviewing cases and authenticating."""
wait_time = between(0.1, 0.5)
def on_start(self) -> None:
"""Log in on session start."""
self.auth_token = ""
res = self.client.post(
"/api/v1/auth/login",
json={"username": "investigator_alpha", "password": "DemoPassword123!"},
name="POST /api/v1/auth/login",
)
if res.status_code == 200:
self.auth_token = res.json().get("access_token", "")
@task(15)
def fetch_cases_list(self) -> None:
"""Query case workbench cases with tenant isolation."""
headers = {
"Authorization": f"Bearer {self.auth_token}" if self.auth_token else "",
"X-Tenant-ID": "bank_alpha",
"X-Bank-ID": "bank_alpha",
}
self.client.get("/api/v1/cases", headers=headers, name="GET /api/v1/cases")
@task(5)
def check_lockout_status(self) -> None:
"""Query brute-force lockout status."""
self.client.get(
"/api/v1/auth/lockout-status?username=investigator_alpha",
name="GET /api/v1/auth/lockout-status",
)
if __name__ == "__main__":
import sys
try:
from locust import main
# Default to this locustfile if -f / --locustfile not explicitly given
if "-f" not in sys.argv and "--locustfile" not in sys.argv:
sys.argv.insert(1, "-f")
sys.argv.insert(2, __file__)
main.main()
except ImportError:
if "--help" in sys.argv or "-h" in sys.argv:
print("locustfile.py: Load testing suite using Locust.")
print("Usage: python scripts/locustfile.py [options]")
print("\nNote: Locust is not installed in the current environment (pip install locust).")
sys.exit(0)
print("[ERROR] Locust is not installed. Run: pip install locust")
sys.exit(1)