We'v done a bit of testing of 2.4 and found that under heavy load, the DB bottlenecks, seemingly based on disk IO for a few slow queries which are written to disk-based temp tables.
Our load test script (jMeter) is as follows:
- Get a list of a user's courses via webservice
- Login to first course page
- Wait 4 seconds
- View my Moodle page
- Wait 5 seconds
- View a different course
- Wait 10 seconds
This was done on a loop with 400 users and a 60 second ramp up.
The pattern we saw was a major spike in slow queries for the first minute, which then dropped off, with generally slow performance (~7-12 second page loads). This is on a very large pair of servers (front end: 16CPU, 31GB, DB: 8CPU, 16GB) on gigabit ethernet, with nothing else running. Despite turning quite a lot of the MySQL buffers up to 11 (notably tmp_table_size and max_heap_table_size, which don't make an impact even at 800M), I can't get the tables to stay in memory, and we have ~10% of temp tables written to disk on every run. The front end is not being stretched too much and the DB server is at 50% RAM and 20% CPU, suggesting it's disk IO holding it back. I have used mysqltuner.pl to try to adjust things and now the only thing it complains about join_buffer_size. There are several thousand joins without indexes causing this warning, but it seems we can't do much about these (it's the ones in the capabilities code that do CONCAT() on the context paths). Even with join_buffer_size at 128M, it still complains.
Does this look normal, and what can we do about it? Does the OU see a similar number of slow queries? Can we just throw RAM at it and turn the buffers up even higher? It feels like we've done this already and I'm not sure exactly how to tell how much RAM it would ultimately need or how big to set the buffers.
I should add that this is for a large DB with 125,000 users and 30,000 courses.
Here's an example of one of the slow queries, that takes 1.7 secs normally and 46 secs under load:
(SELECT ctx.path, rc.roleid, rc.capability, rc.permission
FROM mdl_role_capabilities rc
JOIN mdl_context ctx
ON (ctx.id = rc.contextid)
JOIN mdl_context pctx
ON (pctx.id = '1'
AND (ctx.id = pctx.id
OR ctx.path LIKE CONCAT(pctx.path, '/%')
OR pctx.path LIKE CONCAT(ctx.path, '/%')))
LEFT JOIN mdl_block_instances bi
ON (ctx.contextlevel = 80 AND bi.id = ctx.instanceid)
LEFT JOIN mdl_context bpctx
ON (bpctx.id = bi.parentcontextid)
WHERE rc.roleid = '7'
AND (ctx.contextlevel <= 50 OR bpctx.contextlevel < 50)
)
UNION
(SELECT ctx.path, rc.roleid, rc.capability, rc.permission
FROM mdl_role_capabilities rc
JOIN mdl_context ctx
ON (ctx.id = rc.contextid)
JOIN mdl_context pctx
ON (pctx.id = '357'
AND (ctx.id = pctx.id
OR ctx.path LIKE CONCAT(pctx.path, '/%')
OR pctx.path LIKE CONCAT(ctx.path, '/%')))
LEFT JOIN mdl_block_instances bi
ON (ctx.contextlevel = 80 AND bi.id = ctx.instanceid)
LEFT JOIN mdl_context bpctx
ON (bpctx.id = bi.parentcontextid)
WHERE rc.roleid = '5'
AND (ctx.contextlevel <= 50 OR bpctx.contextlevel < 50)
)
UNION
(SELECT ctx.path, rc.roleid, rc.capability, rc.permission
FROM mdl_role_capabilities rc
JOIN mdl_context ctx
ON (ctx.id = rc.contextid)
JOIN mdl_context pctx
ON (pctx.id IN ('35653','58299','71817','71818','71819','71820','71821','71822','71823','71824','71825','71826','71827','71828','71830','71831','71832','71833','71834','71835','71836','71837','71838','71839','71840','71841','71842','71843','71844','71845','71846','71847','71848','71849','71851','71852','71853','71854','71855','71856','71857','71858','71859','71860','71861','71862','71863','71864','71865','71866','71867','71868','71869','71870','71871','71872','71873','71874','71875','71876','71877','71966','71967','71969','71974','71977','71995','71999','72002','72008','72009','72013','72014','72015','72050','163422','377743','377745','377747','377748','377750','377751','377753','377755','377757','377758','377759','377760','377761','377762','377763','377764','377765','377766','377767','377768','377769','377770','377771','377772','377773','377774','377775','377776','377777','377778','377779','377780','377781','377782','377783','377784','377785','377786','377787','377789','377790','377791','377792','377793','377794','377795','377796','377797','377798','377799','377885','377886','377888','377893','377912','377914','377923','377924','377928','377929','377930','525948')
AND (ctx.id = pctx.id
OR ctx.path LIKE CONCAT(pctx.path, '/%')
OR pctx.path LIKE CONCAT(ctx.path, '/%')))
LEFT JOIN mdl_block_instances bi
ON (ctx.contextlevel = 80 AND bi.id = ctx.instanceid)
LEFT JOIN mdl_context bpctx
ON (bpctx.id = bi.parentcontextid)
WHERE rc.roleid = '10'
AND (ctx.contextlevel <= 50 OR bpctx.contextlevel < 50)
)
UNION
(SELECT ctx.path, rc.roleid, rc.capability, rc.permission
FROM mdl_role_capabilities rc
JOIN mdl_context ctx
ON (ctx.id = rc.contextid)
JOIN mdl_context pctx
ON (pctx.id IN ('35653','71285','71817','71818','71819','71820','71821','71822','71823','71824','71825','71826','71827','71828','71829','71830','71831','71832','71833','71834','71835','71836','71837','71838','71839','71840','71841','71842','71843','71844','71845','71846','71847','71848','71849','71851','71852','71853','71854','71855','71856','71857','71858','71859','71860','71861','71862','71863','71864','71865','71866','71867','71868','71869','71870','71871','71872','71873','71874','71875','71876','71877','71966','71967','71969','71974','71977','71995','71999','72002','72008','72009','72013','72014','72015','72050','163422','377743','377745','377747','377748','377749','377750','377751','377753','377754','377755','377757','377758','377759','377760','377761','377762','377763','377764','377765','377766','377767','377768','377769','377770','377771','377772','377773','377774','377775','377776','377777','377778','377779','377780','377781','377782','377783','377784','377785','377786','377787','377789','377790','377791','377792','377793','377794','377795','377796','377797','377798','377799','377885','377886','377888','377893','377912','377914','377918','377923','377924','377928','377929','377930','525948')
AND (ctx.id = pctx.id
OR ctx.path LIKE CONCAT(pctx.path, '/%')
OR pctx.path LIKE CONCAT(ctx.path, '/%')))
LEFT JOIN mdl_block_instances bi
ON (ctx.contextlevel = 80 AND bi.id = ctx.instanceid)
LEFT JOIN mdl_context bpctx
ON (bpctx.id = bi.parentcontextid)
WHERE rc.roleid = '15'
AND (ctx.contextlevel <= 50 OR bpctx.contextlevel < 50)
)ORDER BY capability