-
-
Notifications
You must be signed in to change notification settings - Fork 825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve ACL contact cache building performance by splitting insert and select #15480
Improve ACL contact cache building performance by splitting insert and select #15480
Conversation
(Standard links)
|
ping @johntwyman |
eaa7e56
to
393372a
Compare
CRM_Core_DAO::executeQuery(" | ||
INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) | ||
SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation | ||
$contats = CRM_Core_DAO::excuteQuery("SELECT DISTINCT $userID as user_id, contact_a.id as contact_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@seamuslee001 typos.. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dang fixed @mattwire
393372a
to
bcfc188
Compare
bcfc188
to
697f356
Compare
@seamuslee001 @johntwyman @eileenmcnaughton @totten I agree with this change... because I've already made it :-) However, instead of duplicating effort could you take a look at https://github.com/mattwire/civicrm-groupaclcache That's been working well on quite a few sites that had deadlock issues but it is nominally version specific (5.13) because it overrides 3 core files. I should get a bit more funding to work on it soon, however we are pretty much at the point where we need to do a big replace (with what I've done) rather than incremental bits. With a bit of luck this diff will apply cleanly to latest: https://github.com/mattwire/civicrm-groupaclcache/blob/master/patches/master...mattwire:refactor_groupaclcache.diff |
@mattwire how is doing the insert into a temporary table any more performance, the docs suggest looping the insertion as i am doing here i would have thought that would be the more performant way? |
@seamuslee001 I'm not sure which is bets for performance. However this code (rightly or wrongly) gets called a lot and you can easily end up more than one process trying to update the tables. By looping and running an INSERT for each contact we lock and unlock the table multiple times, and can end up (if there are deadlocks etc) with a half-finished update. By writing all the changes to a temporary table and bulk copying them across using a single INSERT->SELECT. Frustratingly I've used the slower INSERT IGNORE because it seems that race conditions can often cause duplicates to appear and we don't want to trigger an exception if the value we are adding already exists. |
@mattwire i suppose the potential answer is to run that part in a transcation right? The biggest problem i think for us is because we use the multisite extension it does a LEFT JOIN onto the group_contact table which can be pretty slow on things. a sample query of ours is
|
By the way I need to switch over the temp table to use |
@davejenx this might interest you |
@mattwire we have also considered doing it like this
Which puts more of the issue onto php layer |
Has anyone looked into what EXPLAIN says about the slow SELECT? Also why LEFT JOIN the multisite, looks like an INNER JOIN, no? And I often find that WHERE NOT EXISTS is more performant than LEFT JOIN...WHERE leftjoined.field IS NULL. On the php insert loop, you could use a prepared statement if you didn't mind stepping away from the pear db layer. You could also trust mysql to generate valid SQL safe data instead of using the params validation on what's already there. I haven't looked in detail at these though. |
I've had a brief look at the EXPLAIN statement. The largest cost component is All that said, it's not like it's a particularly expensive query. The issue is the locking that occurs because we're doing INSERT INTO...SELECT. From everything I've read I have the sense our options available are:
Performance of the first option might not be fantastic. The second might max out PHP memory or the MySQL query limit (however unlikely?). I won't pretend I fully understand the consequences of the last option. However I do note (from the first ref):
I believe binary logging is enabled by default in standard MySQL installations. So maybe this isn't a problem. |
Another issue with multiple inserts was mentioned by @mattwire but I'll expand:
I still think optimising the query and applying locks (may need some experimentation) is the best way to go. The faster the query can run, the less chance of deadlocks with other simultaneous processes. I believe this is why @mattwire's has found that the temp table improves things (by the way, Matt, have you tried locking tables then deleting from the queue table before insertion to avoid having to use INSERT IGNORE?) I don't have a site that uses ACLs with enough contacts to be a useful measure to test. Nb. in case it's helpful, with my recent queue patch I tried various locking strategies before settling on one that's pretty full on, but works safely under high load - but only because my queries within the lock are fast. |
FWIW, Monish used a reapplied version of @mattwire 's patch https://gist.github.com/monishdeb/99bf7421de5b5bb24edf28abb7907442 and so far our site has been stable for a day, and we don't see zombie mysql processes asleep. |
@JoeMurray Which civi version did you reapply it against? |
5.14.0 |
@seamuslee001 I think this PR can now be closed as it's superseded? |
@mattwire do we have PR that replaces this? |
Overview
This aims to improve the performance of the cache building by splitting out the finding of contacts from the actual insertion. We have been finding that building cache has been locking tables such as the group_contact table for too long causing other processes to fail.
Before
Tables being locked for too long
After
Table locks improved
Technical Details
MySQL docs recommend
Source https://www.percona.com/community-blog/2018/09/24/minimize-mysql-deadlocks-3-steps/
I believe we hae sufficient tests in this area to prove if this works or not
ping @eileenmcnaughton @totten