Skip to content

Commit b3b92b1

Browse files
author
rhamlett_microsoft
committed
Increase testing aggresiveness.
1 parent 10e3fd0 commit b3b92b1

5 files changed

Lines changed: 158 additions & 97 deletions

File tree

src/PerfProblemSimulator/Controllers/LoadTestController.cs

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public LoadTestController(
137137
* 3. Allows structured parameters in request body
138138
*
139139
* ALTERNATIVE: GET with query parameters
140-
* GET /api/loadtest?workIterations=1000&bufferSizeKb=5
140+
* GET /api/loadtest?workIterations=1000&bufferSizeKb=100
141141
* Simpler but less flexible for complex parameters.
142142
*
143143
* URL PATTERN:
@@ -150,8 +150,9 @@ public LoadTestController(
150150
/// </summary>
151151
/// <param name="workIterations">Number of SHA256 hash iterations (default: 1000).</param>
152152
/// <param name="bufferSizeKb">Memory buffer size in KB (default: 100).</param>
153-
/// <param name="softLimit">Concurrent request soft limit (default: 10).</param>
154-
/// <param name="degradationFactor">Delay ms per request over limit (default: 50).</param>
153+
/// <param name="baselineDelayMs">Minimum blocking delay in ms (default: 500).</param>
154+
/// <param name="softLimit">Concurrent request soft limit (default: 5).</param>
155+
/// <param name="degradationFactor">Delay ms per request over limit (default: 200).</param>
155156
/// <param name="cancellationToken">Cancellation token from the HTTP request pipeline.</param>
156157
/// <returns>Load test result with timing and diagnostic information.</returns>
157158
/// <remarks>
@@ -160,12 +161,13 @@ public LoadTestController(
160161
/// </para>
161162
/// <para>
162163
/// 1. Start a timer (Stopwatch)
163-
/// 2. Check current concurrent request count
164-
/// 3. If over soft limit, calculate and apply degradation delay
165-
/// 4. Perform lightweight CPU work (hash iterations)
166-
/// 5. Allocate small memory buffer (released when request ends)
167-
/// 6. Periodically check if elapsed time > 120s; if so, 20% chance of exception
168-
/// 7. Return response with timing details
164+
/// 2. Apply baseline blocking delay (guarantees thread pool exhaustion)
165+
/// 3. Check current concurrent request count
166+
/// 4. If over soft limit, calculate and apply degradation delay
167+
/// 5. Perform lightweight CPU work (hash iterations)
168+
/// 6. Allocate memory buffer (released when request ends)
169+
/// 7. Periodically check if elapsed time > 120s; if so, 20% chance of exception
170+
/// 8. Return response with timing details
169171
/// </para>
170172
/// <para>
171173
/// <strong>PARAMETERS:</strong>
@@ -180,27 +182,32 @@ public LoadTestController(
180182
/// <description>Size of memory buffer to allocate in kilobytes. Released after request.</description>
181183
/// </item>
182184
/// <item>
183-
/// <term>softLimit (default: 10)</term>
185+
/// <term>baselineDelayMs (default: 500)</term>
186+
/// <description>Minimum blocking delay applied to every request. Ensures thread pool exhaustion.</description>
187+
/// </item>
188+
/// <item>
189+
/// <term>softLimit (default: 5)</term>
184190
/// <description>Concurrent request count before degradation delays begin.</description>
185191
/// </item>
186192
/// <item>
187-
/// <term>degradationFactor (default: 50)</term>
193+
/// <term>degradationFactor (default: 200)</term>
188194
/// <description>Milliseconds of delay added per concurrent request over the soft limit.</description>
189195
/// </item>
190196
/// </list>
191197
/// <para>
192-
/// <strong>DEGRADATION FORMULA:</strong>
198+
/// <strong>TOTAL DELAY FORMULA:</strong>
193199
/// <code>
194-
/// additionalDelayMs = max(0, currentConcurrent - softLimit) * degradationFactor
200+
/// totalDelay = baselineDelayMs + max(0, currentConcurrent - softLimit) * degradationFactor
195201
/// </code>
196202
/// </para>
197203
/// <para>
198-
/// <strong>EXAMPLE SCENARIOS:</strong>
204+
/// <strong>EXAMPLE SCENARIOS (with defaults baselineDelayMs=500, softLimit=5, factor=200):</strong>
199205
/// </para>
200206
/// <list type="bullet">
201-
/// <item>10 concurrent requests, softLimit=50 → 0ms added delay</item>
202-
/// <item>60 concurrent requests, softLimit=50, factor=5 → 50ms added delay</item>
203-
/// <item>150 concurrent requests, softLimit=50, factor=5 → 500ms added delay</item>
207+
/// <item>1 concurrent request → 500ms baseline only</item>
208+
/// <item>10 concurrent requests → 500ms + (10-5)×200 = 1500ms total</item>
209+
/// <item>20 concurrent requests → 500ms + (20-5)×200 = 3500ms total</item>
210+
/// <item>50 concurrent requests → 500ms + (50-5)×200 = 9500ms total</item>
204211
/// </list>
205212
/// </remarks>
206213
/// <response code="200">Load test completed successfully with timing details.</response>
@@ -212,8 +219,9 @@ public LoadTestController(
212219
public async Task<IActionResult> ExecuteLoadTest(
213220
[FromQuery] int workIterations = 1000,
214221
[FromQuery] int bufferSizeKb = 100,
215-
[FromQuery] int softLimit = 10,
216-
[FromQuery] int degradationFactor = 50,
222+
[FromQuery] int baselineDelayMs = 500,
223+
[FromQuery] int softLimit = 5,
224+
[FromQuery] int degradationFactor = 200,
217225
CancellationToken cancellationToken = default)
218226
{
219227
/*
@@ -225,8 +233,8 @@ public async Task<IActionResult> ExecuteLoadTest(
225233
* with Azure Load Testing, JMeter, and browser testing.
226234
*
227235
* Examples:
228-
* - GET/POST /api/loadtest (uses all defaults)
229-
* - GET/POST /api/loadtest?softLimit=25&degradationFactor=10
236+
* - GET/POST /api/loadtest (uses all defaults - maximum stress)
237+
* - GET/POST /api/loadtest?baselineDelayMs=200&softLimit=20&degradationFactor=50
230238
*
231239
* PORTING NOTES:
232240
* - Query parameters are universal across HTTP clients
@@ -239,6 +247,7 @@ public async Task<IActionResult> ExecuteLoadTest(
239247
{
240248
WorkIterations = workIterations,
241249
BufferSizeKb = bufferSizeKb,
250+
BaselineDelayMs = baselineDelayMs,
242251
SoftLimit = softLimit,
243252
DegradationFactor = degradationFactor
244253
};
@@ -302,8 +311,9 @@ public async Task<IActionResult> ExecuteLoadTest(
302311
/// </summary>
303312
/// <param name="workIterations">Number of hash iterations (default: 1000).</param>
304313
/// <param name="bufferSizeKb">Memory buffer size in KB (default: 100).</param>
305-
/// <param name="softLimit">Concurrent request soft limit (default: 10).</param>
306-
/// <param name="degradationFactor">Delay ms per request over limit (default: 50).</param>
314+
/// <param name="baselineDelayMs">Minimum blocking delay in ms (default: 500).</param>
315+
/// <param name="softLimit">Concurrent request soft limit (default: 5).</param>
316+
/// <param name="degradationFactor">Delay ms per request over limit (default: 200).</param>
307317
/// <param name="cancellationToken">Cancellation token.</param>
308318
/// <returns>Load test result with timing details.</returns>
309319
[HttpGet("probe")]
@@ -312,8 +322,9 @@ public async Task<IActionResult> ExecuteLoadTest(
312322
public async Task<IActionResult> ExecuteLoadTestProbe(
313323
[FromQuery] int workIterations = 1000,
314324
[FromQuery] int bufferSizeKb = 100,
315-
[FromQuery] int softLimit = 10,
316-
[FromQuery] int degradationFactor = 50,
325+
[FromQuery] int baselineDelayMs = 500,
326+
[FromQuery] int softLimit = 5,
327+
[FromQuery] int degradationFactor = 200,
317328
CancellationToken cancellationToken = default)
318329
{
319330
/*
@@ -332,7 +343,7 @@ public async Task<IActionResult> ExecuteLoadTestProbe(
332343
*/
333344

334345
// Delegate to main endpoint - both now use query parameters
335-
return await ExecuteLoadTest(workIterations, bufferSizeKb, softLimit, degradationFactor, cancellationToken);
346+
return await ExecuteLoadTest(workIterations, bufferSizeKb, baselineDelayMs, softLimit, degradationFactor, cancellationToken);
336347
}
337348

338349
/*

src/PerfProblemSimulator/Models/LoadTestRequest.cs

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
* {
1212
* "workIterations": 1000,
1313
* "bufferSizeKb": 100,
14-
* "softLimit": 10,
15-
* "degradationFactor": 50
14+
* "baselineDelayMs": 500,
15+
* "softLimit": 5,
16+
* "degradationFactor": 200
1617
* }
1718
*
1819
* OR with defaults (empty body or null):
@@ -56,19 +57,26 @@ namespace PerfProblemSimulator.Models;
5657
/// <term>softLimit</term>
5758
/// <description>
5859
/// Concurrent requests before degradation starts. Lower = earlier degradation.
59-
/// Tune based on expected normal load. 10 is aggressive for testing timeouts.
60+
/// Default of 5 ensures rapid escalation under load.
6061
/// </description>
6162
/// </item>
6263
/// <item>
6364
/// <term>degradationFactor</term>
6465
/// <description>
6566
/// Milliseconds of delay added per request OVER the soft limit.
66-
/// Higher = steeper degradation curve. 50ms is aggressive.
67+
/// Default of 200ms creates steep degradation curve.
6768
///
68-
/// Example: softLimit=10, degradationFactor=50
69-
/// - 20 concurrent: (20-10) * 50 = 500ms added delay
70-
/// - 50 concurrent: (50-10) * 50 = 2000ms added delay
71-
/// - 100 concurrent: (100-10) * 50 = 4500ms added delay
69+
/// Example: softLimit=5, degradationFactor=200
70+
/// - 10 concurrent: (10-5) * 200 = 1000ms added delay
71+
/// - 20 concurrent: (20-5) * 200 = 3000ms added delay
72+
/// - 50 concurrent: (50-5) * 200 = 9000ms added delay
73+
/// </description>
74+
/// </item>
75+
/// <item>
76+
/// <term>baselineDelayMs</term>
77+
/// <description>
78+
/// Minimum blocking delay for every request. Default 500ms ensures
79+
/// thread pool exhaustion under any significant load.
7280
/// </description>
7381
/// </item>
7482
/// </list>
@@ -134,7 +142,7 @@ public class LoadTestRequest
134142
/// </summary>
135143
/// <remarks>
136144
/// <para>
137-
/// <strong>DEFAULT: 10</strong>
145+
/// <strong>DEFAULT: 5</strong>
138146
/// </para>
139147
/// <para>
140148
/// When concurrent requests exceed this limit, additional delay is
@@ -154,14 +162,14 @@ public class LoadTestRequest
154162
/// </list>
155163
/// </para>
156164
/// </remarks>
157-
public int SoftLimit { get; set; } = 10;
165+
public int SoftLimit { get; set; } = 5;
158166

159167
/// <summary>
160168
/// Milliseconds of delay added per concurrent request over the soft limit.
161169
/// </summary>
162170
/// <remarks>
163171
/// <para>
164-
/// <strong>DEFAULT: 50 ms</strong>
172+
/// <strong>DEFAULT: 200 ms</strong>
165173
/// </para>
166174
/// <para>
167175
/// <strong>DEGRADATION FORMULA:</strong>
@@ -170,25 +178,42 @@ public class LoadTestRequest
170178
/// </code>
171179
/// </para>
172180
/// <para>
173-
/// <strong>EXAMPLES (softLimit=10, degradationFactor=50):</strong>
181+
/// <strong>EXAMPLES (softLimit=5, degradationFactor=200):</strong>
174182
/// <list type="bullet">
175-
/// <item>5 concurrent → 0ms added (below soft limit)</item>
176-
/// <item>20 concurrent → 500ms added ((20-10) × 50)</item>
177-
/// <item>50 concurrent → 2000ms added ((50-10) × 50)</item>
178-
/// <item>100 concurrent → 4500ms added ((100-10) × 50)</item>
179-
/// <item>200 concurrent → 9500ms added ((200-10) × 50)</item>
183+
/// <item>5 concurrent → 0ms added (at soft limit)</item>
184+
/// <item>10 concurrent → 1000ms added ((10-5) × 200)</item>
185+
/// <item>20 concurrent → 3000ms added ((20-5) × 200)</item>
186+
/// <item>50 concurrent → 9000ms added ((50-5) × 200)</item>
187+
/// <item>100 concurrent → 19000ms added ((100-5) × 200)</item>
180188
/// </list>
181189
/// </para>
182190
/// <para>
183191
/// <strong>REACHING 230s TIMEOUT:</strong>
184192
/// To reach Azure's 230s timeout with these defaults:
185-
/// (230000ms - 100ms base) / 50ms = 4598 requests over soft limit
186-
/// So: 10 + 4598 = ~4608 concurrent requests
187-
///
188-
/// For lighter degradation, decrease degradationFactor:
189-
/// - degradationFactor=25: ~9200 concurrent requests to timeout
190-
/// - degradationFactor=10: ~23000 concurrent requests to timeout
193+
/// (230000ms - 500ms baseline) / 200ms = ~1147 requests over soft limit
194+
/// So: 5 + 1147 = ~1152 concurrent requests to timeout
195+
/// </para>
196+
/// </remarks>
197+
public int DegradationFactor { get; set; } = 200;
198+
199+
/// <summary>
200+
/// Minimum blocking delay applied to every request in milliseconds.
201+
/// </summary>
202+
/// <remarks>
203+
/// <para>
204+
/// <strong>DEFAULT: 500 ms</strong>
205+
/// </para>
206+
/// <para>
207+
/// This baseline delay is applied BEFORE the degradation calculation.
208+
/// It ensures every request blocks a thread for at least this duration,
209+
/// guaranteeing thread pool exhaustion under any significant load.
210+
/// </para>
211+
/// <para>
212+
/// Combined with degradation factor, total delay is:
213+
/// <code>
214+
/// totalDelay = baselineDelayMs + max(0, concurrent - softLimit) * degradationFactor
215+
/// </code>
191216
/// </para>
192217
/// </remarks>
193-
public int DegradationFactor { get; set; } = 50;
218+
public int BaselineDelayMs { get; set; } = 500;
194219
}

src/PerfProblemSimulator/Services/LoadTestService.cs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -445,28 +445,50 @@ public async Task<LoadTestResult> ExecuteWorkAsync(LoadTestRequest request, Canc
445445
try
446446
{
447447
/*
448-
* STEP 3: CALCULATE DEGRADATION DELAY
448+
* STEP 3A: APPLY BASELINE BLOCKING DELAY
449+
* =================================================================
450+
*
451+
* Every request blocks for at least baselineDelayMs, regardless of
452+
* concurrent request count. This GUARANTEES thread pool exhaustion
453+
* under any significant load.
454+
*
455+
* THREAD BLOCKING:
456+
* We use Thread.Sleep to BLOCK threads. At 500ms baseline with
457+
* high request rate, threads will exhaust rapidly.
458+
*/
459+
if (request.BaselineDelayMs > 0)
460+
{
461+
_logger.LogDebug("Applying baseline blocking delay: {Delay}ms", request.BaselineDelayMs);
462+
Thread.Sleep(request.BaselineDelayMs);
463+
degradationDelayApplied += request.BaselineDelayMs;
464+
465+
// Check for timeout exception after baseline delay
466+
CheckAndThrowTimeoutException(stopwatch);
467+
}
468+
469+
/*
470+
* STEP 3B: CALCULATE DEGRADATION DELAY
449471
* =================================================================
450472
*
451473
* FORMULA:
452474
* overLimit = max(0, currentConcurrent - softLimit)
453475
* delayMs = overLimit * degradationFactor
454476
*
455-
* EXAMPLES (with softLimit=50, degradationFactor=5):
456-
* - 30 concurrent → overLimit=0 → delay=0ms
457-
* - 50 concurrent → overLimit=0 → delay=0ms
458-
* - 60 concurrent → overLimit=10 → delay=50ms
459-
* - 100 concurrent → overLimit=50 → delay=250ms
460-
* - 200 concurrent → overLimit=150 → delay=750ms
477+
* EXAMPLES (with softLimit=5, degradationFactor=200):
478+
* - 5 concurrent → overLimit=0 → delay=0ms
479+
* - 10 concurrent → overLimit=5 → delay=1000ms
480+
* - 20 concurrent → overLimit=15 → delay=3000ms
481+
* - 50 concurrent → overLimit=45 → delay=9000ms
482+
* - 100 concurrent → overLimit=95 → delay=19000ms
461483
*
462-
* This creates a LINEAR degradation curve above the soft limit.
484+
* Combined with baseline 500ms, total delays become significant quickly.
463485
*/
464486
var overLimit = Math.Max(0, currentConcurrent - request.SoftLimit);
465487
var totalDegradationDelayMs = overLimit * request.DegradationFactor;
466488

467489
_logger.LogDebug(
468-
"Load test: Concurrent={Concurrent}, OverLimit={OverLimit}, DegradationDelay={Delay}ms",
469-
currentConcurrent, overLimit, totalDegradationDelayMs);
490+
"Load test: Concurrent={Concurrent}, OverLimit={OverLimit}, BaselineDelay={Baseline}ms, DegradationDelay={Delay}ms",
491+
currentConcurrent, overLimit, request.BaselineDelayMs, totalDegradationDelayMs);
470492

471493
/*
472494
* STEP 4: APPLY DEGRADATION DELAY (with exception checks)

0 commit comments

Comments
 (0)