An educational Azure App Service application that intentionally creates performance problems for learning and demonstration purposes.
This application is designed to help developers and DevOps engineers:
- Learn how to diagnose common performance issues in Azure App Service
- Practice using Azure monitoring and diagnostic tools
- Demonstrate performance anti-patterns in a controlled environment
- Train support teams on identifying and resolving performance problems
This application intentionally creates performance problems!
- π₯ CPU stress - Creates dedicated threads running spin loops to consume all CPU cores
- π Memory pressure - Allocates and pins memory blocks to increase working set
- π§΅ Thread pool starvation - Uses sync-over-async anti-patterns to block thread pool threads
- οΏ½ Slow requests - Generates long-running requests with sync-over-async patterns for CLR Profiler analysis
- οΏ½π₯ Application crashes - Triggers fatal crashes for testing Azure Crash Monitoring and memory dumps
Only deploy this application in isolated, non-production environments.
# Clone the repository
git clone https://github.com/your-org/perf-problem-simulator.git
cd perf-problem-simulator
# Restore and build
dotnet build
# Run the application
dotnet run --project src/PerfProblemSimulator
# Open in browser
# Dashboard: http://localhost:5000
# Swagger: http://localhost:5000/swagger# Run all tests
dotnet test
# Run with coverage
dotnet test --collect:"XPlat Code Coverage"The application includes a real-time dashboard at the root URL that shows:
- CPU usage - Current processor utilization
- Memory - Working set and GC heap sizes
- Thread pool - Active threads and queue length
- Request latency - Real-time probe response time (shows impact of thread pool starvation)
- Active simulations - Currently running problem simulations
The dashboard uses SignalR for real-time updates and includes controls to trigger each type of simulation.
The CPU and Memory metric tiles use dynamic color coding based on utilization percentage:
| Color | Utilization | Status |
|---|---|---|
| Black (default) | 0-60% | Normal |
| Yellow | 60-80% | Warning - elevated usage |
| Red | >80% | Danger - potential resource exhaustion |
Note: Memory thresholds are calculated dynamically based on the actual total available memory reported by the server, ensuring accurate warnings regardless of the machine's RAM configuration.
| Endpoint | Method | Description |
|---|---|---|
/api/health |
GET | Basic health check |
/api/health/status |
GET | Detailed health with active simulations |
/api/health/probe |
GET | Lightweight probe for latency measurement |
/api/health/build |
GET | Build information and assembly version |
/api/metrics/current |
GET | Latest metrics snapshot |
/api/metrics/health |
GET | Detailed health status with warnings |
/api/admin/stats |
GET | Simulation and resource statistics |
| Endpoint | Method | Description |
|---|---|---|
/api/cpu/trigger-high-cpu |
POST | Trigger CPU stress |
Request body:
{
"durationSeconds": 30,
"targetPercentage": 100
}durationSeconds: How long to run (default: 30)targetPercentage: Target CPU usage 1-100 (default: 100)
| Endpoint | Method | Description |
|---|---|---|
/api/memory/allocate-memory |
POST | Allocate memory block |
/api/memory/release-memory |
POST | Release all allocated memory |
/api/memory/status |
GET | Get current memory allocation status |
Request body (allocate):
{
"sizeMegabytes": 100
}| Endpoint | Method | Description |
|---|---|---|
/api/threadblock/trigger-sync-over-async |
POST | Trigger thread blocking |
Request body:
{
"delayMilliseconds": 5000,
"concurrentRequests": 100
}| Endpoint | Method | Description |
|---|---|---|
/api/slowrequest/start |
POST | Start slow request simulation |
/api/slowrequest/stop |
POST | Stop slow request simulation |
/api/slowrequest/status |
GET | Get current simulation status |
/api/slowrequest/scenarios |
GET | Get scenario descriptions for CLR Profiler |
Request body (start):
{
"requestDurationSeconds": 25,
"intervalSeconds": 2,
"maxRequests": 10
}The slow request simulator generates requests using three different sync-over-async patterns:
- SimpleSyncOverAsync: Blocking calls - look for
FetchDataSync_BLOCKING_HERE,ProcessDataSync_BLOCKING_HERE,SaveDataSync_BLOCKING_HEREin traces - NestedSyncOverAsync: Sync methods that block internally - look for
*_BLOCKS_INTERNALLYmethods - DatabasePattern: Simulated database/HTTP blocking - look for
*Sync_SYNC_BLOCKmethods
| Endpoint | Method | Description |
|---|---|---|
/api/crash/trigger |
POST | Trigger a crash with options |
/api/crash/now |
GET/POST | Immediate synchronous crash (best for Azure Crash Monitoring) |
/api/crash/types |
GET | List available crash types |
/api/crash/failfast |
POST | Quick FailFast crash |
/api/crash/stackoverflow |
POST | Quick StackOverflow crash |
Request body (trigger):
{
"crashType": "FailFast",
"delaySeconds": 3,
"message": "Optional crash message"
}Available crash types: FailFast, StackOverflow, UnhandledException, AccessViolation, OutOfMemory
| Endpoint | Method | Description |
|---|---|---|
/api/admin/stats |
GET | Get current simulation statistics |
The dashboard includes a Request Latency Monitor that demonstrates how thread pool starvation affects real-world request processing.
- A dedicated background thread (not from the thread pool) continuously probes
/api/health/probe - Latency is measured end-to-end: request sent β response received
- Results are broadcast via SignalR to the dashboard in real-time
| Scenario | Latency (Queue + Processing) | Status | Explanation |
|---|---|---|---|
| Normal operation | < 150ms | π’ Good | Thread pool threads available |
| Mild starvation | 150ms - 1s | π‘ Degraded | Requests beginning to queue |
| Severe starvation | > 1s | π΄ Severe | Significant queuing delay |
| Timeout | 30s | π΄ Critical | No thread became available within timeout |
During thread pool starvation, CPU and memory metrics often look normal, but users experience severe latency. The latency monitor makes this invisible problem visible - you can watch response times spike from milliseconds to seconds when triggering the sync-over-async simulation.
Configuration is managed through appsettings.json:
{
"ProblemSimulator": {
"MetricsCollectionIntervalMs": 1000
}
}Note: This application is designed to be fully breakable for educational purposes. There are no safety limits on resource consumption β simulations can run until the application crashes or resources are exhausted.
| Variable | Description | Default |
|---|---|---|
DISABLE_PROBLEM_ENDPOINTS |
Set to true to disable all problem-triggering endpoints |
false |
# Login to Azure
az login
# Create resource group
az group create --name rg-perf-simulator --location eastus
# Create App Service plan
az appservice plan create \
--name asp-perf-simulator \
--resource-group rg-perf-simulator \
--sku B1 \
--is-linux
# Create Web App
az webapp create \
--name your-unique-app-name \
--resource-group rg-perf-simulator \
--plan asp-perf-simulator \
--runtime "DOTNETCORE:10.0"
# Deploy
cd src/PerfProblemSimulator
dotnet publish -c Release
az webapp deploy \
--resource-group rg-perf-simulator \
--name your-unique-app-name \
--src-path bin/Release/net10.0/publishAfter deployment, consider disabling problem endpoints:
az webapp config appsettings set \
--resource-group rg-perf-simulator \
--name your-unique-app-name \
--settings DISABLE_PROBLEM_ENDPOINTS=trueThis application is designed to work with Azure App Service diagnostics:
- Diagnose and Solve Problems - App Service blade for automated diagnosis
- Application Insights - For detailed telemetry and performance monitoring
- Process Explorer - For real-time process monitoring
- CPU Profiling - Capture and analyze CPU traces
- Memory Dumps - Analyze memory allocations
See Azure Monitoring Guide for detailed instructions.
src/PerfProblemSimulator/
βββ Controllers/ # API endpoints
β βββ AdminController.cs
β βββ CpuController.cs
β βββ CrashController.cs
β βββ HealthController.cs
β βββ MemoryController.cs
β βββ MetricsController.cs
β βββ SlowRequestController.cs
β βββ ThreadBlockController.cs
βββ Services/ # Business logic
β βββ CpuStressService.cs
β βββ CrashService.cs
β βββ LatencyProbeService.cs
β βββ MemoryPressureService.cs
β βββ MetricsBroadcastService.cs
β βββ MetricsCollector.cs
β βββ SimulationTracker.cs
β βββ SlowRequestService.cs
β βββ ThreadBlockService.cs
βββ Hubs/ # SignalR for real-time updates
β βββ MetricsHub.cs
β βββ IMetricsClient.cs
βββ Models/ # Data transfer objects
βββ Middleware/ # Request pipeline
β βββ ProblemEndpointGuard.cs
βββ wwwroot/ # SPA dashboard
βββ index.html
βββ documentation.html
βββ azure-monitoring-guide.html
βββ css/dashboard.css
βββ js/dashboard.js
The project includes comprehensive unit and integration tests:
# Run all tests
dotnet test
# Run with verbose output
dotnet test --logger "console;verbosity=detailed"
# Run specific test category
dotnet test --filter "Category=Unit"This project is licensed under the MIT License - see the LICENSE file for details.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Designed for educational use in Azure App Service training
- Inspired by common performance anti-patterns encountered in production
- Built with .NET 10.0 and ASP.NET Core