PlainSQL / backend /tests /load /locustfile.py
LalitChaudhari3's picture
feat: synchronize text-to-sql-bot codebase with Hugging Face Space repository, including Docker build configurations
6086e71
Raw
History Blame Contribute Delete
3.63 kB
"""
PlainSQL Load Test β€” Simulates real user behavior across all query types.
Run locally:
locust -f tests/load/locustfile.py --host=http://localhost:8000
Web UI opens at http://localhost:8089 β€” configure users, ramp-up, and duration.
Recommended scenarios:
Smoke: 5 users, 1/sec ramp, 2 min
Normal: 30 users, 5/sec ramp, 10 min
Peak: 100 users, 10/sec ramp, 10 min
Soak: 30 users, 5/sec ramp, 60 min (memory leak detection)
"""
from locust import HttpUser, task, between, tag
import random
# ── Realistic query distribution ─────────────────────────
EASY_QUERIES = [
"Show top 5 employees by salary",
"List all products",
"Show me customers from North America",
"What is the most expensive product?",
"Show me products with low stock",
]
MEDIUM_QUERIES = [
"Total sales revenue by region",
"Which department has the highest average salary?",
"How many employees are in each department?",
"Average product price by category",
]
HARD_QUERIES = [
"Who are the top 3 salespeople by total revenue?",
"Compare sales between Q1 and Q2",
"Monthly sales trend for the last 6 months",
"Which employees have sold more than the department average?",
]
CHAT_QUERIES = [
"Hello",
"What can you do?",
"Thanks!",
"Who are you?",
"How does this work?",
]
# ── Repeat queries to test cache hit behavior ────────────
CACHED_QUERIES = [
"Show top 5 employees by salary",
"What is the total sales revenue?",
]
class PlainSQLUser(HttpUser):
"""Simulates a real user interacting with PlainSQL."""
wait_time = between(2, 8) # 2-8 seconds between requests (realistic pacing)
def on_start(self):
"""Login and get JWT token on user spawn."""
response = self.client.post(
"/api/v1/auth/login",
json={"username": "analyst", "password": "analyst123"},
name="/auth/login",
)
if response.status_code == 200:
self.token = response.json().get("access_token", "")
self.headers = {"Authorization": f"Bearer {self.token}"}
else:
self.headers = {}
def _chat(self, question: str, label: str):
"""Helper to send a chat request with auth headers."""
self.client.post(
"/chat",
json={"question": question, "history": []},
headers=self.headers,
name=f"/chat [{label}]",
)
@task(40)
@tag("easy")
def easy_query(self):
"""Simple data queries β€” should complete in 1-3s."""
self._chat(random.choice(EASY_QUERIES), "easy")
@task(20)
@tag("medium")
def medium_query(self):
"""Aggregation queries β€” medium complexity."""
self._chat(random.choice(MEDIUM_QUERIES), "medium")
@task(10)
@tag("hard")
def hard_query(self):
"""Complex JOIN queries β€” expected 2-4s."""
self._chat(random.choice(HARD_QUERIES), "hard")
@task(10)
@tag("chat")
def chat_message(self):
"""Conversational messages β€” should return in <100ms (no LLM)."""
self._chat(random.choice(CHAT_QUERIES), "chat")
@task(15)
@tag("cached")
def cached_query(self):
"""Repeat queries β€” should be served from cache in <10ms."""
self._chat(random.choice(CACHED_QUERIES), "cached")
@task(5)
@tag("health")
def health_check(self):
"""Health endpoint β€” should always respond in <50ms."""
self.client.get("/api/v1/health", name="/health")