Load test results in moodle 45 (feb 2026)

Versión 2 (Emilio Penna, Jueves, 19 de Marzo de 2026 10:19:18 -0300)

1 1
2 1
h1. Moodle Quiz Load Test Results — Moodle 4.5 (March 2026)
3 1
4 1
This page documents load test results for Moodle 4.5 quiz under concurrent exam conditions.
5 1
The goal is to share infrastructure sizing experience that may be useful for other institutions
6 1
running large-scale simultaneous online exams.
7 1
8 1
For the test procedure and files (JMeter script, quiz backup, users file), see:
9 1
[[Load_tests_in_moodle_41_(update_2025)]]
10 1
11 1
---
12 1
13 1
h2. Summary
14 1
15 1
We tested a Moodle 4.5 deployment with 2000 and 3000 simultaneous virtual users (VUs) completing
16 1
a 16-question quiz. The system handled 2000 VUs comfortably with no errors and good response times.
17 1
At 3000 VUs, the only point of stress was the synchronized exam start (startattempt spike);
18 1
all subsequent quiz navigation remained stable even at that load.
19 1
20 1
The main finding is that *CPU is the actual bottleneck* in this architecture, not RAM.
21 1
With PHP-FPM + OPcache + a separated database server, memory pressure was minimal even at peak load.
22 1
This challenges the common Moodle documentation recommendation that frames RAM as the primary
23 1
performance lever — that guidance appears to be a legacy of older mod_php architectures without OPcache.
24 1
25 1
We also tested Apache mod_cache on the reverse proxy. It reduced latency for static assets and
26 1
pre-exam navigation (10–35% improvement), but had a paradoxical effect on the startattempt spike:
27 1
pages load faster, so users arrive at the synchronization point more tightly clustered,
28 1
slightly increasing peak pressure at exam start.
29 1
30 1
---
31 1
32 1
h2. Infrastructure
33 1
34 1
|_.Component|_.Details|
35 1
|Reverse proxy|Apache 2.4 (MPM event), mod_shib, mod_cache, mod_remoteip|
36 1
|Web / app server|Apache 2.4 + PHP-FPM 8.3, Debian 13, *12 vCPU / 12 GB RAM*|
37 1
|Database server|MySQL Community 8.4, Debian 13, *4 vCPU / 16 GB RAM*|
38 1
|PHP acceleration|OPcache enabled. Moodle Universal Cache (MUC): file cache (default)|
39 1
|PHP-FPM pool|pm = dynamic, pm.max_children = 90|
40 1
|Virtualization|VMware (VMs on shared physical hardware — introduces run-to-run variability)|
41 1
|Storage|SAN/shared storage with SSD tier|
42 1
|Monitoring|Custom shell script (CSV, 10s interval) + Percona PMM (DB) + nmon (DB server)|
43 1
44 1
---
45 1
46 1
h2. Test Quiz Characteristics
47 1
48 1
|_.Parameter|_.Value|
49 1
|Moodle version|4.5|
50 1
|Quiz pages|8 pages, 2 questions per page (16 questions total)|
51 1
|Question types|12 multiple choice, 2 true/false, 1 matching, 1 short answer (free text)|
52 1
|Images in questions|4 questions with small images (< 50 KB each)|
53 1
|Time limit|None|
54 1
|Navigation|Sequential (one page at a time)|
55 1
56 1
---
57 1
58 1
h2. Load Test Configuration
59 1
60 1
|_.Parameter|_.Value|
61 1
|Tool|Apache JMeter 5.6.3|
62 1
|JMeter machine|Dedicated, 32 GB RAM / 8 CPU, @-Xms8g -Xmx8g -XX:+UseG1GC@|
63 1
|Virtual users (VUs)|2000 / 2500 / 3000|
64 1
|Ramp-up|180 seconds|
65 1
|Think time|Random per thread, uniform distribution 30–90 s|
66 1
|Exam start simulation|Synchronizing Timer (all VUs held before startattempt, then released together)|
67 2 Emilio Penna
|Post-start timer|Gaussian Random Timer, ? = 30 s, offset = 5 s|
68 1
|Approximate test duration|~30 minutes per run|
69 1
70 1
The Synchronizing Timer models the real-world scenario where all students click "Start attempt"
71 1
within a short window at the beginning of a scheduled exam. This is the most demanding moment
72 1
for the infrastructure.
73 1
74 1
---
75 1
76 1
h2. Results
77 1
78 1
h3. 2000 VUs — Stable, comfortable headroom
79 1
80 1
Two runs in identical conditions confirmed high reproducibility (TOTAL p95 differed by only 2 ms
81 1
between runs). Key figures:
82 1
83 1
|_.Metric|_.Value|
84 1
|TOTAL p95|~408 ms|
85 1
|TOTAL p99|~503 ms|
86 1
|startattempt p95 (controller)|~924 ms (avg of both runs)|
87 1
|Quiz pages p95 (pages 1–8)|410–490 ms|
88 1
|Errors|< 0.002% (effectively zero)|
89 2 Emilio Penna
|CPU load1 peak (12 vCPU)|~7.5 (? 63% saturation)|
90 1
|PHP-FPM busy peak|10–20 workers / 90 configured|
91 1
|RAM peak|~1.6 GB used / 12 GB available|
92 1
|Swap|0 at all times|
93 1
94 1
The startattempt spike lasted approximately 2 minutes, after which load dropped steadily.
95 1
All quiz navigation after exam start was unaffected.
96 1
97 1
h3. 3000 VUs — Acceptable, with a localized spike at exam start
98 1
99 1
|_.Metric|_.Value|
100 1
|TOTAL p95|423 ms|
101 1
|TOTAL p99|859 ms (driven by startattempt outliers)|
102 1
|startattempt p95 (controller)|2689 ms|
103 1
|Quiz pages p95 (pages 1–8)|399–483 ms|
104 1
|Errors|0.033% — all on startattempt endpoint|
105 2 Emilio Penna
|CPU load1 peak (12 vCPU)|22.4 (? 187% — queue forming)|
106 1
|PHP-FPM busy peak|77 workers / 90 configured|
107 1
|RAM peak|~2.2 GB used / 12 GB available|
108 1
|Swap|0 at all times|
109 1
110 1
Pages 1–8 response times at 3000 VUs were virtually identical to the 2000 VU runs.
111 1
The system degraded *only* at the synchronized exam start, not during the exam itself.
112 1
113 1
h3. Effect of mod_cache on the reverse proxy (2000 VUs)
114 1
115 1
Tested with a whitelist-based mod_cache configuration on the reverse proxy caching static assets,
116 1
requirejs bundles, and shared pluginfile.php resources.
117 1
118 1
|_.Endpoint|_.p95 without cache|_.p95 with cache|_.Delta|
119 1
|get / (homepage)|215 ms|140 ms|*-75 ms*|
120 1
|POST /login|272 ms|244 ms|-28 ms|
121 1
|course/view.php|139 ms|124 ms|-15 ms|
122 1
|Quiz pages (avg pages 1–8)|435 ms|415 ms|*-20 ms*|
123 2 Emilio Penna
|startattempt (controller)|775 ms|1023 ms|*+248 ms* ?|
124 1
|Errors|0.002%|0.000%|—|
125 1
126 1
The cache improved all static and navigation endpoints. The startattempt controller worsened
127 1
because mod_cache accelerated pre-exam page delivery, causing VUs to arrive at the
128 1
synchronization point more tightly clustered, amplifying the spike.
129 1
In real exams with natural student dispersion this effect would be attenuated.
130 1
131 1
---
132 1
133 1
h2. Key Findings
134 1
135 1
h3. CPU is the bottleneck, not RAM
136 1
137 1
With PHP-FPM + OPcache + a separated database server, the memory footprint per worker is low
138 1
(~60–70 MB private RSS; shared OPcache loaded once). At 2000 VUs peak, only ~260 MB of additional
139 1
RAM was consumed. CPU was consistently the limiting resource.
140 1
141 1
This is relevant because Moodle's official documentation historically emphasizes RAM as the
142 1
primary sizing parameter. That guidance reflects mod_php architectures without OPcache,
143 1
where each Apache process carries a full PHP interpreter in memory. With PHP-FPM and OPcache,
144 1
the architecture is fundamentally different.
145 1
146 1
h3. The startattempt spike is the sole critical point
147 1
148 1
All quiz page navigation (pages 1–8, finish) remained stable across all load levels.
149 1
The only metric that degraded significantly with increased VUs was the synchronized exam start.
150 1
Any optimization that reduces the startattempt spike (staggered start, exam scheduling,
151 1
reduced synchronization) has disproportionate impact on perceived performance.
152 1
153 1
h3. The database was not a bottleneck
154 1
155 1
At 2000 VUs, MySQL operated almost entirely from the InnoDB buffer pool (zero read I/O observed
156 1
via nmon). Write I/O peaked at ~6.5 MB/s during the startattempt spike (disk ~98% busy),
157 1
but iowait on the DB server remained below 4%, indicating writes did not block query execution.
158 1
DB server CPU peaked at 70% during the spike and recovered quickly.
159 1
160 1
The 16 GB RAM on the DB server allowed the working set to remain in the buffer pool throughout
161 1
the test — a key factor for this result.
162 1
163 1
h3. Reproducibility and VMware variability
164 1
165 1
Running on shared VMware infrastructure introduces run-to-run variability. We ran at least two
166 1
identical runs at each load level to distinguish real behavior from hypervisor noise.
167 1
At 2000 VUs the results were highly consistent (TOTAL p95 ±2 ms). Some variability was observed
168 1
in the startattempt spike (p95 range: 775–1072 ms across two runs), which is expected given
169 1
its sensitivity to exact CPU availability at the moment of the synchronized burst.
170 1
171 1
---
172 1
173 1
h2. Practical Sizing Reference
174 1
175 1
Based on these results, for this infrastructure profile:
176 1
177 1
|_.Concurrent students|_.Assessment|
178 1
|Up to ~1600|Comfortable headroom. CPU peak well under 70%, PHP-FPM pool far from saturation.|
179 1
|~2000|Supported with margin. Startattempt spike reaches ~65% CPU for ~2 minutes. No errors.|
180 1
|~2500|Under evaluation (results pending).|
181 1
|~3000|Functional but at the limit of the startattempt spike. 0.03% errors at exam start, CPU queuing during spike. All subsequent navigation unaffected.|
182 1
183 1
*Note:* these figures assume a fully synchronized exam start (worst case).
184 1
Real exams where students enter over 2–5 minutes will perform significantly better at any load level.
185 1
186 1
---
187 1
188 1
h2. Monitoring Setup
189 1
190 1
The following metrics were captured at 10-second intervals via a shell script during each run:
191 1
192 1
* @load average@ (1, 5, 15 min) — primary CPU saturation indicator
193 1
* RAM used and swap
194 1
* Apache processes
195 1
* PHP-FPM: total workers, busy, idle
196 1
* HTTPS connections (port 443)
197 1
* MySQL TCP connections
198 1
199 1
The DB server was monitored independently with @nmon@ (5-second interval), capturing CPU per core,
200 1
memory, disk I/O (read/write KB/s, %busy), and network throughput.
201 1
Percona PMM was used for MySQL-level metrics.
202 1
203 1
---
204 1
205 1
h2. References and Resources
206 1
207 1
* Test procedure and files (JMeter script, quiz backup, users CSV): [[Load_tests_in_moodle_41_(update_2025)]]
208 1
* Moodle documentation on performance: https://docs.moodle.org/en/Performance_recommendations
209 1
* PHP-FPM process manager configuration: https://www.php.net/manual/en/install.fpm.configuration.php
210 1
* Apache mod_cache documentation: https://httpd.apache.org/docs/2.4/mod/mod_cache.html
211 1
212 1
_Results contributed by SECIU — Universidad de la República (Uruguay), March 2026._
213 1
_Infrastructure: eva-perf.seciu.edu.uy (test environment)._