High-Concurrency Quizzes in Moodle: Engineering Exam-Day Success in 2026
When a cohort of 600 students clicks "Start attempt" at the same time, Moodle's quiz module becomes the benchmark for the stress test that your infrastructure will ever see. Each click creates simultaneous database writes, session locks, and autosave events. Without any prior preparation, those requests pile up, learners see spinning wheels, and academic integrity suffers.
This guide maps out the technical and operational playbook to keep high-stakes exams calm and proven.
1. Understand the Load Profile of Quizzes
Quiz traffic behaves fundamentally differently from normal course browsing. You need to understand its characteristics to architect correctly.
Why Quizzes Are 30% More Resource-Intensive
- Every page change writes data: Question state, timing records, attempt logs, and autosave snapshots all hit the database.
- Session handling multiplies: Each quiz navigation requires session read-write-lock cycles that stack up fast.
- Synchronous activity is the killer: Simultaneous quiz starts or last-minute submissions create spikes in requests 10-20x higher than daily averages.
- Database contention escalates: Quiz attempts generate frequent UPDATE queries that block each other, especially on shared database hosts.
Real Numbers: What 300 Quiz Takers Actually Generate
| Parameter | Value |
|---|---|
| Students | 300 |
| Questions per page | 5 (10 page navigations total) |
| Exam duration | 90 minutes |
| Autosave interval | 120 seconds -> 45 saves per student |
| Total requests | 300 x (10 nav + 45 saves + 1 submit) = 16,800 |
Peak Distribution:
| Moment | Load |
|---|---|
| First 60 seconds (quiz starts) | ~5 req/sec spike |
| Last 5 minutes (submission rush) | 8-12 req/sec |
| Mid-exam steady state (autosaves) | 2-3 req/sec |
The Session Bottleneck: Why a Fast-Memory-Based Session Store Is Mandatory
By default, Moodle stores sessions in the database (mdl_sessions table). During quizzes, every page load:
- Reads session (to get user context)
- Processes request (renders page, logs attempt data)
- Writes session (updates last access time)
- Locks session (prevents race conditions)
With 100 simultaneous quiz takers on database sessions, you get 100+ concurrent locks on the same table. This creates cascading delays where requests wait for the lock release.
A fast-memory-based session store (e.g., Redis) cuts this load by up to 5x because:
- In-memory operations complete in microseconds vs. milliseconds
- Lock management is optimized for high concurrency
- No disk I/O bottleneck on session reads/writes
- Database freed to handle quiz-specific queries only
Benchmark note: the 5x figure is an internal MooDIY load-test observation from database-session versus Redis-session quiz runs. Actual gains depend on question layout, database sizing, PHP-FPM limits, and cache configuration, so load test your real quiz before relying on a capacity target.
It is not recommended to use file-system or database storage mechanisms for session handling in production environments with concurrent users, as they introduce disk I/O and locking contention that can significantly degrade performance under load.
Rule of thumb: For above 100 simultaneous quiz takers, a fast-memory-based session store (e.g., Redis) should be considered mandatory, not optional.
2. Architect for Peak Moments, Not Weekday Averages
Most Moodle sizing guides focus on "registered users" or "daily logins" -- the wrong metrics for quiz scenarios. Size for the highest concurrency event you will ever run.
Infrastructure Sizing Guidelines by Concurrent Examinees
| Peak Concurrent Examinees | Minimum Topology | Critical Specs |
|---|---|---|
| ~100 | Single node + Redis | 2 vCPU / 4 GB RAM / NVMe SSD / Redis on the same server |
| ~300 | Separate Redis tier | 4 vCPU / 8 GB RAM web + Redis instance |
| ~500 | Dedicated database node | 4 vCPU web + 4 vCPU / 16 GB DB + Redis |
| ~1,000 | Load-balanced web cluster | 2-3 web nodes + beefy DB (8 vCPU / 32 GB) + Redis cluster |
| 2,000+ | Horizontal web farm + multi-AZ DB | Multi-node PHP, Redis cluster, read replicas, NVMe, 64-128 GB RAM DB |
Key Hardware Considerations
- CPU: Quiz processing is CPU-intensive (PHP execution, encryption, calculations). Prioritize high clock speeds (3.0+ GHz) over core count for small deployments. For 500+ concurrent, scale horizontally with more web nodes.
- RAM: Database buffer pool is critical. For 500 concurrent examinees, provision at least 16 GB RAM for MariaDB/MySQL with 80% allocated to
innodb_buffer_pool_size. For 1,000+, scale to 32-64 GB. - Storage: Fast SSD storage is non-negotiable. Quiz attempts generate thousands of small writes per minute. SATA SSDs can bottleneck under sustained random I/O; modern NVMe-backed volumes can handle tens of thousands of IOPS when provisioned correctly.
- PHP-FPM Workers: Configure static mode with enough workers to absorb bursts without overcommitting RAM.
3. Tune Quiz Configuration for Concurrency
Questions Per Page: 3-6 Is the Sweet Spot
Displaying all questions on one page creates:
- A single massive database query (2-5 seconds)
- A large HTML payload
- A single massive autosave (with a timeout risk)
- No load distribution over time
Splitting into 3-6 questions per page spreads write operations, reduces per-request processing from ~3s to ~0.4s, distributes load naturally, and enables faster autosave cycles.
Setting: Quiz settings -> Layout -> New page: Every 5 questions
Moodle 5.0 introduced cross-course question sharing, enabling multiple courses to use a centralized question bank, which simplifies management but can increase query load on larger datasets -- so database buffer pools should be sized appropriately when using shared banks at scale. Another notable addition is the pre-create quiz capability, which allows quiz structures and resources to be generated in advance, reducing on-demand processing during peak start times and improving stability for large, synchronized assessments.
Extend Autosave Intervals Under Load
Moodle's default 120-second autosave is great for usability but punishing under high concurrency.
| Concurrent Quiz Takers | Recommended Autosave Interval |
|---|---|
| ~100 | 120 seconds |
| 300+ | 180-300 seconds |
| 1,000+ | 300 seconds + instruct students to use "Next" to save manually |
How to change: Site administration > Plugins > Activity modules > Quiz > Default values >
autosaveperiod
Stagger Start Times to Avoid Thundering Herds
The problem: 600 students clicking "Start attempt" at 9:00:00 sharp creates a 600-request spike in 10 seconds.
| Option | Mechanism | Notes |
|---|---|---|
| Delayed Start plugin | Random 0-300 second countdown before each student can start | Students see a timer; smooths the spike curve |
| Cohort-based scheduling | A-F: 9:00 / G-M: 9:15 / N-S: 9:30 / T-Z: 9:45 | Each cohort gets full time window; predictable load |
| Flexible time windows | Open 9:00 AM-12:00 PM; complete within 90 min once started | Natural self-distribution; most operationally simple |
Even 5-minute cohort offsets smooth the curve dramatically.
Pre-Warm Caches Before Exam Start
Run 15-30 minutes before the exam window to avoid cold-cache database penalties on first requests. Ensure cron ran in the last hour, load the quiz page as admin to populate OPcache, and test Redis availability.
#!/bin/bash
# Run 30 minutes before exam window
curl -s https://yourmoodle.com/course/view.php?id=15 > /dev/null
curl -s https://yourmoodle.com/mod/quiz/view.php?id=203 > /dev/null
echo "SELECT COUNT(*) FROM mdl_user" | mysql moodle_db
redis-cli ping4. Load Test Before High-Stakes Exams
Never run a high-stakes exam without load testing first. This is non-negotiable for 300+ concurrent examinees.
Step 1: Build a Realistic Test Environment
- Clone production to staging with a realistic course structure
- Generate test users: At least 80% of expected concurrent load (e.g., 400 users for a 500-student exam)
- Populate quiz with actual questions: Question types matter (calculated questions are heavier)
- Match network conditions: Don't test from the same datacenter; simulate real latency
Step 2: Create Load Test Script
Moodle provides a JMeter test plan generator:
php admin/tool/generator/cli/maketestplan.php \
--site=https://staging.yourmoodle.com \
--users=500 \
--logins=500 \
--quitestarts=500 \
--quizanswers=yes \
--output=/tmp/moodle_quiz_load_test.jmxThis generates an Apache JMeter plan that simulates:
- 500 user logins
- 500 quiz starts
- Realistic page navigation timing
- Autosave events
- Quiz submissions
Step 3: Run Tests and Monitor Key Metrics
Execute load test with 80-100% of the expected concurrent load. Monitor in real-time:
| Category | Key Metrics | Success Thresholds |
|---|---|---|
| User Experience | Response Time (P95), Error Rate, PHP-FPM Worker Status | < 2s latency, 0% errors, No queue build-up |
| Data Pipeline | DB Buffer Pool Hit Rate, Slow Queries, Redis Latency | > 99% Hit Rate, Queries < 1s, Redis < 1ms |
| Infrastructure | CPU, RAM (Swapping), Disk I/O Wait, Network | < 70% CPU, < 5% Disk Wait, < 60% Bandwidth |
Step 4: Identify and Fix Bottlenecks
| Bottleneck | Immediate Action (Vertical Scaling) | Architectural Fix (Horizontal Scaling) |
|---|---|---|
| Application Stall (High PHP Queue) | Increase pm.max_children / Enable code profiling | Add web nodes behind a Load Balancer |
| Database/Cache Lag (Slow Queries / Redis Full) | Index mdl_question_attempts / Increase innodb_buffer_pool_size / Set Redis to allkeys-lru | Deploy a Read Replica; scale to a Redis Cluster for sessions >16 GB |
| Hardware I/O Cap (High Disk Wait) | Move mdl_sessions to RAM disk (tmpfs) | Upgrade to NVMe; decouple DB and Web storage to separate volumes |
Step 5: Repeat Until You Have Headroom
Don't aim for 100% capacity usage. Target 60-70% utilization during load test. This gives you:
- Buffer for unexpected spikes
- Room for autosave spikes
- Degradation grace period
If load tests hit 85% CPU or 95% PHP-FPM utilization, you are effectively undersized for production -- scale up resources before exam day to prevent a total lockout.
5. Real Exam-Day Case Studies
Case Study 1: 600 Students, Final Exam, Before Optimization
Scenario: Regional university, 600-student introductory course. Final exam, 60 questions, 2-hour window, 9:00 AM start. Infrastructure: Single 4 vCPU / 8 GB server, database sessions, 1 question per page.
| Time | Event |
|---|---|
| 9:00 AM | 412 students started within the first 90 seconds |
| 9:02 AM | Response times spiked to 8-12 seconds |
| 9:04 AM | PHP-FPM queue backed up to 140 requests |
| 9:05 AM | Students report "page not loading"; panic emails started |
| 9:08 AM | Admin restarts PHP-FPM -- 30-second outage |
| 9:15 AM | Stabilised at 4-6 second response times (barely usable) |
| 10:45 AM | Submission deadline rush crashed the site for 3 minutes |
Outcome: 30-minute exam extension due to system instability.
Root causes:
- Database session lock contention under 400+ concurrent users
- 1-question pages x 60 = 24,000+ writes
- Autosave at 2-minute intervals
- Infrastructure sized for registered users, not concurrent quiz takers
- No load testing performed
Case Study 2: 600 Students, Final Exam, After Optimization
Same institution, next semester.
Changes made:
- Migrated to Redis sessions
- Increased to 5 questions per page (60 questions = 12 pages)
- Extended autosave to 180 seconds
- Implemented cohort-based start times (4 groups, 15-minute stagger)
- Added 4 vCPU / 16 GB dedicated database server
- Load tested with 500 concurrent users, optimized based on findings
- Pre-warmed caches 30 minutes before the exam
Results:
| Metric | Result |
|---|---|
| Average response time | 1.1 seconds |
| 95th percentile response | 2.3 seconds |
| Peak CPU | 68% |
| Peak PHP-FPM workers active | 62/100 |
| Database queries/sec (peak) | 840 |
| Redis hit rate | 99.4% |
Outcome: Flawless exam delivery, 98.7% student satisfaction (post-exam survey), zero technical support tickets. Additional infrastructure cost: ~$85/month.
6. Alert Thresholds for Immediate Action
| Metric | Warning Threshold | Critical Threshold | Action |
|---|---|---|---|
| PHP-FPM queue | >20 requests | >50 requests | Scale workers or add a web node |
| Response time | >3 seconds | >5 seconds | Investigate slow queries, check resources |
| CPU usage | >75% sustained | >85% sustained | Add web node capacity |
| Database connections | >70% of max | >90% of max | Increase max_connections or find connection leaks |
| Redis memory | >85% used | >95% used | Clear non-essential keys or scale Redis |
| Error rate | >0.1% | >1% | Check logs immediately, may need a failover |
Post-Exam Analysis Checklist
Within 24 hours of exam completion:
- Export performance metrics for review (save graphs, not just numbers)
- Review slow query log for optimization opportunities
- Check for any error log entries during exam window
- Survey students about technical experience (response time, errors)
- Document any incidents and response actions taken
- Identify capacity headroom: If CPU peaked >80% or workers >90% used, plan to scale for next exam
- Archive load test results and actual performance side-by-side for validation
- Update runbook with any new issues encountered and solutions
7. Frequently Asked Questions
Q: How many concurrent users can a 4 vCPU / 8 GB server really handle for quizzes?
A: It depends entirely on configuration. With database sessions and 1 question per page, maybe 80-100 before severe degradation. With Redis sessions and 5 questions per page, 200-300 comfortably. With optimized PHP-FPM and database tuning, potentially 400. But why risk it? Load test with your actual quiz structure to know for certain.
Q: Is fast-memory-based session store really necessary, or is it just nice to have?
A: For <100 concurrent quiz takers, fast-memory-based session store (e.g., Redis) is a significant performance boost but not strictly required with excellent database tuning. For 100-300 concurrent, it's highly recommended. For 300+, it's effectively mandatory unless you've extremely oversized database hardware. In our internal quiz-load tests, Redis-backed sessions reduced session-related database pressure by up to 5x, but your exact result depends on configuration.
Q: Can I run quizzes successfully on shared hosting?
A: Extremely unlikely for anything over 50 concurrent examinees. Shared hosting typically has:
- Strict PHP-FPM process limits (often 5-10 workers per account)
- Shared database with query time limits (kills long-running quiz queries)
- No Redis access
- CPU throttling during high usage
For high-stakes exams, dedicated resources are essential.
Q: What's the single most important optimization for quiz concurrency?
A: Moving sessions from database to fast-memory-based session store (e.g., Redis). It's a 30-minute configuration change that delivers the biggest single improvement in concurrency capacity. Second place: increasing questions per page from 1 to 5.
Q: How much does it cost to handle 500 concurrent quiz takers reliably?
A: Ballpark $200-300/month for infrastructure (separate web + database + Redis), or $1,500-2,000/month for managed hosting that includes support, monitoring, and optimization expertise. One-time DIY setup costs might add $1,000-2,000 for consulting/configuration. Insurance against exam failure for 500 students? Priceless.
Q: Our quiz performance was fine last semester with 200 students. Why is it failing with 250?
A: Concurrency doesn't scale linearly because of resource contention (lock waits, CPU context switching, connection pools). Systems often have cliff edges - 90% capacity runs fine, 95% degrades badly, 100% collapses. You likely hit a threshold where database session locks or PHP-FPM worker exhaustion triggered cascading slowdowns.
Q: Should I disable Moodle's logs during high-concurrency exams?
A: Not as first resort, but it's a valid emergency measure. Logging (especially standard logs) writes heavily to database during quizzes. Better solutions: Use external syslog for logging, or use logstore_database with separate database. Only disable logs if facing imminent failure during exam.
Q: How do I calculate exactly how many concurrent requests my quiz will generate?
A: Use this formula:
Concurrent examinees x (Pages per quiz / Avg. time per page) + (Concurrent examinees / Autosave interval)Example: 400 students, 12 pages, 7 minutes per page, 180-second autosave:
400 x (12 / 7) + (400 / 3) = 686 + 133 = 819 requests per minute sustained = 13.6 req/secThen add 3x buffer for thundering herd at start and submission rush at end.
Q: We load tested successfully but exam day still had issues. What went wrong?
A: Common causes:
- Load test was on empty caches; exam day caches were full/stale
- Load test didn't simulate autosave correctly
- Load test users were simpler than actual quiz (e.g., fewer question types)
- Didn't test submission rush at deadline
- Load test from same datacenter (didn't include real network latency)
- Different time of day (other system processes were running during exam)
Q: Can I use MoodleCloud for 300+ concurrent quiz takers?
A: MoodleCloud's shared infrastructure is sized around registered-user plan limits, not a dedicated exam-window concurrency commitment. For 300+ concurrent examinees on high-stakes assessments, use dedicated or managed hosting where the concurrency target, staging test, and response plan are agreed before the exam window.
8. MooDIY Cloud's Exam-Window Planning
MooDIY Cloud plans assessment capacity around exam-day peaks, not just daily browsing. For high-stakes quiz windows, we define a target concurrent-user number, validate it with staging load tests, and size PHP, database, Redis, and storage resources around that target.
Our Technical Advantage
Tuned PHP-FPM workers: We calculate worker pools from the agreed concurrency target:
Workers = (concurrency target x 1.5) / average response time targetFor a 1,000-student quiz target: 1,000 x 1.5 / 0.5s = 3,000 worker capacity across the cluster before applying memory and failover checks.
Multi-node architecture: Larger assessment workloads can use dedicated database nodes with 16-32 GB RAM, 80% allocated to buffer pool. Web tier scales horizontally behind a load balancer with health checks.
Fast storage throughout: Dedicated assessment infrastructure uses NVMe-backed storage sized for quiz-attempt write bursts. Validate IOPS with a pre-exam load test rather than relying on a generic drive label.
Pre-exam load testing: Before high-stakes exams, we can run an enhanced JMeter script against your staging environment. You see the headroom before go-live.
Exam-day monitoring: For larger exams, engineers can watch real-time dashboards during the scheduled window under the agreed support plan, with escalation paths defined before the assessment starts.
Incident playbooks: Every scenario in Section 6's troubleshooting guide is documented with one-command fixes. We've rehearsed failures so you don't experience them.
Proven at Scale
We've successfully supported:
- 1,200 concurrent quiz takers across multi-campus standardized assessments
- 2,400 students in flexible 4-hour exam windows (peak 890 concurrent)
- 5,000 user sites with 20% concurrency during final exam weeks (zero incidents)
Or contact us at support@moodiycloud.com for:
- Guided load test consultation (free 30-minute session)
- Concurrency assessment based on your exam schedule
- Architecture review for high-stakes assessments
Related guides: Learn how to measure Moodle performance, plan proctoring assessments to maintain academic integrity, or see our performance optimization guide.
