CiviCRM Community Forums (archive)

*

News:

Have a question about CiviCRM?
Get it answered quickly at the new
CiviCRM Stack Exchange Q+A site

This forum was archived on 25 November 2017. Learn more.
How to get involved.
What to do if you think you've found a bug.



  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Support »
  • Using CiviCRM »
  • Using Core CiviCRM Functions (Moderator: Yashodha Chaku) »
  • MySQL Resource Issue
Pages: [1]

Author Topic: MySQL Resource Issue  (Read 780 times)

m4olivei

  • I post occasionally
  • **
  • Posts: 39
  • Karma: 2
  • CiviCRM version: 4.3.5
  • CMS version: Drupal 7.23
  • MySQL version: MySQL 5
  • PHP version: PHP 5.3
MySQL Resource Issue
April 11, 2014, 10:07:57 am
Hello,

Site stats:

Drupal 7.23
CiviCRM 4.3
MySQL 5.5.36

This morning I had one of my clients websites go down, the following error was returned on all requests:

Error
The website encountered an unexpected error. Please try again later.
Error message
PDOException: SQLSTATE[42000] [1203] User client_____ already has more than 'max_user_connections' active connections in lock_may_be_available() (line 167 of /home/username/public_html/drupal/includes/lock.inc).

This happened after we attempted to use the CiviGroup Roles Sync module to sync some user registrations.  After investigating, we noticed that MySQL CPU and memory were spiking and as such MySQL was unresponsive.  Taking a look at SHOW PROCESSLIST at that time, showed that there were some queries on the CiviCRM database that were clearly to blame:

Code: [Select]
+----------+-------------+-----------+------------------------+---------+-------+---------------------------------+------------------------------------------------------------------------------------------------------+
| Id       | User        | Host      | db                     | Command | Time  | State                           | Info                                                                                                 |
+----------+-------------+-----------+------------------------+---------+-------+---------------------------------+------------------------------------------------------------------------------------------------------+
| 14616996 | client_____ | localhost | client_civicrm_db_____ | Query   | 97025 | Copying to tmp table            | SELECT count(DISTINCT contact_a.id) as rowCount  FROM civicrm_contact contact_a  LEFT JOIN civicrm_g |
| 15176334 | client_____ | localhost | client_civicrm_db_____ | Query   | 93205 | Copying to tmp table            | SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name  FROM civicrm_contact contact_a  LE |
| 15196152 | client_____ | localhost | client_civicrm_db_____ | Query   | 91584 | Copying to tmp table            | SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name  FROM civicrm_contact contact_a  LE |
| 15213642 | client_____ | localhost | client_civicrm_db_____ | Query   | 90371 | Copying to tmp table            | SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name  FROM civicrm_contact contact_a  LE |
| 15221325 | client_____ | localhost | client_civicrm_db_____ | Query   | 89716 | Copying to tmp table            | SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name  FROM civicrm_contact contact_a LEF |
| 15221947 | client_____ | localhost | client_civicrm_db_____ | Query   | 89658 | Copying to tmp table            | SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name  FROM civicrm_contact contact_a LEF |
| 15822898 | client_____ | localhost | client_civicrm_db_____ | Query   |  8276 | Waiting for table metadata lock | DROP TRIGGER IF EXISTS civicrm_contact_before_insert                                                 |
| 15824757 | client_____ | localhost | client_civicrm_db_____ | Query   |  8100 | Waiting for table metadata lock | SELECT  IF ( SUM( value.count*lineItem.qty ),
                 SUM( value.count*lineItem.qty ) +
    |
| 15827514 | client_____ | localhost | client_civicrm_db_____ | Query   |  7710 | Waiting for table metadata lock | SELECT    civicrm_contact.display_name as display_name,
          civicrm_contact.contact_type as co |
| 15827522 | client_____ | localhost | client_civicrm_db_____ | Query   |  7709 | Waiting for table metadata lock | SELECT    civicrm_contact.display_name as display_name,
          civicrm_contact.contact_type as co |
| 15831376 | client_____ | localhost | client_civicrm_db_____ | Query   |  7240 | Waiting for table metadata lock | SELECT    civicrm_contact.display_name as display_name,
          civicrm_contact.contact_type as co |
| 15832671 | client_____ | localhost | client_civicrm_db_____ | Query   |  7060 | Waiting for table metadata lock | SELECT    civicrm_contact.display_name as display_name,
          civicrm_contact.contact_type as co |
| 15833862 | client_____ | localhost | client_civicrm_db_____ | Query   |  6892 | Waiting for table metadata lock | SELECT contact_a.id as contact_id, contact_a.contact_type  as `contact_type`, contact_a.contact_sub_ |
| 15836057 | client_____ | localhost | client_civicrm_db_____ | Query   |  6578 | Waiting for table metadata lock | INSERT INTO dedupe (id1, weight)
                SELECT contact.id as id1, 10 as weight
            |
| 15837723 | client_____ | localhost | client_civicrm_db_____ | Query   |  6347 | Waiting for table metadata lock | SELECT  count(*)
  FROM  civicrm_contact
 WHERE  id IN (10545, 5274 )                                |
| 15840486 | client_____ | localhost | client_civicrm_db_____ | Query   |  5992 | Waiting for table metadata lock | SELECT  count(*)
  FROM  civicrm_contact
 WHERE  id IN (10545, 47383 )                               |
| 15841292 | client_____ | localhost | client_civicrm_db_____ | Query   |  5905 | Waiting for table metadata lock | SELECT *
 FROM civicrm_contact
 
 WHERE (  civicrm_contact.id = 14750 )                            |
| 15842863 | client_____ | localhost | client_civicrm_db_____ | Query   |  5726 | Waiting for table metadata lock | SELECT DISTINCT(civicrm_mailing_event_queue.contact_id) as contact_id,
       civicrm_contact.displa |
| 15843492 | client_____ | localhost | client_civicrm_db_____ | Query   |  5637 | Waitinquitg for table metadata lock | SELECT *
 FROM civicrm_contact
 
 WHERE (  civicrm_contact.id = 129 )                              |
| 15844541 | client_____ | localhost | client_civicrm_db_____ | Query   |  5524 | Waiting for table metadata lock | SELECT  count(*)
  FROM  civicrm_contact
 WHERE  id IN (10545, 5274 )                                |
| 15845363 | client_____ | localhost | client_civicrm_db_____ | Query   |  5424 | Waiting for table metadata lock | INSERT INTO dedupe (id1, weight)
                SELECT contact.id as id1, 10 as weight
            |
| 15846134 | client_____ | localhost | client_civicrm_db_____ | Query   |  5331 | Waiting for table metadata lock | SELECT *
 FROM civicrm_contact
 
 WHERE (  civicrm_contact.id = 129 )                              |
| 15846491 | client_____ | localhost | client_civicrm_db_____ | Query   |  5283 | Waiting for table metadata lock | SELECT *
 FROM civicrm_contact
 
 WHERE (  civicrm_contact.id = 129 )                              |
| 15846708 | client_____ | localhost | client_civicrm_db_____ | Query   |  5251 | Waiting for table metadata lock | SELECT  is_deleted 
 FROM civicrm_contact
 
 WHERE (  civicrm_contact.id = 129 )                   |
| 15893757 | client_____ | localhost | client_civicrm_db_____      | Query   |     0 | NULL                            | show processlist                                                                                     |
+----------+-------------+-----------+------------------------+---------+-------+---------------------------------+------------------------------------------------------------------------------------------------------+
25 rows in set (0.00 sec)

To resolve the immediate issue, I ran the MySQL KILL <process_id> command to stop the top 6 SELECT queries in the 'Copying to tmp table' state that had been running for more than a day.  After that the remaining queries completed and the site became responsive again.

We believe those top 6 queries stem from a Contact search in which we we're trying to search contacts by Group and Created date range after Oct of last year.  The client had been having trouble with such queries and we were investigating.  In that case, the site would become unresponsive (spinning on the search request) and eventually coming back with a 404 response.

Has anyone seen this behaviour before?  Does anyone recognize the queries that were blocking everything?  What can we do to fix this long term?  Are there performance related updates > CiviCRM 4.3 that could help?

Any advice is appreciated.

Thanks,
Matthew Oliveira
« Last Edit: April 11, 2014, 10:16:20 am by m4olivei »

m4olivei

  • I post occasionally
  • **
  • Posts: 39
  • Karma: 2
  • CiviCRM version: 4.3.5
  • CMS version: Drupal 7.23
  • MySQL version: MySQL 5
  • PHP version: PHP 5.3
Re: MySQL Resource Issue
April 11, 2014, 01:36:25 pm
We were able to replicate the issue on our staging sever with a copy of the database in question.  This is the full query:

Code: [Select]
SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name 
FROM civicrm_contact contact_a
LEFT JOIN civicrm_log ON (civicrm_log.entity_id = contact_a.id AND civicrm_log.entity_table = 'civicrm_contact')
LEFT JOIN civicrm_contact contact_b_log ON (civicrm_log.modified_id = contact_b_log.id)
WHERE  ( civicrm_log.modified_date >= '20131016000000' AND civicrm_log.id IN ( select id from civicrm_log group by entity_id order by id ) )  AND (contact_a.is_deleted = 0)   
ORDER BY UPPER(LEFT(contact_a.sort_name, 1)) asc;

Here are some stats on the tables involved:

civicrm_contact
~59,209 rows
12.5 MiB

civicrm_log
~920,140
89.6 MiB

I'm not a MySQL expert, but I'm thinking joining a 59,209 row table to a 920,140 row table is not going to be a good time.  Any insight would be helpful here.

Thanks,
Matt

Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: MySQL Resource Issue
April 12, 2014, 09:58:00 am

would be great if your org can contribute resources / $$$ or a patch to help fix and improve the below query (which is part of the search sub-system)

In the below case specifically where we dont need the name of who modified the contact, the left join is not needed

We've also started storing the modified and created dates in the contact table, so it can be made even more efficient.

i suspect its a 10-20 hour project to make these improvements

lobo

A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

m4olivei

  • I post occasionally
  • **
  • Posts: 39
  • Karma: 2
  • CiviCRM version: 4.3.5
  • CMS version: Drupal 7.23
  • MySQL version: MySQL 5
  • PHP version: PHP 5.3
Re: MySQL Resource Issue
April 14, 2014, 07:17:06 am
Lobo,

Thanks for chiming in.

Ultimately I will need to come up with a patch, as this will continue happening on their site.  Could you offer any advice as to where to look in the code, and /or what specific classes are involved in building this problem query?  Is this situation any different in newer versions of CiviCRM?

Taking a closer look at the query, this is the query to build the A-Z index link list on the search result page.  My impression after a brief debug session is that the query for the search is built first, then passed to the A-Z index builder that add's it's stuff and runs the query.  First this I'll try is to skip the A-Z list altogether and see how the main query does on it's own.

Thanks,
Matt

Donald Lobo

  • Administrator
  • I’m (like) Lobo ;)
  • *****
  • Posts: 15963
  • Karma: 470
    • CiviCRM site
  • CiviCRM version: 4.2+
  • CMS version: Drupal 7, Joomla 2.5+
  • MySQL version: 5.5.x
  • PHP version: 5.4.x
Re: MySQL Resource Issue
April 14, 2014, 09:25:16 am

1. there is an option in search preferences to disable the A-Z pager (which speeds things up a fair bit)

2. The search code is in CRM/Contact/BAO/Query.php. the changeLog function should have most of the changeLog specific part of the code

3. I think if we add created_id / modified_id to the civicrm_contact table, we can bypass the civicrm_log table completely for all searches. But for a search by date range, we dont need the left join to the contact table anyway

lobo
A new CiviCRM Q&A resource needs YOUR help to get started. Visit our StackExchange proposed site, sign up and vote on 5 questions

m4olivei

  • I post occasionally
  • **
  • Posts: 39
  • Karma: 2
  • CiviCRM version: 4.3.5
  • CMS version: Drupal 7.23
  • MySQL version: MySQL 5
  • PHP version: PHP 5.3
Re: MySQL Resource Issue
April 14, 2014, 09:31:37 am
Update (haha, you posted at the same time I wrote this, I discovered a lot of the same things you note :)):

Been stepping through code all morning.  Figure out that most, if not all of the contact search functionality (building and running the query) is in:

CRM/Contact/BAO/Query.php

Specifically, considering the above query, the WHERE conditions are built in:

CRM_Contact_BAO_Query::whereClauseSingle()

WHERE condition for the change log is built in these two functions:

CRM_Contact_BAO_Query::modifiedDates()
CRM_Contact_BAO_Query::dateQueryBuilder()

CRM_Contact_BAO_Query::modifiedDates() is also where the subquery is added.  The problem JOINS are added in:

CRM_Contact_BAO_Query::fromClause()

I think that's all I need to make some changes to optimize here.  The query I noted above turned out to be a convenient minimal query showing the core issue.  I've since been able to see the ginormous main query for the actual contact data.  It too has the same problem JOINS and subqueries, so I'll continue using this minimal form as a way to optimize.

As noted by Lobo, the created and modified dates are stored in civicrm_contact, so for the Change Log params, we should be able to do away with any JOIN on civicrm_log.  In theory we should be able to go from this:

Code: [Select]
SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name 
FROM civicrm_contact contact_a
LEFT JOIN civicrm_log ON (civicrm_log.entity_id = contact_a.id AND civicrm_log.entity_table = 'civicrm_contact')
LEFT JOIN civicrm_contact contact_b_log ON (civicrm_log.modified_id = contact_b_log.id)
WHERE  ( civicrm_log.modified_date >= '20131016000000' AND civicrm_log.id IN ( select id from civicrm_log group by entity_id order by id ) )  AND (contact_a.is_deleted = 0)   
ORDER BY UPPER(LEFT(contact_a.sort_name, 1)) asc;

To this:

Code: [Select]
SELECT DISTINCT UPPER(LEFT(contact_a.sort_name, 1)) as sort_name 
FROM civicrm_contact contact_a
WHERE  ( contact_a.modified_date >= '20131016000000')  AND (contact_a.is_deleted = 0)   
ORDER BY UPPER(LEFT(contact_a.sort_name, 1)) asc;

m4olivei

  • I post occasionally
  • **
  • Posts: 39
  • Karma: 2
  • CiviCRM version: 4.3.5
  • CMS version: Drupal 7.23
  • MySQL version: MySQL 5
  • PHP version: PHP 5.3
Re: MySQL Resource Issue
April 14, 2014, 10:13:03 am
So I took a look at what's evolved between 4.3.5 and 4.4.4 in the functions I noted.  Turns out little has changed, except somewhere between the two versions, the subquery with the expensive GROUP BY is removed.  Tried that on the 4.3.5 copy my client is on and it got the query performant enough that the page now responds normally and returns the result instead of hanging MySQL.

Thus while it works now, could still be made better.  I'll see if I can get resources to work on this.  Will keep posted.

In the end, I'll likely patch 4.3.5 and schedule an upgrade for the client in the near future.  I'll post the patch that works for me when available.

Thanks,
Matt

Pages: [1]
  • CiviCRM Community Forums (archive) »
  • Old sections (read-only, deprecated) »
  • Support »
  • Using CiviCRM »
  • Using Core CiviCRM Functions (Moderator: Yashodha Chaku) »
  • MySQL Resource Issue

This forum was archived on 2017-11-26.