role_assign(), role_unassign() and changing roles

role_assign(), role_unassign() and changing roles

by Martín Langhoff -
Number of replies: 35

MartinD, Petr, $otherrolesexperts wink

Working on enrol/database, I want to process a couple hundred thousand user roles wink and speeding things up...

My initial question was: what is the right way of changing role? unassign()/assign()? Is this required?. Looking at those functions, I see a lot of code, including callbacks to modules, and one call to sync_metacourses per enrolment. Ouch.

I was under the impression that role_assign() was a core function -- rather than a surface / UI function. Hmmm. We need _core functions in key areas of Moodle like roles to be fast fast *fast* _ ... so I'm going to be working on the following ideas:

  • All the checking role_assign() is doing is insane and extremely costly. By the time any codepath reaches role_assign() they better have checked courseid, userid, and the phase of the moon. Can I excise those? wink I'll probably write role_assign_f() where f stands for fast and it assumes the caller has performed the relevant sanity checks.
  • Similarly, the fast version skips sync_metacourses() wide eyes
  • I don't think anyone has been told about these new functions $modulename_role_assign(). So... before anyone uses them, can we change the semantics so we can call them after all the assignments have been done, with the timemodified they have to scan for?
  • Same for unassign, naturally

Overall - I think it'll be a good idea to define what the public API of accesslib is, and how it should be used. So we can tweak how things work inside for performance and fuzzy bunnies.

Average of ratings: -
In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Dan Marsden -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Plugins guardians Picture of Testers Picture of Translators

sounds good to me! - I've noticed a big performance hit on our server with  1.7/1.8 and I've suspected Roles to be the culprit! (btw I think roles are kinda screwed in the current 1.8_stable in CVS - see MDL-9606)

smile

Dan

In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Samuli Karevaara -
"All the checking role_assign() is doing is insane and extremely costly"

Hear hear! evil

I see that there are a few PHP scripts to calculate the phase of the moon, like this one at weberdev.com, filed under "Useful Tools" clown

Ps. Okay, my frustration with the slowness of some of the roles functions starts to show... I'd like to (w)hack them to speed also, but I still don't grasp them well enough not to break "everything".
In reply to Samuli Karevaara

Re: role_assign(), role_unassign() and changing roles

by Martin Dougiamas -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
Saamuli and Dan, can you be more specific?

I know it's very tempting to blame things on "roles" but it really depends on the exact operation you are attempting. As far as I know we do not have any major performance issues in 1.8 ... we need specific reproducible reports to be able to examine the performance in that situation.

http://tracker.moodle.org/secure/IssueNavigator.jspa?reset=true&mode=hide&pid=10011&sorter/order=DESC&sorter/field=priority&resolution=-1&component=10097
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Samuli Karevaara -
My frustration mostly comes from the times when I wasn't able to get 1.7 to any usable state in our environment. This was the extremely slow login thing, mainly because we had a lot of site-level activities for students which moodle.org doesn't have. 1.7 login did some things like looping through all of the courses and all of the modules in those courses, just to see if the user has any rights in them, to have the capabilities cached. 1.8 is much better already! I'll try to isolate some of the issues that are left in 1.8 and file them in the tracker.
In reply to Samuli Karevaara

Re: role_assign(), role_unassign() and changing roles

by Martin Dougiamas -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
My frustration comes from complaints about things in 1.7 release that were actually addressed months ago in 1.7.x and 1.8.x.

Yes, please file those bugs, I'd love to see them and put our resources into fixing them.
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Samuli Karevaara -
Ok, point taken. Apologies.
In reply to Samuli Karevaara

Re: role_assign(), role_unassign() and changing roles

by Samuli Karevaara -
I feel that I own you and the team another apology and a big thank-you also: one gripe that I used to have was that upgrading took "forever" when the roles were reassigned, with a recent 1.8+ it was much much faster! I see that Petr has done a bit of spring (here at least it's spring) cleaning on the accesslib.php last month... Thanks!
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Michael Penney -
This may not help any, and rate me accordingly. I'm not at HSU anymore, but I hear they are having big performance problems with 1.8 (fortunately Jeff has made sure they are testing on a testing machinesmile, which appear to be due to their very large number of courses>3500 and the role checking in those courses.

I know that we can use Moodle.org as a test standard for large sites with lots of users, but there are not that many courses, so performance issues caused by 1000s of courses have to wait for the big production sites to start moving over to show up, which is an ouchy situationsad.

Perhaps a solution: Create a course for every user on Moodle.org--move the Blog to a standard course module and give everyone a forum of their own too, for commenting on blogssmile. That would give Moodle.org a profile more like other large sites: lots of users and lots of courses, and make the test cases on Moodle.org more closely match test cases on large educational sites.


In reply to Michael Penney

Re: role_assign(), role_unassign() and changing roles

by Martin Dougiamas -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
If you talk to them please ask them to file some bug reports detailing the exact pages and numbers involved.
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Jeff Graham -
Hi Martin,

Done smile

MDL-9617

I wanted to get more information to provide to you folks so that you weren't shooting in the dark.

My last day at HSU is this coming Monday and I wanted to get enough information into the bug tracker as I will not have access to this information after Monday, as a result If you need any more information please get back to me ASAP so that I can dig out the numbers for you.

Performance problems seems to be largely isolated to the admin account, but there may be some implications for non-admin accounts.

regards,
Jeff


Average of ratings: Useful (1)
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by H Hak -

I have a similar login problem

Moodle 1.8+
mdl_course_categories sql : 2200 records
mdl_courses sql                : 3800 records

Login as admin takes 26 sec
Login as student takes 5 sec

An Identical environment on 1.6.5  both logins takes about 1 second.

I switched from OS / machines (specification) but it it didn't make any difference.

I'm using the latest 1.8 version downloade at 2007/04/27



In reply to H Hak

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -

And that's really good performance. If you enable PERF_TO_LOG you'll get a reading of the number of DB queries. That is the problem I am working on right now to get things back to taking ~40 dbqueries regardless of the number of categories or courses.

To do that, we need a couple of functions that push all the work into SQL instead of loops into recursive functions that query the DB.

In reply to H Hak

Re: role_assign(), role_unassign() and changing roles

by H Hak -

Moodle 1.8 performance. (some tests)

I have increased memory from 1Gb to 3GB on ubuntu Debian  adaptec 160 scsi 10k

Perspective
In one second you can do...
832000 function calls
25700 16KB files read from disk (cache)
7300 regular expression replaces over 1KB of text
9800 16KB files written to disk (cache)
2570 get_record calls on the course table
750 insert_record calls on the course table
180 update_record calls on the course table         

It didn't have any effect login time: admin 26 sec  student 5 sec
mdl_course_categories : 2200 records
mdl_courses sql            : 3800 records

After drop table mdl_courses.sql and rebuild to first 100 records.
Login time:    admin:  6 sec
                     student:  5 sec 
                     fresh student (no member of any course ) 4.5 sec

After drop table mdl_courses_categories.sql and rebuild to first 100 records.
Login time:    admin:  2.3 sec
                     student:  2.3 sec 
                     fresh student (no member of any course ) 1.6 sec




 
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Dan Marsden -
Picture of Core developers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers Picture of Plugins guardians Picture of Testers Picture of Translators

Hi MD,

Point Taken! - I'll try to post some more specific examples in the next week or so! - I'm pretty stuck in meetings this week and next, but I'll try to get some time in-between!

smile

Dan

In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Martin Dougiamas -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
role_assign and role_unassign are definitely the functions to use for assigning or unassigning roles. The API is all of accesslib.php as far as I'm concerned.

The checking is not insane ... they have all that checking to avoid screw-ups in a complicated and crucial operation.

You can add a $skipchecks=false parameter to them if you want but you BETTER be doing those checks beforehand.

Please don't start adding xxxxxx_f functions and further complicating the API!
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -

The API is all of accesslib.php as far as I'm concerned.

Martin -- can we avoid taking that position? Only a few of those functions are ever used outside of accesslib and a few upgrade and special scripts. If we can segregate the "public" part of accesslib vs the "private" part, I have a chance of reworking things to run faster.

Why do you want to make it all public?

The checking is not insane ...

Well... it is not insane for data coming from the end user. But it is for functions that are internal, where the caller is supposed to have done the homework first. And in this case, the checks are very expensive.

In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Martin Dougiamas -
Picture of Core developers Picture of Documentation writers Picture of Moodle HQ Picture of Particularly helpful Moodlers Picture of Plugin developers Picture of Testers
Adding something like a $skipchecks=false parameter is probably the best thing to do a barebones assign with no checks, no forum subscriptions, no metacourse syncing etc.

If you know which functions can be improved does it matter whether it's labelled public or private? Surely optimisation applies equally to both.
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -
Ok - I'll switch to adding a parameter.

This and next week I am working on the issues re scalability of roles, and there is a lot I can do if I am able to rearrange a bit how accesslib works internally. I am working on it, so I don't have a 100% clear picture of what the changes look like finalised. Will post the patches for discussion as soon as I have them.

But the way things are looking now is that I'll need to change some areas of accesslib a bit. That'll mean changing the internal function calls. Of course, I want to preserve the "public" part -- that is, the calls currently being used by Moodle code.

That's all. I think I made a mistake posting this too early, without code to show what I mean.
In reply to Martin Dougiamas

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -
> but you BETTER be doing those checks beforehand

Well -- I did a quick audit of the callers to assign_role() and it looks like all of them are passing reasonably well validated parameters. So IMHO we could _just_ remove those checks. In other words - it looks it would have been useful validation during early roles develoment, but no longer relevant right now.

(All the callers are in admin, auth plugins, enrol plugins and restorelib. All core code that has every reason to have fetched those params from the DB itself, and double checked user/external input.)
In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Petr Skoda -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers
My +1 for keeping the checks there at least in debug mode, because they do catch coding bugs.
In reply to Petr Skoda

Re: role_assign(), role_unassign() and changing roles

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers
Isn't this the place where we really ought to be using foreign key constraints in the database?

I know we can't just create all of the contraints from the install.xml files, that would create too many errors, but perhaps we need a way to create certain ones, where there is a clear benefit (like performance) and/or where we believe we have the number of resources to handle the number of bugs exposed.

Here, where it is a relatively new table, and we believe all insertions ever have been carefully checked, it should be pretty safe.

Another candidate would be the new gradebook tables.

And I would certainly like to start using FKs to flush out bugs in the quiz.
In reply to Tim Hunt

Re: role_assign(), role_unassign() and changing roles

by Paolo Oprandi -
I just had a quick test using the IMS Enterprise enrolment feature. I passed in 7,166 users, 601 courses and 10,157 role assignments (seems low?). Using Moodle 1.6 IMS Enterprise took 246 seconds (4.1 mins) and using Moodle 1.8 it took 1107 seconds (18.5 mins) with the standard role mappings from 1.6...
As the times are quite low it isn't a show stopper, but nevertheless the increase in time by 4 is quite drammatic
In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Petr Skoda -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers
Yes, role assign/unassign is slow - it was expected to be slow.

One of the expensive operations is forum subscription init/cleanup. I do not know a way around this. Other modules may need some extra processing too.

Th metacourse sync could be made optional and executed only once for each course affected by the external sync operation.

skodak
In reply to Petr Skoda

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -

Yes, role assign/unassign is slow - it was expected to be slow.

Hmmmm. Actually it isn't expected to be slow mixed on 1.4/1.5/1.6 it was really fast! And that's what all the large installations were counting on.

One of the expensive operations is forum subscription init/cleanup.

Thanks for the hint - I'll look into it - I'm sure there's a way to do it in pure SQL over many enrolments.

In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Petr Skoda -
Picture of Core developers Picture of Documentation writers Picture of Peer reviewers Picture of Plugin developers
hmm, there was no role assign/unassign in 1.4-1.6 smile There was only course enrolment before and I agree it was super fast compared to current code, but less complex and no special cases caused by roles & capabilities.
In reply to Petr Skoda

Re: role_assign(), role_unassign() and changing roles

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers
sync_metacourse is orders of magnitude faster since I rewrote it to do everything with two SQL queries instead of lots of PHP loops. I did that in response to a performnce problem we were having, adn that was enough.

However, you are right, it would be good to have an option to delay doing it when you have a bunch of assignments being made, and when you can guarantee to call sync_metacourses outside you calling loop.

Similarly, a (separate) optional boolean parameter to skip the test of things like userid at the start makes sense.

However, as Petr says, the most expensive bit is things like forum subscriptions, and that is the bit that is really hard to solve.
In reply to Petr Skoda

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -

One of the expensive operations is forum subscription init/cleanup.

Ok - I've given this a bit of a read. My initial notes:

We have a problem in course and category moving. It used to be really lightweight as categories had no bearing on Moodle behaviour. Now one of our main challenges should be moving categories cheaply because there is a ton of work that needs to happen...

Except that we aren't doing it... Do forum default subscriptions still work (if you are a student category-wide and courses are moved)? Is there a way forum is catching those users somehow?

I am not too partial to implementing these -- as I don't see any easy way we can support category-level enrolments in a scalable fashion. Personally, I am more inclined to discourage category-level enrolments and add an option to switch them off. At least until we find a scalable way of dealing with them. In the meantime, it is cheap to optimise for the case where there are none.

Questions:

  • If listing course participants was very cheap - could we drop these forum_assign_role() calls, and use an "unsub" table to keep track of who's explicitly unsubbed from the default forum subscriptions?
  • If that works, and noone cries, can we drop the "call all modules" part of assign_role()? }-)
  • Or -- can I change the semantics...?

In terms of changing semantics, I am thinking of either changing the way we call it, or supporting an alternative $mod_assign_role_batch(). The idea is that is will be callable from any and all of

  • cron
  • at each role assignment
  • at the end of batch role assignments

The module keeps track of the timestamp of the last role assignment processed and will handle a single assignment or a thousand with a constant number of DB queries. We might need some extra thinking for role unassignments (maybe log them temporarily so we can execute the unenrolment actions...)

And I want to say... I'm sorry! -- I know all of this is a bit annoying, and it is hard work... But we must switch to an approach to abstractions that plays better with the DB than making db queries inside code loops.

We have to move all the SQL out of the loops, gather a ton of data using JOINs and use the loops (walking those evil recordsets!) to process it. At least that's the only approach I know that will help us scale the way 1.4/1.5 scale and further.

If we move towards such constant-db-queries model in the core code, then Moodle's scalability bottleneck shifts to the DB, and the current crop of DBs are really blazing fast. And they all have a bagful of tricks we can use to make things even faster...

In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Tim Hunt -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers
Of course, in Moodle 1.9, Role assignment -> forum subscription should probably be triggered by an event.

A assume role assignment will be an event one can ask to receive.

Of course, this does not necessarily make the performance situation any better.
In reply to Tim Hunt

Re: role_assign(), role_unassign() and changing roles

by sam marshall -
Picture of Core developers Picture of Peer reviewers Picture of Plugin developers
Agreed with Tim, events for this sounds good. Since forum subscriptions aren't the most critical things in the world, these could happen on cron... (and with a low priority if my suggestion about allowing flexible priorities/times for passing events is taken up).

Performance improvements in this area are all good as long as they don't break anything! Does simpletest for accesslib exist? Maybe it does, not sure, haven't got time to look right now - I'd feel an awful lot happier about any changes if it does...

By the way if performance really only reduces by a factor of 4 then that's fine imo. :> I suspect it is worse in some situations though. We see many queries on admin logon last I looked (it is much fewer for students, but still high). Performance work is on our agenda for the second half of this year but if Martin L fixes it first that would be good... :>

(We do have some other local performance hacks in course view which made a significant difference to our system, which I have posted as MDL bugs.)

By the way maybe this is not the place but I really think the PERFDB thing should always be turned on. It has no bad consequences (incrementing an in-memory variable? please) and is basically the most useful part of the 'performance info' option. Also, since you often want to view performance info on a live server, it would be best if there were a way to enable this for specific users. [Oh dear, does this mean another has_capability...] We currently have a hack that uses our in-house authentication system to do that. I also have some code, which is local ie I didn't put it in core (I think) which categorises database queries e.g. DB queries 1950, weight 1968: S=1949 U=1

(The weight is estimated for our Postgres system, numbers differ for MySQL; it's very sketchy estimate anyhow, maybe just the S=, U= are better.)

Also by the way Moodle does many more redundant queries if debug is turned on so anybody looking at performance should ensure debug is turned off first. (The performance tuning mantra: Debug OFF, performance info and perfdb ON. smile

--sam
In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -
Some related action on MDL-7416: get_my_courses is very slow with lots of courses and as usual more patches being previewed in the branch http://git.catalyst.net.nz/gitweb?p=moodle-r2.git;a=shortlog;h=mdl18-local

In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Wen Hao Chuang -
Martin, just curious, did all of these patches went into the MOODLE_STABLE branch? How much testing were done with these patches? Thanks!
In reply to Wen Hao Chuang

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -
assign/unassign have $fast now. And all the fixes to get_my_courses() are there. I expect to do another round of optimisations in a couple of weeks -- as my project priorities come back to performance, and giving people more time to collect profiling data (that list of heavy pages, with dbqueries count wink ).

At that stage, I will tackle as much as I can without breaking with current accesslib, but some of the problems will require doing what I've outlined in my RFC thread. It's probably going to lead to a "enterprise-scale" branch until we find a way to merge it into 1.9.
In reply to Martín Langhoff

Re: role_assign(), role_unassign() and changing roles

by Wen Hao Chuang -
ML could you please share the list of heavy pages with large dbqueries count that you currently have with the rest of us? We are currently collecting performance profiling data too but at the same time also testing the new patches to see if they still working correctly functionality-wise. We will post our results to here later too. Thanks!
In reply to Wen Hao Chuang

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -
Right now I have none sad as we're working on features for the 1.8 rollout I have on. Our focus is on getting all the features going, and then return to performance.

Hopefully when we get to that stage there'll be a good map of where we should focus, and perhaps patches from other people too wink

(So much work, so little time)
In reply to Wen Hao Chuang

Re: role_assign(), role_unassign() and changing roles

by Martín Langhoff -
Wen - can you guys write unit tests for the roles stuff? That would also help us a ton...