import os import random import time import uuid from locust import FastHttpUser, task, between, events # Configuration defaults with environment variable overrides MIN_WAIT = float(os.getenv("LOCUST_MIN_WAIT", "0.001")) MAX_WAIT = float(os.getenv("LOCUST_MAX_WAIT", "0.010")) TREAT_429_AS_SUCCESS = os.getenv("TREAT_429_AS_SUCCESS", "false").lower() in ("true", "1", "yes") # Valid Radio Access Types supported by the pipeline validation layer VALID_RATS = [1, 6, 9] # Service types SERVICE_TYPES = ["4G_DATA", "5G_DATA", "VOICE", "SMS"] class CDRLoadUser(FastHttpUser): """ High-performance Locust user for generating and ingesting Telecom CDR records. Uses FastHttpUser (geventhttpclient) for maximum throughput with minimal CPU overhead. Runs continuously until manually stopped in the Web GUI or terminal. """ # Low wait time between iterations to maintain continuous traffic flow wait_time = between(MIN_WAIT, MAX_WAIT) @task def ingest_cdr(self) -> None: """ Generates a valid CDR payload and posts it to the raw CDR ingestion endpoint. """ now = int(time.time()) raw_id = f"locust-{uuid.uuid4().hex}" payload = { "raw_id": raw_id, "src_ip": "10.240.12.1", "device_id": "310410998877665", "caller_num": "+14155552671", "svc": random.choice(SERVICE_TYPES), "up_bytes": random.randint(512, 8192), "down_bytes": random.randint(1024, 32768), "rat": random.choice(VALID_RATS), "lac": "4021", "ci": "8812", "epoch_start": now, "epoch_end": now + 300, } with self.client.post( "/api/v1/cdr/raw", json=payload, name="/api/v1/cdr/raw", catch_response=True, ) as response: if response.status_code == 202: response.success() elif response.status_code == 429: # 429 indicates bounded queue saturation (graceful backpressure) if TREAT_429_AS_SUCCESS: response.success() else: response.failure("429 Too Many Requests (Queue Backpressure)") elif response.status_code == 503: response.failure("503 Service Unavailable (Kafka Broker Failure)") elif response.status_code == 400: response.failure(f"400 Bad Request: {response.text}") else: response.failure(f"Unexpected HTTP {response.status_code}: {response.text}")