The $user object is created in one of two different ways in Drupal:
1. It is created by session.inc during bootstrap. The user 'load' op is not called here.
2. It is created by a call to user_load(). The user 'load' op is called here. This happens semi-randomly throughout the codebase, e.g. when a node author is loaded, when a user page is viewed or edited, when a user logs in or out.
The reason for this is optimization killes and I agree that it's not necessary to do a full user_load on every request.
The problem is that module writers can't be guaranteed that the $user object they are working with is a "fully loaded" $user object, as Chris Johnson notes.
The current solution is to call user_load() yourself if you want to be sure. This is expensive because user_load has no caching.
It would be a great advantage to be able to test whether user_load has already run or not. One approach would be to implement the user hook for your module and set a flag there, then test it later. But that's expensive too. So here's my proposal: a one-line patch that simply records that user_load() has run:
<?php
$user->user_loaded = TRUE;
?>Now modules can simply check for the presence of $user->user_loaded.
| Comment | File | Size | Author |
|---|---|---|---|
| #27 | user_load_static_cache_d6-91786-27.patch | 1.75 KB | oriol_e9g |
| #25 | user_load_static_cache_d6-91786-25.patch | 1.75 KB | Offlein |
| #23 | user_load_static_cache.patch | 1.79 KB | robertdouglass |
| #20 | d5_user_load_cache.patch | 1.82 KB | bobooon |
| #14 | drupal_user.diff | 1.03 KB | hickory |
Comments
Comment #1
hickory commentedWould it be easier if modules called user_load as normal, but the user_load function had a static variable that was set if it had already been run and just returned $user?
I agree that this is a problem.
Comment #2
killes@www.drop.org commentedshoudl get fixed in HEAD first.
Comment #3
moshe weitzman commentedyes, a static var cache like node_load() seems best. any eaosn not to go that route?
Comment #4
jvandyk commentedHow will you determine whether the calling function intends the 'load' hook to be fired or not? To be consistent with node_load, the load hook would only be fired the first time, and will require a change to the user_load function signature to reset the cache.
Comment #5
moshe weitzman commentedyes, thats what i had in mind.
the only place where we load user info without firing hook_user(load) is during session creation and i agree that that doesn't need to change.
Comment #6
dries commentedThis isn't really needed. By convention, the global $user object is the object loaded by the session handling.
If you need the full user object, do a user_load() and add caching to user_load(). It might cost you 1ms.
Comment #7
moshe weitzman commentedupdate: no progress. user_load() still has no static cache like node_load(). these two issues have worked on it.
Comment #8
pwolanin commentedright, we should really have added a static cache to user_load in D6 since we now often take a simple int parameter. It was an unfortunate oversight (bug?).
simple (but untested) patch attached that adds such a cache.
Comment #9
moshe weitzman commentedtypo at the end: if ($cachable)
Comment #10
pwolanin commentedsince db_fetch_object() is defined in the API as returning FALSE for no result, we can also trim a couple lines. fixed typo.
Comment #11
pwolanin commentedComment #12
moshe weitzman commentedLooks good. We need to reset this cache after a user_save(), no?
Comment #13
pwolanin commentedyea- good point. Also, we should probably remove the caching code in user_category_load()? Otherwise we are caching the same data twice.
Comment #14
hickory commentedAttached is the patch that I've been using with Drupal 5 for a year or so - it's slightly different because it uses memcache, but should illustrate what needs to be changed.
The main thing to watch out for is modules (buddylist, for example) that write directly to the {users} table without going through user_save - they either need to be rewritten to use user_save for all changes to user objects, or to have a line added that invalidates the user cache when a change is made.
Comment #15
sunQ: Why do we want to clone a user object before passing it along? If a user object is changed somewhere, we would not need to reload it (again)?
Also, possibly related: #254491: Standardize static caching
Comment #16
mercmobily commentedHi,
I hope this progresses as fast as possible, and the fix gets backported to D6.
Right now, not having user_load cache the result causes a lot of trouble.
I am writing a module that uses hook_user() and was wondering why so _many_ queries were being bounced off my DB server. Now I know...
Bye,
Merc.
Comment #17
frankcarey commentedYeah, I'm using a panel page (Michelle's APK) adn notice that user_load is being called 11 times on one page, seems like this is gonna be a lot of overhead when the site launches.
Have others found alternative methods? (I see the memcache above)
Comment #18
dries commentedSee #281596: Performance: static caching of user objects in user_load(), respecting conditions.
Comment #19
catchWent in as part of #347250: Multiple load users
Comment #20
bobooon commentedFor those of you who are stuck with Drupal 5 and need a simple caching solution I have attached a patch which will get you there, effectively and reliably. It's probably not as robust as the Drupal 7 implementation but it will due for most of the performance needs. It's been tested and used on a production site for a few months with no issues.
NOTE: It also allows you to call user_load() only passing it a numeric value, rather than having to specify the entire array('uid' => X).
Comment #21
crea commentedSubscribing
Comment #22
carlescliment commentedTake care when applying patches like this.
Imagine you have a module that adds some data to users:
As modifications are made outside user_save(), you may fall into inconsistencies if you don't explicitly clean the static cache each time you modify user-related data.
I wrote a patch to mantain users in a cache, but I'll definitely revert to the previous version. Safety beats efficiency.
Comment #23
robertdouglass commentedHere's a re-rolled version of pwolanin's patch from #8. Drupal 6.
Comment #24
robertdouglass commentedNote that #23 is not a plug-n-play safe solution. There will be cases where this caching breaks existing functionality and you will have to track them down and use the 2nd parameter to clear the cache.
Comment #25
Offlein commentedRegarding the patch in #23, using this at all seems to be "site-breakingly bad" to me, because it improperly changes the first argument, $user_info, to $array, which it used to be in 2008. This value changed a long time ago. It will cause you to not be able to log into your site.
I'm attaching a D6 patch that should accomplish the same with latest versions of Drupal.
Comment #26
oriol_e9gThere is a mistake in the patch:
The var should be always "cacheable" or "cachable", not a mix :D
Comment #27
oriol_e9gComment #28
Offlein commentedNice catch! Mine would surely generate a PHP notice or something! Thank you!
Comment #29
greenreaperThis is great, even in 2021. Thanks for the patch.
Similarly useful for D6 maintainers repeatedly accessing user profiles in e.g. comment templates is this user_load_profile caching.