Load test summary: so-gasing.id /play/

15 October 2026, 14:45 to 15:46 WIB, against production (https://so-gasing.id)

Open the full JMeter report

Virtual visitors
120
for 1 hour
Requests sent
136,768
about 37 per second
Page, version, kabupaten
< 0.1%
failed
Leaderboard
53%
failed

The short version

The page itself held up well. The HTML page, /version.json and the kabupaten list stayed fast and almost never failed during the whole hour.

The leaderboard API did not. About half of all leaderboard requests failed, and for roughly 13 minutes it was completely down. Because the leaderboard is part of every page load, more than half of all simulated visits ended with a broken leaderboard.

It was already failing in the first minute, when only 3 to 8 visitors were active. So this is not only a "too much traffic" problem. The endpoint is slow even under light load and gets worse from there.

How the test was set up

SettingValue
Test started15 October 2026, 14:45:45 WIB (Asia/Jakarta, UTC+7)
Test ended15 October 2026, 15:46:45 WIB (Asia/Jakarta, UTC+7)
Virtual visitors (threads)120
Ramp up10 minutes (0 to 120 visitors)
Total length1 hour
Pause between visits2 to 5 seconds, random
Load generatorOne MacBook (M1 Pro), JMeter 5.6.3 in Podman

120 threads came from the projection of 600,000 registered users. Using the defaults in load-calc.py (20% active per day, 30% of those in the busiest hour, 20 minute sessions), that gives about 12,000 students online at the same time, or around 30 page loads per second. Each virtual visitor reloads /play/ every few seconds, much more often than a real student, so 120 of them are enough to create that traffic.

Active threads over time: a straight climb from 0 to 120 in the first 10 minutes, then flat at 120 until the end
Active threads over time. Visitors were added steadily for 10 minutes, then 120 stayed active until the end. Times in the report charts are UTC, so 07:45 here is 14:45 WIB. Open in report

Which endpoints we tested and why

Every virtual visitor repeats what a real browser does when a student opens https://so-gasing.id/play/. The page makes three API calls by itself every time it opens, so we included all of them. If we only tested the HTML, the server would see about a quarter of the real number of requests.

GET /play/

The HTML page itself, about 230 KB compressed. Cloudflare does not cache it, so every visit reaches our server. This is the main thing a student waits for.

GET /version.json

A small file the page checks on load to know which app version is live. Cheap for the server, but it is one more request per visit, so it adds to the total request count.

GET /api/leaderboard?scope=Nasional&metric=points&page=1&limit=50

The national top 50 ranking shown on the page. This is the heaviest call because the server has to sort students by points. It is the most likely place for the database to struggle, which is exactly what the test found.

GET /api/schools/all_kabupatens.json

The list of regions used on the page. It goes through /api, so it may touch the backend, and it is requested on every visit.

All four requests are wrapped in one transaction called "Open /play/ (full page load)", so the report also shows the total time a visitor waits.

What we left out on purpose: anything that writes data (saving progress, profile updates, checkout) or needs a login, so the test did not create junk data in production. JMeter does not run JavaScript, so the game itself was not played. Images, scripts and other static files were skipped because Cloudflare serves those from its cache and they would mostly measure Cloudflare, not our server.

Results per endpoint

EndpointRequestsFailedMedian95th pct
GET /play/34,2360.07%431 ms1.5 s
GET /version.json34,2160.00%210 ms1.1 s
GET /api/schools/all_kabupatens.json34,1190.10%178 ms0.5 s
GET /api/leaderboard34,19753%1.6 s12.3 s
Full page load (all four)34,11953%3.1 s13.2 s

"Median" is the typical time. "95th pct" means 95 out of 100 requests were faster than this number, so it shows what the slower visitors experience.

The test downloaded about 2.2 MB per second (around 18 Mbps). That is well within what one laptop and a normal internet connection can handle, so the errors come from the server side and not from the test machine.

JMeter statistics table showing sample counts, error rates and response time percentiles for each request
The statistics table from the report. The leaderboard row is the only one with a high error rate, and it drags the full page load down with it. Open in report

What went wrong with the leaderboard

ErrorCountWhat it likely means
500 Internal Server Error10,931The app crashed or gave up. Most came back after about 10 seconds, which looks like a timeout inside the app or the database query.
429 Too Many Requests4,361A rate limit kicked in. Only the leaderboard got these, so there is a limit set specifically on this endpoint.
Timed out after 60 seconds1,774The server never answered at all.
502 Bad Gateway1,085The proxy in front of the app could not reach it, usually because the app was overloaded or restarting.
JMeter errors table: 500 errors are 60% of all errors, 429 are 24%, timeouts 10%, 502 6%
All errors in the run, from the report. Almost every one of them belongs to the leaderboard. Open in report

How it played out over the hour

Response times over time: the leaderboard and full page load lines jump to about 40 seconds between 07:51 and 08:04 UTC, while the other endpoints stay near zero
Average response time per minute. The light blue (leaderboard) and green (full page load) lines are the only ones far above zero. The flat plateau around 40 seconds between 07:51 and 08:04 UTC (14:51 to 15:04 WIB) is the outage. The other three endpoints stay flat along the bottom the whole time. Open in report
Response codes per second: successful 200 responses stay low around 8 per second during the outage, then rise to 50 to 70 per second; 500 and 429 errors continue at a few per second throughout
Responses per second by status code. During the outage, successful responses (yellow, 200) drop to about 8 per second because visitors are stuck waiting on the leaderboard. After 08:05 UTC they recover, but 500 (red) and 429 (brown) keep coming the whole time. Open in report

What this means

Files in this folder

FileWhat it is
report/index.htmlFull interactive JMeter dashboard
SUMMARY.mdThis summary as Markdown
screenshots/Chart and table screenshots taken from the report
results.jtlRaw data, one line per request
jmeter.logJMeter's own log