For each page view by a registered user we update the access value in the users table. For this mysql acquires a write lock which then blocks a lot of reads which want to read from the users table (which happens also each page view, and often more than once). On a high-traffic site such as drupal.org this can decrease mysql performance. For this reason and due to the fact that the access value is hardly ever used (all I found was the user table on /admin/user) I propose to move the access value into its own table, users_access. Attached patch does just that.

Comments

killes@www.drop.org’s picture

StatusFileSize
new1.4 KB

Here's the install file that you need.

killes@www.drop.org’s picture

For people using the mysql query cache this patch should be a huge improvement. Untill now queries involving the users table are basically not cached by the query cache as the users table changes all the time and any cached queries involving this table are discarded...

chx’s picture

Category: bug » task

Very nice trick.

dries’s picture

I don't get it. Do we have performance benchmarks?

chx’s picture

Benchmarking this is quite hard. However, not invalidating the query cache for users is obviously a benefit.

killes@www.drop.org’s picture

benchmarks:



Time for my test script to run:

                    D5-dev
          with patch    without patch

InnoDB    12m 4s +-6s   12m43s +- 7s
MyISAM    11m59s +- 8s  12m33s +- 3s

For whatever reason, InnoDB is slightly slower. No idea why. Note that I did not tweak the standard Debian InnoDB settings in any way. The patch is a slight improvement (4%) in execution time.


Cache miss ratios:

                    D5-dev
          with patch    without patch
InnoDB    0.21            0.24
MyISAM    0.21            0.25

The patch is an improvement by 16%.

Table lock ratio:

                    D5-dev
          with patch    without patch
InnoDB     12058:1         7704:1
MyISAM     21303:1          914:1


Again, the patch provides a significant improvement over the non-patched version. The difference InnoDB vs MyISAM looks big, but if you keep in mind, that only 7 and 4 table blockings were conted it is clear that some statistical ambivalence is hidden here.

m3avrck’s picture

Version: 6.x-dev » 5.x-dev

Gerhard, awesome!

Correct me if I'm wrong but I don't see any reason why this should wait for 6.x, would be a nice performance benefit for 5.x.

chx’s picture

Version: 5.x-dev » 6.x-dev

Why this should wait for D6? Because today is the Drupal 5 release date and this would need a database upgrade which is a particularly bad idea past RC...

gerhard killesreiter’s picture

I think it would make sense to move the "login" field also to the users_access table (and rename that table). Currently, a user logging in blocks other users from getting a new page or loading a node or ...

killes@www.drop.org’s picture

StatusFileSize
new3.99 KB

profile module uses the access flag.

Caleb G2’s picture

subscribed

moshe weitzman’s picture

nice patch. subscribing

webchick’s picture

Wicked; this has bugged me for a LONG time.

kbahey’s picture

Status: Needs review » Needs work

On the mailing list, I expressed that I am ambivalent about this.

Updating the access field only happens in session.inc, and hence no one else is affected. That is good.

On the other hand, any module that wants to print out the last access time for a user will now have a penalty of an extra join for a 1:1 relationship. Breaking a 1:1 relationship into a different table is often against normalization, and usually proves problematic down the road.

Here are the modules affected by this are:

./buddylist/buddylist.module
./devel/devel.module
./erp/erp_home/erp_home.module
./evaluation/evaluation.module
./hof/hof.module
./im/im.module
./inactive_user/inactive_user.module
./knosos/node_access/node_access.module
./og2list/og2list.module
./print/print.module
./profile-ng/profile.module
./syndication/syndication.module
./user_list/user_list.module
./welcome/welcome_demosite.inc

If everyone else decides to go with this patch, then you need to modify this part too in user.module, and add an extra join for the who is new block.

$result = db_query_range('SELECT uid, name FROM {users} WHERE status != 0 AND access != 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5));

Setting this to needs work for the above query.

moshe weitzman’s picture

@kbahey - there isn't much question about this rightness of this patch. the current situation penalizes everyone so that the handful of modules that you mention can avoid a JOIN.

kbahey’s picture

Point taken.

Other alternatives:

Has the users table been converted into InnoDB on drupal.org?

Perhaps playing with various locking mechanisms with it being InnoDB may alleviate the problem, since only one row at a time need to be locked when updated.

Details here http://dev.mysql.com/doc/refman/5.0/en/innodb-lock-modes.html

killes@www.drop.org’s picture

Status: Needs work » Needs review
StatusFileSize
new5.21 KB

Ok, I've updated the patch to fix the "Who's online" query and also remove access from the "default user fields" somewhere else.

kbahey: The list you retrieved seems to have an error, eg og2list does not use the access info.

Next step is to also move the login info. The login field is only used during the password retrieval operation. Seems reasonable to do a user_load there.

killes@www.drop.org’s picture

StatusFileSize
new5.93 KB

The changes to move the login field are almost too small to mention. user_load is already executed everywhere.

killes@www.drop.org’s picture

StatusFileSize
new2.04 KB

Here's the updated users.install file. Needs some pgsql love.

killes@www.drop.org’s picture

StatusFileSize
new2.16 KB

Improved user.install file, complete with pgsql and some mysql beautifications thanks to sammys.

kbahey’s picture

Here is an updated list of modules.

./affiliate/affiliate.module
./buddylist/buddylist.module
./devel/devel.module
./erp/erp_home/erp_home.module
./hof/hof.module
./im/im.module
./inactive_user/inactive_user.module
./print/print.module
./profile-ng/profile.module
./syndication/syndication.module
./tasks/tasks.module
./user_list/user_list.module

Here is the command used:

$ cd HEAD/contributions/modules
$ find . -name "*.module" -exec grep "{users}" {} /dev/null \; | grep -w access |awk -F: '{print $1}' | sort -u

matt westgate’s picture

This is interesting. We don't explicitly lock the users table so I don't understand the write lock problem. Perhaps this is an internal MySQL thing I don't know about?

I benchmarked this patch locally and didn't see any performance improvements, but I only benchmarked as a single user with (ab -n 50 -c 5 -C cookie_value localhost). My benchmark only really shows there's not a performance degradation for low-traffic sites, really.

killes@www.drop.org’s picture

http://dev.mysql.com/doc/refman/5.0/en/internal-locking.html

2.
When a lock is released, the lock is made available to the threads in the write lock queue and then to the threads in the read lock queue. This means that if you have many updates for a table, SELECT statements wait until there are no more updates.

Emphasis mine.

matt westgate’s picture

StatusFileSize
new5.71 KB

Thanks for the reference material. I tested the patch differently now and can confirm the LOCK contention. I'm using a slightly different patch, one with the login table still a part of the users table. Moving the login column is such a small performance gain compared to the access column that I prefer to keep it with the rest of its sibling data.

Before the patch (using ab -n 50 -c 5)

mysql> SHOW STATUS LIKE 'Table%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Table_locks_immediate | 2111  | 
| Table_locks_waited    | 34    | 
+-----------------------+-------+

After the patch:

mysql> SHOW STATUS LIKE 'Table%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| Table_locks_immediate | 974   | 
| Table_locks_waited    | 1     | 
+-----------------------+-------+
killes@www.drop.org’s picture

Matt, try to login/out a few times during your test run. I believe oving user.login to the other table is worth it.

BioALIEN’s picture

Subscribing.

matt westgate’s picture

StatusFileSize
new8.97 KB

I had chance to revisit this patch and cleaned some things up. Should be ready for another review now. I added in the system.install table changes and merged the user.install updates to a single function. I also added documentation and a little bug fix to user.module.

matt westgate’s picture

StatusFileSize
new1.68 KB

And the user install file.

matt westgate’s picture

StatusFileSize
new7.89 KB

Killes found an error in my last patch. Let's try this one.

moshe weitzman’s picture

i read through the patch and looks good. i think this comment is still valid and should remain.:

"// TODO: this can be an expensive query. Perhaps only execute it every x minutes. Requires investigation into cache expiration."

These two queries are the only writes that we perform on a typical page view. We should aim to get rid of them. Especially the one for access time. I would think that if we write that once every 5 minutes it is enough. As it says, we just have to work through cache expire stuff.

anyway, i am arguing about a comment which is obviously quite minor. nice work.

m3avrck’s picture

This patch is ready to go, anything holding up for 6.x commit?

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

i tested this. rtbc.

fyi, you can create diffs including new files with fakeadd.sh

gábor hojtsy’s picture

Dries, I bet the drupal.org infrastructure team will be able to provide numbers probably, if the above is not convincing.

dries’s picture

Status: Reviewed & tested by the community » Needs work

Is it me, or isn't there an upgrade path? Where is the users_access table created?

killes@www.drop.org’s picture

m3avrck’s picture

bumping, this should still be fine, although it probably needs a reroll

jvandyk’s picture

Status: Needs work » Needs review
StatusFileSize
new10.42 KB

Rerolled. Moved things to user.schema since that is where we define schemas now. user.install is a new file and provides an update path.

killes@www.drop.org’s picture

I've been thinking that this patch might not be needed if we make InnoDB the recommended MySQL engine for bigger Drupal installs.

However, I think I was wrong there:

1) Drupal.org runs an earlier version of this patch where only the access column got moved, not the login column.

2) In the slow query log there are instances of the update for the login column being slow, but none for the access table:

### 38 Queries
### Taking 16 to 51 seconds to complete

UPDATE users SET login = XXX WHERE uid = XXX;

So, regardless of SQL engine this patch is a good idea.

moshe weitzman’s picture

FYI, I have patch in which signficantly reduces writes into users.access. see http://drupal.org/node/40545

moshe weitzman’s picture

my patch got in - http://drupal.org/node/40545. i am tempted to mark this won't fix. we are now not writing into this field very often. at leats, we should postpone ot D7 IMO and see if this is needed after some real world experience. if someone agrees, please change the issue.

dries’s picture

Given that Moshe's patch went in, I'd like to see us re-evaluate the need for this patch. Moshe's patch should help both MyISAM as InnoDB users, and is likely to buy us more cycles than killes' patch.

Seeing that updating the login timestamp can be that slow, makes me wonder though. The strange part is that only 38 queries (out of hundreds of similar queries?) were slow -- that doesn't seem like consistent behavior. Are these queries slow day after day or was it a one time observation?

All in all, it's probably best to focus our time on more pressing performance patches, and to re-evaluate this patch during the Drupal 7 development cycle.

dharamgollapudi’s picture

Subscribing...

moshe weitzman’s picture

Status: Needs review » Closed (won't fix)

i think killes agrees thats this patch is no longer desireable for D6 and beyond. if i'm wrong, please reopen.

killes@www.drop.org’s picture

Version: 6.x-dev » 7.x-dev
Status: Closed (won't fix) » Needs work

I quite disagree. The patch that went in is an ugly band-aid, not much more than a hack.

catch’s picture

killes brought this patch up again on irc. It'd be cleaner to normalize this information properly I think, and we're firmly into Drupal 7 now.

moshe weitzman’s picture

Avoiding a write query on every page is hardly a hack or a band-aid. Personally, I think a new table just adds a join for little benefit. But feel free try to get this in. I won't block it.

drubage’s picture

Is there any solution to this problem for 5? We have a lot of users/nodes/pageviews and are seeing long wait times for users table (and sessions table) related UPDATES. Since there is an update to the users table for latest access time on each page load I am wondering if this lockout problem is in fact the biggest contributor to database lag. Thanks!

-Drew

catch’s picture

Version: 7.x-dev » 8.x-dev

Moving to D8.

valthebald’s picture

Issue summary: View changes
Issue tags: +needs relevance check

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

roderik’s picture

Status: Needs work » Closed (duplicate)
Issue tags: -needs relevance check
Related issues: +#1835496: Factor {users_field_data}.access out of the table and entity caches

#1835496: Factor {users_field_data}.access out of the table and entity caches is a duplicate. It's newer but has a better issue summary (this one is outdated since comment ~#41), so closing this one.