Skip to content

Repository files navigation

⚡ Thunderbolt

Enterprise-Grade Distributed Load & Performance Testing Platform

Quick StartArchitectureFeaturesAPIDeploymentContributing


Thunderbolt is a cloud-native, distributed load testing platform built with .NET 10, Akka.NET, and event sourcing. It orchestrates thousands of virtual users across a cluster of worker nodes to simulate realistic traffic patterns against HTTP, gRPC, WebSocket, MQTT, AMQP, and raw TCP/UDP endpoints.

✨ Features

  • Distributed Cluster Engine — Akka.NET cluster with coordinator/worker topology, automatic shard rebalancing, and split-brain resolution
  • Multi-Protocol Support — HTTP, gRPC, WebSocket, MQTT, AMQP, Raw TCP/UDP with a pluggable protocol handler architecture
  • JSON Scenario Definitions — Declarative test scenarios with steps, extractors, assertions, data feeders, and multiple load profiles (ramp-up, constant, steps, spike, custom)
  • Real-Time Metrics — Live streaming via SignalR with HdrHistogram percentile tracking (P50/P75/P90/P95/P99), RPS, error rates, and throughput
  • Event-Sourced Persistence — Full test lifecycle stored via Marten/PostgreSQL event store with projections for read models
  • Time-Series Metrics Storage — InfluxDB for high-resolution metric data with configurable batching and gzip compression
  • Event Streaming — Kafka-based event bus for inter-service communication and external integrations
  • AI-Powered Agents — Microsoft Semantic Kernel agents for scenario generation, metrics analysis, SLO advisory, and test comparison
  • Blazor Dashboard — Server-side Blazor UI with MudBlazor components for test management, real-time monitoring, and AI assistant
  • Multi-Tenancy — Tenant isolation via header-based resolution with per-tenant event streams
  • Plugin System — Hot-loadable protocol plugins via assembly scanning
  • Kubernetes-Native — Helm charts, Kubernetes API discovery, and Docker images for all services
  • Observability — OpenTelemetry tracing + Prometheus metrics export, Serilog structured logging with Seq sink

🏗 Architecture

 ┌──────────────────┐
│ Dashboard │
│ (Blazor/MudBlazor)│
└────────┬─────────┘
│ SignalR + HTTP
┌────────▼─────────┐
│ API Server │
│ (ASP.NET Minimal)│
└────────┬─────────┘
│ Akka.NET Cluster
┌──────────────┼──────────────┐
│ │ │
┌──────▼──────┐ ┌───▼──────┐ ┌───▼──────┐
│ Coordinator │ │ Worker-1 │ │ Worker-N │
│ (Singleton) │ │ (Sharded)│ │ (Sharded)│
└──────┬──────┘ └───┬──────┘ └───┬──────┘
│ │ │
│ ┌────▼────┐ ┌────▼────┐
│ │ Virtual │ │ Virtual │
│ │ Users │ │ Users │
│ └────┬────┘ └────┬────┘
│ │ │
┌──────▼──────────────▼──────────────▼──────┐
│ Target System(s) │
└───────────────────────────────────────────┘
┌────────────┐ ┌────────────┐ ┌────────────┐
│ PostgreSQL │ │ InfluxDB │ │ Kafka │
│(Event Store)│ │ (Metrics) │ │(Streaming) │
└────────────┘ └────────────┘ └────────────┘

Node Roles

RoleDescription
CoordinatorCluster singleton that orchestrates test lifecycle, distributes VUs across workers, handles auto-stop timers, and manages worker failover
WorkerSharded actor region that spawns and manages VirtualUserActor instances executing scenario steps via protocol handlers
APIASP.NET Minimal API node joined to the cluster, exposes REST endpoints, SignalR hub, and Prometheus scraping
DashboardBlazor Server app consuming the API with real-time SignalR metrics streaming

📦 Project Structure

src/
├── Thunderbolt.Core/ # Domain models, aggregates, events, messages, protocols
├── Thunderbolt.Engine/ # Akka.NET actors (Coordinator, Worker, VirtualUser, MetricsAggregator)
├── Thunderbolt.Scenarios/ # Scenario parsing, load profiles, assertions, data feeders, extractors
├── Thunderbolt.Protocols/ # Protocol handler implementations
│ ├── Thunderbolt.Protocols.Abstractions/
│ ├── Thunderbolt.Protocols.Http/
│ ├── Thunderbolt.Protocols.Grpc/
│ ├── Thunderbolt.Protocols.WebSocket/
│ ├── Thunderbolt.Protocols.Mqtt/
│ ├── Thunderbolt.Protocols.Amqp/
│ └── Thunderbolt.Protocols.RawSocket/
├── Thunderbolt.Persistence/ # Marten event store, projections, read models
├── Thunderbolt.Metrics/ # InfluxDB writer, query service, HdrHistogram
├── Thunderbolt.Streaming/ # Kafka producer/consumer, event subscriptions
├── Thunderbolt.Plugins/ # Plugin host, protocol registry, assembly loading
├── Thunderbolt.Agents/ # AI agents (Semantic Kernel) — scenario generator, metrics analyst, SLO advisor
├── Thunderbolt.Api/ # REST API, SignalR hub, middleware, authentication
├── Thunderbolt.Coordinator/ # Coordinator node host
├── Thunderbolt.Worker/ # Worker node host
└── Thunderbolt.Dashboard/ # Blazor Server dashboard
tests/ # xUnit tests with FluentAssertions, NSubstitute, Testcontainers
plugins/ # Example protocol plugin
scenarios/ # Sample scenario definitions (JSON)
deploy/
├── docker/ # Dockerfiles for each service
├── helm/thunderbolt/ # Helm chart for Kubernetes deployment
└── k8s/ # Raw Kubernetes manifests

🚀 Quick Start

Prerequisites

Option 1: Docker Compose (Recommended)

Build and publish the applications:

# Publish all services
dotnet publish src/Thunderbolt.Api -c Release -o out/api
dotnet publish src/Thunderbolt.Coordinator -c Release -o out/coordinator
dotnet publish src/Thunderbolt.Worker -c Release -o out/worker
dotnet publish src/Thunderbolt.Dashboard -c Release -o out/dashboard

Start the full stack:

docker compose up --build -d

This starts:

ServiceURL
Dashboardhttp://localhost:5100
APIhttp://localhost:5000
InfluxDBhttp://localhost:8086
PostgreSQLlocalhost:5432
Kafkalocalhost:9092

The default stack includes 1 coordinator, 2 workers, 1 API, and 1 dashboard node.

Option 2: Local Development

Start infrastructure services only:

docker compose up postgres influxdb kafka -d

Then run each service in separate terminals:

# Terminal 1 — Coordinator
dotnet run --project src/Thunderbolt.Coordinator
# Terminal 2 — Worker
dotnet run --project src/Thunderbolt.Worker
# Terminal 3 — API
dotnet run --project src/Thunderbolt.Api
# Terminal 4 — Dashboard
dotnet run --project src/Thunderbolt.Dashboard

📋 Configuration

Configuration is managed via appsettings.json and environment variables. Key sections:

Cluster Configuration

{
"Thunderbolt": {
"Cluster": {
"Hostname": "0.0.0.0",
"Port": 8558,
"Role": "coordinator|worker|api",
"SeedNodes": ["akka.tcp://thunderbolt@coordinator:8558"],
"UseKubernetesDiscovery": false,
"KubernetesLabelSelector": "app=thunderbolt",
"SplitBrainStrategy": "keep-majority",
"NumberOfShards": 100,
"PersistenceConnectionString": "Host=postgres;Database=thunderbolt;..."
}
}
}

Metrics (InfluxDB)

{
"Thunderbolt": {
"InfluxDb": {
"Url": "http://localhost:8086",
"Token": "your-token",
"Organization": "thunderbolt",
"Bucket": "metrics",
"BatchSize": 5000,
"FlushIntervalMs": 1000,
"EnableGzip": true
}
}
}

Streaming (Kafka)

{
"Thunderbolt": {
"Kafka": {
"BootstrapServers": "localhost:9092",
"GroupId": "thunderbolt-api",
"TestEventsTopic": "thunderbolt.test-events",
"MetricsTopic": "thunderbolt.metrics",
"CommandsTopic": "thunderbolt.commands"
}
}
}

AI Agents (Semantic Kernel)

{
"Thunderbolt": {
"Ai": {
"Provider": "AzureOpenAI",
"ModelId": "gpt-4o",
"Endpoint": "https://your-endpoint.openai.azure.com",
"ApiKey": "your-api-key",
"MaxTokens": 4096,
"Temperature": 0.3,
"Agents": {
"ScenarioGenerator": true,
"MetricsAnalyst": true,
"SloAdvisor": true,
"TestPlanner": true
}
}
}
}

⚠️Security: Never commit API keys or secrets. Use environment variables or a secret manager in production.

Environment Variable Overrides

All configuration keys can be set via environment variables using the __ (double underscore) separator:

Thunderbolt__Cluster__Role=worker
Thunderbolt__InfluxDb__Token=your-token
Thunderbolt__Kafka__BootstrapServers=kafka:29092
ConnectionStrings__PostgreSQL="Host=postgres;Database=thunderbolt;..."

📖 API Reference

All endpoints are prefixed with /api/v1 and require authentication (JWT Bearer in production, auto-authenticated in Development mode).

Load Tests

MethodEndpointDescription
POST/api/v1/testsCreate and start a new load test
GET/api/v1/testsList all tests (paginated)
GET/api/v1/tests/{testId}Get test details
GET/api/v1/tests/{testId}/statusGet live test status with real-time metrics
POST/api/v1/tests/{testId}/stopGracefully stop a running test
DELETE/api/v1/tests/{testId}Cancel a test

Scenarios

MethodEndpointDescription
POST/api/v1/scenariosCreate a new scenario
GET/api/v1/scenariosList all scenarios
GET/api/v1/scenarios/{id}Get scenario details
PUT/api/v1/scenarios/{id}Update a scenario
DELETE/api/v1/scenarios/{id}Delete a scenario

Metrics

MethodEndpointDescription
GET/api/v1/metrics/{testId}Query historical metrics from InfluxDB

AI Agents

MethodEndpointDescription
POST/api/v1/ai/generate-scenarioGenerate a scenario from natural language
POST/api/v1/ai/analyze-metricsAI analysis of test metrics
POST/api/v1/ai/slo-advisorGet SLO recommendations
POST/api/v1/ai/compare-testsCompare two test runs

Real-Time

ProtocolEndpointDescription
SignalR/hubs/loadtestLive metrics streaming (VU count, RPS, latency percentiles, errors)
Prometheus/metricsPrometheus scraping endpoint

Multi-Tenancy

Include the tenant header in all API requests:

X-Tenant-Id: your-tenant-id

📝 Scenario Definition

Scenarios are defined in JSON and support complex user journeys with variable extraction, data feeding, and assertions.

Example: E-Commerce Load Test

{
"name": "E-Commerce User Journey",
"description": "Simulates browsing, searching, and adding to cart",
"protocol": "http",
"loadProfile": {
"type": "steps",
"stages": [
{ "users": 50, "durationSeconds": 60 },
{ "users": 150, "durationSeconds": 120 },
{ "users": 300, "durationSeconds": 180 },
{ "users": 50, "durationSeconds": 60 }
]
},
"steps": [
{
"name": "Homepage",
"type": "http_request",
"method": "GET",
"url": "https://example.com",
"expectedStatusCodes": [200],
"headers": { "Accept": "text/html" },
"thinkTimeMs": 2000,
"timeoutSeconds": 15,
"extractors": [
{
"type": "regex",
"name": "csrfToken",
"pattern": "<meta name=\"csrf-token\" content=\"([^\"]+)\""
},
{
"type": "cookie",
"name": "sessionId",
"pattern": "JSESSIONID"
}
]
},
{
"name": "Add to Cart",
"type": "http_request",
"method": "POST",
"url": "https://example.com/api/cart/add",
"headers": {
"X-CSRF-Token": "{{csrfToken}}",
"Cookie": "JSESSIONID={{sessionId}}"
},
"body": "{\"productId\":\"{{productId}}\",\"quantity\":1}",
"contentType": "application/json",
"thinkTimeMs": 1500,
"extractors": [
{ "type": "json_path", "name": "cartId", "pattern": "cartId" }
]
}
],
"assertions": [
{ "type": "response_time_percentile", "percentile": 95, "maxMs": 3000 },
{ "type": "error_rate", "maxErrorRate": 0.01 },
{ "type": "throughput", "minRps": 50 }
],
"dataFeeder": {
"type": "json",
"strategy": "random",
"data": [
{ "productId": "SKU-001" },
{ "productId": "SKU-002" },
{ "productId": "SKU-003" }
]
}
}

Load Profile Types

TypeDescription
rampUpLinear ramp from 0 to N users over a duration
constantFixed number of users for a duration
stepsStaged increases/decreases in user count
spikeSudden burst of users to test resilience
customUser-defined load curve

Extractor Types

TypeDescription
regexExtract values from response body via regex capture groups
json_pathExtract values from JSON response body
headerExtract values from response headers
cookieExtract values from response cookies

Assertion Types

TypeDescription
response_time_percentileP50/P75/P90/P95/P99 latency thresholds
error_rateMaximum allowed error rate (0.0 – 1.0)
throughputMinimum requests per second

🧪 Testing

# Run all tests
dotnet test# Run specific test project
dotnet test tests/Thunderbolt.Core.Tests
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"

The test suite includes:

  • Unit tests — Domain models, aggregates, scenarios, protocols, metrics
  • Integration tests — PostgreSQL and Kafka via Testcontainers
  • Libraries — xUnit, FluentAssertions, NSubstitute

🐳 Docker

Individual Dockerfiles are provided for each service:

# Build individual images
docker build -f docker/Dockerfile.api -t thunderbolt-api .
docker build -f docker/Dockerfile.coordinator -t thunderbolt-coordinator .
docker build -f docker/Dockerfile.worker -t thunderbolt-worker .
docker build -f docker/Dockerfile.dashboard -t thunderbolt-dashboard .

☸️ Deployment

Kubernetes (Helm)

helm install thunderbolt deploy/helm/thunderbolt \
--namespace thunderbolt \
--create-namespace \
--set coordinator.replicas=1 \
--set worker.replicas=3 \
--set api.replicas=2

The Helm chart includes:

  • Coordinator Deployment (singleton pattern)
  • Worker StatefulSet (scalable)
  • API Deployment with Service
  • Dashboard Deployment with Service
  • ConfigMap for shared configuration
  • RBAC for Kubernetes API discovery

Scaling Workers

Workers auto-join the cluster via seed node discovery. To scale:

# Kubernetes
kubectl scale deployment thunderbolt-worker --replicas=5 -n thunderbolt
# Docker Compose
docker compose up --scale worker-1=3 -d

The coordinator automatically redistributes virtual users when workers join or leave, including failover when a worker becomes unreachable.

🔧 Technology Stack

ComponentTechnology
Runtime.NET 10 / C# (latest)
Actor SystemAkka.NET 1.5 (Cluster, Sharding, Persistence, DistributedPubSub)
Event StoreMarten 7.x / PostgreSQL 16
Time-Series DBInfluxDB 2.7
Message BrokerApache Kafka (Confluent)
DashboardBlazor Server / MudBlazor 8.x
Real-TimeASP.NET SignalR
AIMicrosoft Semantic Kernel 1.74 / Azure OpenAI
HistogramsHdrHistogram
AuthJWT Bearer / OpenID Connect
TelemetryOpenTelemetry + Prometheus
LoggingSerilog (Console + Seq)
SerializationSystem.Text.Json / YamlDotNet
TestingxUnit, FluentAssertions, NSubstitute, Testcontainers
ContainerizationDocker, Helm, Kubernetes

📊 Metrics & Observability

Thunderbolt captures detailed metrics for every request:

MetricDescription
DurationMsTotal request duration
TtfbMsTime to first byte
ConnectMsTCP connection time
TlsMsTLS handshake time
BytesSentRequest payload size
BytesReceivedResponse payload size
StatusCodeHTTP/protocol status code
IsErrorError flag (status code + pattern matching)

Aggregated metrics are computed in real-time using HdrHistogram:

  • Percentiles: P50, P75, P90, P95, P99
  • Throughput: Requests/second
  • Error Rate: Errors / Total requests
  • Min/Max/Avg: Latency statistics

🤖 AI Agents

Thunderbolt includes four AI-powered agents built with Microsoft Semantic Kernel:

AgentDescription
Scenario GeneratorGenerates complete JSON scenario definitions from natural language descriptions
Metrics AnalystAnalyzes test results and provides performance insights
SLO AdvisorRecommends Service Level Objectives based on test data
Test ComparisonCompares two test runs and highlights regressions

Configure AI by setting the Thunderbolt:Ai section in appsettings.json or via environment variables.

🔌 Plugin System

Create custom protocol handlers by implementing IProtocolHandler and packaging as a .NET class library:

publicclassMyProtocolHandler:IProtocolHandler{publicstringProtocolName=>"my-protocol";publicTask<ResponseResult>ExecuteAsync(RequestContextcontext){// Your protocol implementation}}

Place the compiled DLL in the plugins/ directory — Thunderbolt auto-discovers and registers it at startup.

📄 License

This project is licensed under the MIT License.

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Built with ⚡ by the Thunderbolt Contributors

About

Distributed High-Available, High-Scalable Load & Performance Testing Toolkit

Resources

Code of conduct

Contributing

Security policy

Stars

3 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages