Skip to content

Commit ebc898f

Browse files
committed
✨ Add performance testing files and update sitemap
- Introduced new performance testing files for spike tests. - Added images for better representation in the testing section. - Updated sitemap to reflect new content and structure.
1 parent da37630 commit ebc898f

File tree

6 files changed

+242
-0
lines changed

6 files changed

+242
-0
lines changed

TestArena/Blog/Common/NavigationUtils/SiteMap.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ public static class SiteMap
134134
new DateTime(2025, 8, 16),
135135
"images/blog/performance-testing/stress-test/banner.png",
136136
["Performance Testing", "K6", "API", ".NET", "Stress Test"]),
137+
new("Performance Testing for APIs: Spike testing with K6",
138+
"/blog/performance-testing/spike-test-with-k6",
139+
new DateTime(2025, 8, 23),
140+
"images/blog/performance-testing/spike-test/banner.png",
141+
["Performance Testing", "K6", "API", ".NET", "Spike Test"]),
137142
];
138143

139144
public static IEnumerable<PageInfo> PublishedArticles => Pages.Where(p => p.IsPublished && p.PublishedOn <= DateTime.UtcNow);
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
@page "/blog/performance-testing/spike-test-with-k6"
2+
@using Microsoft.AspNetCore.Components
3+
@using Microsoft.AspNetCore.Components.Web
4+
@using TestArena.Blog.Common
5+
@using TestArena.Blog.Common.NavigationUtils
6+
@using System.Text
7+
@using System.Collections.Generic
8+
9+
@code {
10+
PageInfo currentPage = SiteMap.Pages.FirstOrDefault(x => x.RelativePath == "/blog/performance-testing/spike-test-with-k6")!;
11+
12+
List<RenderFragment> spikeTestGoals = new List<RenderFragment>
13+
{
14+
builder => builder.AddContent(0, "Validate system behavior under sudden traffic surges"),
15+
builder => builder.AddContent(0, "Detect cascading failures caused by resource exhaustion"),
16+
builder => builder.AddContent(0, "Measure recovery time and data integrity after the spike"),
17+
builder => builder.AddContent(0, "Identify capacity bottlenecks that only appear under abrupt load")
18+
};
19+
20+
List<RenderFragment> whenToUseSpikeTest = new List<RenderFragment>
21+
{
22+
builder => builder.AddContent(0, "Before marketing campaigns or feature launches that can create viral traffic"),
23+
builder => builder.AddContent(0, "When you expect short-lived, very high-concurrency patterns (flash sales, viral content)"),
24+
builder => builder.AddContent(0, "To validate auto-scaling and circuit-breaker behavior under abrupt load"),
25+
builder => builder.AddContent(0, "When investigating intermittent production outages that coincide with traffic bursts")
26+
};
27+
}
28+
29+
<BlogContainer>
30+
<Header Title="@currentPage.Header" Image="@currentPage.ArticleImage" PublishedOn="@currentPage.PublishedOn" Authors="Ajay Kumar" Description="How to design and run spike tests with k6, plus real-world incidents that spike testing could have prevented" />
31+
32+
<Section Heading="🔦 Introduction" Level="5">
33+
<p>
34+
Picture this: a small community library's online reservation page is quiet all week, then a popular local author announces a surprise appearance. Within minutes, hundreds — then thousands — of visitors flood the site to reserve tickets. The system wasn’t built for this sudden surge and becomes unresponsive for the very people it was meant to serve.
35+
</p>
36+
<p>
37+
Unlike stress tests which gradually push a system to and beyond its limits, spike tests focus on sudden, very-short-term surges in traffic. These are common in real-world scenarios like flash sales, viral social posts, breaking news, or a single influencer driving traffic. Spike testing answers the critical question: "How does my system behave when traffic jumps abruptly?" — and just as importantly, "How quickly does it recover?"
38+
</p>
39+
<List Heading="Primary Goals of Spike Testing" HeadingLevel="5" ChildContents="@spikeTestGoals" />
40+
</Section>
41+
42+
<Section Heading="Why Spike Tests Matter (Real-World Lessons)" Level="5">
43+
<p>
44+
Many outages aren't caused by steady growth but by sudden spikes. Below are real-world incidents and the ways spike testing could have reduced their impact:
45+
</p>
46+
<ul>
47+
<li>
48+
<strong>Ticketing Site Overload (Hypothetical, inspired by common incidents):</strong> A concert promoter released tickets with a surprise drop. The site was fine under normal traffic but had synchronous locking and a database connection pool sized for routine load. When thousands of users hit 'buy' simultaneously, the pool exhausted, requests queued, and timeouts cascaded into sold-out errors and duplicate charges. A spike test would have revealed the connection pool and locking hotspots and allowed engineering to add queuing or optimistic concurrency.
49+
</li>
50+
<li>
51+
<strong>News Publisher Cache Stampede:</strong> A breaking headline went viral and cache keys expired simultaneously, causing a thundering herd of requests to backend services. The origin servers were overwhelmed. A spike test with cache expiry patterns would have shown this failure mode and encouraged implementing request coalescing, proactive cache warming, or longer TTLs for critical resources.
52+
</li>
53+
<li>
54+
<strong>E-commerce Flash Sale Failures:</strong> During a limited-time sale, shoppers experienced slow checkout and inventory mismatches because inventory writes couldn’t keep up with burst traffic, and fallback paths performed inconsistent updates. Spike testing would have revealed write contention and helped design idempotent purchase flows and stronger inventory reservation mechanisms.
55+
</li>
56+
<li>
57+
<strong>API Rate-Limiter Misconfiguration:</strong> A public API had generous per-minute quotas but strict per-second limits. When a partner system retried aggressively after brief failures, it triggered per-second throttles and a severe outage. Spike tests exposing per-second behavior would have surfaced the configuration mismatch.
58+
</li>
59+
</ul>
60+
</Section>
61+
62+
<Section Heading="When to Use Spike Testing" Level="5">
63+
<p>
64+
Spike testing is essential in scenarios where traffic can jump instantaneously. Use it when:
65+
</p>
66+
<List Heading="Common Scenarios" HeadingLevel="5" ChildContents="@whenToUseSpikeTest" />
67+
68+
<CalloutBox Type="Tip">
69+
If a single event can change your traffic profile in under a minute, run a spike test that reflects the worst realistic sudden jump.
70+
</CalloutBox>
71+
</Section>
72+
73+
<Section Heading="Designing a Spike Test with k6" Level="5">
74+
<p>
75+
The key elements of a spike test are a small baseline, a rapid ramp to a high target for a short duration, and an immediate recovery period to observe system behavior. We'll use the sample k6 script in our repository as a concrete example.
76+
</p>
77+
78+
<Section Heading="The Spike Test Script (k6)" Level="5">
79+
<CodeSnippet Description="K6 Spike Test Script" Language="javascript">
80+
import http from 'k6/http';
81+
import { check, sleep } from 'k6';
82+
import { Rate } from 'k6/metrics';
83+
import { htmlReport } from "https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js";
84+
import { textSummary } from "https://jslib.k6.io/k6-summary/0.0.1/index.js";
85+
86+
// Custom metric to track failure rate (for our checks)
87+
const failureRate = new Rate('check_failure_rate');
88+
89+
/*
90+
Spike test example:
91+
- Keep a small baseline of virtual users for a period
92+
- Suddenly spike to a very large number of users for a short time
93+
- Return quickly back to baseline and observe recovery
94+
95+
This demonstrates how the system behaves under a sudden surge of traffic.
96+
*/
97+
export const options = {
98+
stages: [
99+
// Baseline warm-up
100+
{ duration: '10s', target: 10 }, // Ramp up to 10 users
101+
{ duration: '30s', target: 10 }, // Steady baseline for 30 seconds
102+
103+
// Spike
104+
{ duration: '5s', target: 500 }, // Rapid ramp up to 500 users (spike)
105+
{ duration: '30s', target: 500 }, // Stay at spike load for 30 seconds
106+
107+
// Recovery back to baseline
108+
{ duration: '20s', target: 10 }, // Rapid ramp down to baseline
109+
{ duration: '30s', target: 10 }, // Let the system recover at baseline
110+
111+
// Cooldown to 0
112+
{ duration: '30s', target: 0 }, // Ramp down to 0 users
113+
],
114+
115+
// Thresholds tuned for spike (we allow some degradation during spike but keep limits reasonable)
116+
thresholds: {
117+
'http_req_duration': [
118+
'p(95)&lt;2000', // 95% of requests should be below 2s
119+
'p(99)&lt;5000', // 99% of requests should be below 5s during a big spike
120+
],
121+
'http_req_failed': ['rate&lt;0.10'], // Allow up to 10% failure rate during the spike
122+
'check_failure_rate': ['rate&lt;0.10'], // Same threshold for our custom check
123+
},
124+
};
125+
126+
export default function () {
127+
// Target URL - adjust if your API runs on a different host/port
128+
const url = 'http://localhost:5127/mostborrowedbook';
129+
130+
// Add a tag so metrics in k6 can be filtered/grouped by endpoint
131+
const response = http.get(url, { tags: { name: 'MostBorrowedBook' } });
132+
133+
// Basic checks to validate correctness and a soft latency guard
134+
const checks = check(response, {
135+
'status is 200': (r) => r.status === 200,
136+
'response time &lt; 2000ms': (r) => r.timings.duration &lt; 2000,
137+
'valid response body': (r) => r.body && r.body.length &gt; 0,
138+
});
139+
140+
// Record custom metric for later assertions
141+
failureRate.add(!checks);
142+
143+
// Helpful debug log when things fail — keep minimal to avoid log spam during large spikes
144+
if (!checks) {
145+
console.log(`Check failed. status=${response.status} duration=${response.timings.duration}`);
146+
}
147+
148+
// Short randomized sleep to avoid perfectly synchronized requests
149+
sleep(Math.random() * 1 + 0.5); // 0.5 - 1.5s
150+
}
151+
152+
// Produce an HTML report plus a console summary when the test finishes
153+
export function handleSummary(data) {
154+
return {
155+
"spike-test-results.html": htmlReport(data),
156+
stdout: textSummary(data, { indent: ' ', enableColors: true }),
157+
};
158+
}
159+
</CodeSnippet>
160+
161+
<Section Heading="Key Components Explained" Level="5">
162+
<p>
163+
Important parts to note:
164+
</p>
165+
<ol>
166+
<li><strong>Baseline:</strong> Keeps a small steady load so the system isn't entirely cold when the spike begins.</li>
167+
<li><strong>Rapid Ramp (Spike):</strong> A 5–10 second ramp to a very high VU count simulates real-world sudden surges.</li>
168+
<li><strong>Hold Duration:</strong> The spike is short (tens of seconds). This shows systems that survive short bursts versus those that collapse under brief pressure.</li>
169+
<li><strong>Recovery Observation:</strong> The immediate ramp-down and subsequent baseline period reveal whether the system recovers gracefully or remains degraded.</li>
170+
<li><strong>Checks &amp; Custom Metrics:</strong> Functional checks (status, response body) and a `check_failure_rate` metric help correlate functional correctness with performance metrics.</li>
171+
</ol>
172+
</Section>
173+
</Section>
174+
175+
<Section Heading="How to Run and Interpret Results" Level="5">
176+
<p>
177+
Run the spike test from the `api-performance-testing` directory:
178+
</p>
179+
<CodeSnippet Language="bash">
180+
k6 run k6-spike-test-demo.js
181+
</CodeSnippet>
182+
<p>
183+
After completion, inspect `spike-test-results.html` for a visual breakdown. Key signals to look for:
184+
</p>
185+
<BlogImage
186+
ImagePath="/images/blog/performance-testing/spike-test/spike-test-result.png"
187+
Description="K6 spike test HTML report screenshot showing latency and error timelines"
188+
Number="1" />
189+
<ul>
190+
<li><strong>Latency spikes:</strong> Large p95/p99 spikes during the ramp indicate resource saturation or queuing.</li>
191+
<li><strong>Error patterns:</strong> Sudden rises in `http_req_failed` point to throttling, exhausted connection pools, or upstream timeouts.</li>
192+
<li><strong>Recovery curve:</strong> How long does p95 take to return to baseline? Long recovery can indicate background tasks or caches rebuilding.</li>
193+
<li><strong>Functional failures:</strong> `check_failure_rate` shows correctness regressions — e.g., incomplete responses or incorrect data during the spike.</li>
194+
</ul>
195+
</Section>
196+
197+
<Section Heading="Mitigations and Practical Fixes" Level="5">
198+
<p>
199+
If a spike test reveals problems, here are practical mitigations to consider:
200+
</p>
201+
<ol>
202+
<li><strong>Connection Pooling &amp; Backpressure:</strong> Increase pool sizes where appropriate and implement backpressure/queueing to smooth sudden bursts to downstream services.</li>
203+
<li><strong>Resiliency Patterns:</strong> Circuit breakers, retries with jitter, and bulkheads prevent cascading failures during spikes.</li>
204+
<li><strong>Cache Strategy:</strong> Use cache warming, request coalescing, and staggered expirations to avoid a stampede on cache misses.</li>
205+
<li><strong>Idempotency &amp; Rate Limits:</strong> Make key operations idempotent and ensure rate-limit rules match real traffic patterns (per-second vs per-minute boundaries).</li>
206+
<li><strong>Autoscaling Tuning:</strong> Ensure autoscaling reacts fast enough for expected spike durations or adopt pre-warming strategies when spikes are predictable.</li>
207+
</ol>
208+
</Section>
209+
210+
<Section Heading="Summary and Next Steps" Level="5">
211+
<p>
212+
Spike tests target a specific, high-risk scenario: abrupt traffic surges. They reveal different failure modes than gradual stress tests — namely race conditions, cache stampedes, connection pool exhaustion, and misconfigured rate-limits. By rehearsing these scenarios in a controlled environment, teams can harden systems and design better fallback behavior.
213+
</p>
214+
<p>
215+
Next steps:
216+
</p>
217+
<ol>
218+
<li>Run the provided `k6` spike script against a staging environment that mirrors production.</li>
219+
<li>Iterate on infrastructure fixes, then re-run to verify improvements.</li>
220+
<li>Automate periodic spike tests for critical endpoints ahead of known events (drops, launches).</li>
221+
</ol>
222+
</Section>
223+
</Section>
224+
225+
<Section Heading="Resources" Level="5">
226+
<ul>
227+
<li><a href="https://k6.io/docs/">k6 Documentation</a></li>
228+
<li>Repository: <a href="https://github.com/ajaysskumar/API-Performance-Testing" target="_blank">ajaysskumar/API-Performance-Testing</a> (contains `k6-spike-test-demo.js`)</li>
229+
</ul>
230+
</Section>
231+
232+
</BlogContainer>
247 KB
Loading
1.25 MB
Loading
598 KB
Loading

TestArena/wwwroot/sitemap.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,9 @@
115115
<lastmod>2025-08-17</lastmod>
116116
<changefreq>monthly</changefreq>
117117
</url>
118+
<url>
119+
<loc>https://devcodex.in/blog/performance-testing/spike-test-with-k6</loc>
120+
<lastmod>2025-08-24</lastmod>
121+
<changefreq>monthly</changefreq>
122+
</url>
118123
</urlset>

0 commit comments

Comments
 (0)