Problem/Motivation

The following pattern can be seen through-out the module's code for D7:

$settings = array();
$data = db_query("SELECT settings FROM {domain_conf} WHERE domain_id = :domain_id", array(':domain_id' => $domain['domain_id']))->fetchField();
if (!empty($data)) {
  $settings = domain_unserialize($data);
}

On a particular portal solution, this is the 5th most time-consuming query (based on NewRelic's monitoring). See stats on the attached screenshot. For some reason it appears that the query is invoked 2 times per request, as the system's throughput is between 600-800 requests per minute, but the query is about 1500-1600. Note that SQL's query cache could have been a very good fit for exactly this functionality, but due to the amount of editors that we have, the system is much more responsive with SQL's query cache disabled, so this queries are executed always.

The lack of centralized function that is making the call is not easing the debugging process.

Proposed resolution

The data provided from this is rarely changed (once configured), making it a very good candidate for caching. It would be very nice to have domain_conf_data_get, domain_conf_data_delete and domain_conf_data_set utilities that will handle the CRUD operations and also the cache layers storage and consistency between them.

Static cache for minimizing network requests to not more than one on standard read scenario.
Persistent cache (cache_api) to offload the DB from read traffic, when possible - out-scoped to #2735661: Domain conf - issues with persistent caches..

Remaining tasks

Patch, reviews, RTBC, commit.

User interface changes

None.

API changes

Change of the access procedures for accessing domain configurations, so modules will use functions instead of RAW db queries.

Data model changes

None.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

ndobromirov created an issue. See original summary.

ndobromirov’s picture

Title: Domain conf perfrmance » Domain conf - bad SQL perfrmance
Status: Active » Needs review
FileSize
14.66 KB

Updated title.
Implemented the CRUD utilities mentioned in the solution proposal.
Added one more for deletion.
Refactored the code in the module to utilize the new code-base, it shrank substantially (at least in line length).

Uploading patch.
TODO: Add the cache logic on top of it.

ndobromirov’s picture

Renamed the CRUD utilities to follow the same namespace: domain_conf_data_(get|set|delete).
Added static and persistent cache.
Attaching patch and diff to the previous one from #2.

ndobromirov’s picture

Fixed a small issue in the cache invalidation logic in the delete.
Ignore patch in #3.

ndobromirov’s picture

- Added new utility parameter to the set method called $merge, defaults to TRUE. When TRUE, it will merge the new settings on top of the old ones. When FALSE, it will overwrite the whole settings array with the new value.
- Improved docks.
- Refactoring and fixes to utilize correctly the new $merge parameter and the previously introduced $reset in the get utility.
- Uploading a new patch and diff to the previous one.

ndobromirov’s picture

Issue summary: View changes

Updated description with more information.

ndobromirov’s picture

ndobromirov’s picture

Fix typos, causing notices... Uploading patch and diff to previous.

ndobromirov’s picture

Status: Needs review » Needs work

TODOs I came up with for the last week.
- The use if the 3 new methods needs to be checked thoroughly again for the need to proxy some reset calls (maybe).
- The persistent cache should be moved to cache_bootstrap, instead of the default cache bin. This is because variables API in core is using it and data we will be holding there is pretty much the same kind (configuration). This is expected to improve bootstrap speeds when used with some advanced cache configurations even more. Ones like this.

ndobromirov’s picture

So here is the new patch, apparently there were issues...
1. After the re-audit I've found 1 place, where a reset flag was not passed correctly.
2. Moved the cache bin the cache_bootstrap.

Uploading patch and diff to #8. Hiding old ones.

ndobromirov’s picture

Status: Needs work » Needs review

Forget to change the status.

agentrickard’s picture

Nice work. Do we have any form of test coverage?

ndobromirov’s picture

None at the moment...

ndobromirov’s picture

Status: Needs review » Needs work
Issue tags: +Needs committer feedback

Do we have any form of test coverage on the old functions, as this is just data acquisition low level layer. It replaces a DB query. So if the old tests pass this will not cause regressions. If there are no old tests, I can not see the scope of this issue to cover that. How should we proceed with that?

It will not be trivial to cover everything, but that's for a separate issue anyway.

As there are 3 new functions I agree that they need to be covered, so I am marking as needs work - to add the testing for the new API.

ndobromirov’s picture

After reviewing the code again I think that:
1. domain_conf_features_rebuild this can be further optimized to make only 1 write query (delete or update), not up-to 2 as it is now (delete and maybe insert).

2. The domain_conf_data_delete should force (int) cast to the domain_id passed to the DB. Maybe add input param validation, as we have int and 'all' and currently we trust the developers that the method will be used correctly.

agentrickard’s picture

Right, we don't have great coverage of Domain Conf in D7, so that may be a high barrier. I'm ok with manual testing, though maybe just a simple test around the internal caching is what I was thinking.

* Did the data get cached?
* Is it retrieved from cache as expected?
* Is the cache cleared when called to?

ndobromirov’s picture

Hi, here are the tests against this new API.

During the testing, there seems to be an issue with either memcache (in my local set-up - not likely, as it a way too simple to configure...) or this functionality in general against any custom cache-api back-end. With my settings.php like so:

/**
 * Add the domain module setup routine.
 */
include DRUPAL_ROOT . '/sites/all/modules/domain/settings.inc';

// Memcache...
$conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc';
$conf['memcache_servers'] = array('127.0.0.1:11211' => 'default');
$conf['memcache_key_prefix'] = $databases['default']['default']['database'];
$conf['cache_default_class'] = 'MemCacheDrupal';
$conf['cache_class_cache_form'] = 'DrupalDatabaseCache';

All the new tests pass. With the settings configured like this:

// Memcache...
$conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc';
$conf['memcache_servers'] = array('127.0.0.1:11211' => 'default');
$conf['memcache_key_prefix'] = $databases['default']['default']['database'];
$conf['cache_default_class'] = 'MemCacheDrupal';
$conf['cache_class_cache_form'] = 'DrupalDatabaseCache';

/**
 * Add the domain module setup routine.
 */
include DRUPAL_ROOT . '/sites/all/modules/domain/settings.inc';

They don't due to persistent cache not working (cache-miss during the tests)...

It seems that it works with DB as cache back-end (as the custom one is not configured yet), for alternative cache back-ends it might be either too-early in the bootstrap or not well thought out (or my local set-up issue)...

Too strange to debug in the evening :).

Fast workaround would be to have static cache only, but this will negate most of the positives from this patch, as DB will still be accessed for this type of data. Still code structure will be greatly improved. Any feedback and also testing at your end with / without memcache would be greatly appreciated :)

Notes 1 & 2 from #15 are still pending, as I was stuck on the described issue with the cache_api.

I will be able to look into this next Sunday (maybe) if there is no progresses.

ndobromirov’s picture

Status: Needs work » Needs review
sk2013’s picture

@ ndobromirov.

It seems its a great Idea. We are using Domain access without memcache.

I am not sure on how to test this patch. The following is the way to test this patch? Please confirm:

1.measure performance of loading same node (page load)before applying the patch. (Question is how to measure. Can we use pagespeedtest)
2. After applying patch measure performance of same page loading.

Is this the right way - please suggest.

Thanks for taking this up

agentrickard’s picture

That issue in 17 is probably due to how Domain Conf loads its data. I suspect the custom $conf settings are being overwritten, so including them _after_ the domain loader script is the correct configuration.

We should probably document that.

ndobromirov’s picture

#19 The best way to test for back-end performance is for time to first byte with AB or even better - XHProf as it will give more insight. Note that page cache should be disabled. In #17 I was referring to testing in the way the patch works with memcache at all or not, as I had issues as already described. Apparently not with the memcache instance :( .

#20 I think we need to fix the issue with the overriding (if it's an issue). If just a docks change is OK then.

But... The issue here was the need to make the query to hit a cache layer and not the DB. As it is, with the working scenario, the domain_boostrap(); will utilize DB for extracting the data and then we will have memcache configured.

In the docks at the moment there is clearly stated that usually the line should be at the end:

IMPORTANT: You must add these lines to settings.php after the
$databases. Normally, you should add these lines to the end of the
settings.php file.

So it is expected to have it as the currently broken case (configs at the end).
@agentrickard Can you confirm this, or this will need more debugging :D

ndobromirov’s picture

Other way to make this moving and have some progress is to out-scope the persistent cache into a follow-up, as it's a clearly a separate issue that is more complex.

agentrickard’s picture

Agreed. We should move the memcache issue to something separate.

I don't typically work with memcache locally, so testing will be an issue.

ndobromirov’s picture

Here is the new patch with persistent cache layer out if it + a partial diff.
The new tests now pass always.
The notes from #15 are fixed.
I've opened the issue that we've discussed and agreed in #22 and #23 as postponed on this one.
I've hidden all the patches except the latest ones from the issue description.
Updated issue summary - proposal section to note the new child issue.

ndobromirov’s picture

@agentrickard, did you have any time to test this?

agentrickard’s picture

No. I've been working on Drupal 8.

agentrickard’s picture

I cleaned up the comments a bit but otherwise, this is great!

agentrickard’s picture

Status: Needs review » Fixed

Committed! Nice work!

2b199fc..8928f42 7.x-3.x -> 7.x-3.x

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

xjm’s picture

Issue tags: -Needs committer feedback