I was logging my chaches, and it seems to me that there are many, many calls to DrupalCacheInterface::set() by the Countries module.

When I have a view with countries, a set() is done for the selected countries.
When I show a node, a set() is done for all(!) countries.
This is with and without the EntityCache patch from #1397762: Entity cache integration.

You can use below change to get the data on screen (PS dpm() does not work , so I used print() .)

--- includes/cache.inc	(revision 685)
+++ includes/cache.inc	(working copy)
@@ -409,6 +409,7 @@
   }
 
   function set($cid, $data, $expire = CACHE_PERMANENT) {
+print(__FILE__ .' '. __FUNCTION__ . ' ' . $cid . '<br>');
     $fields = array(
       'serialized' => 0,
       'created' => REQUEST_TIME,
CommentFileSizeAuthor
#2 cache_set_create.txt21.7 KBjohnv

Comments

alan d.’s picture

Are there corresponding database calls too? The entire countries array should be bulk loaded in a singular hit and then passed out from the cached version as required.

I can not replicate any excessive overheads here. Of the 455 DB calls, only 4 were related to the countries load, 3 bulk loads and 1 singular load from a node displaying a countries field.

johnv’s picture

StatusFileSize
new21.7 KB

The problem does not seem to be with get() or multipleGet()-functions, since that is normal.
But ther is a lot of set()'s when showing data.

I send you a file, for the following case:
- Add a Countries field to a page 'Basic page' (from default D7 installation)
- go to node/add/page to create the page (not even showing it!)
- I saved the print-lines in the file.

Some other module generates set() for $cid 1 to 18 - don't know yet what that is.

alan d.’s picture

Component: Code » Documentation
Category: bug » support
Status: Active » Fixed

And when you load the page for the second time, there are none :)

Every fieldable entity gets cached, and we bulk load countries in a single hit to avoid multiple individual database calls per country as that is much worse. So that's the 250 calls for the first load and none afterwards.

Regards single c/f all. This is a very non-scientific test based off 1 page load:

0.4ms for a single db call to get id / title (and no field loading)
8.4ms for the complete 253 country & field loads

20 times slower, but you get 250 times as much data (ie countries and fields), or 0.03 ms per country

I flushed the cache on this site while on a typical development site, nothing too special, a couple of sliders, few blocks, etc on a panel page. There were over 2000 db calls, and ~1/4 were due to node and file [the entity behind both files & images] entity field cache sets. Second load, there were 374 db calls [menu / path modules accounts for much of these], but only 3 cache sets calls.

So this is just how Drupal works if you want fieldable entities.

alan d.’s picture

Assigned: Unassigned » alan d.
Category: support » task
Status: Fixed » Active

Actually, I may have a look at the option widgets to see if these can be optimized. This is what is causing the high load on a single hit.

johnv’s picture

And when you load the page for the second time, there are none :)

You wish... every refresh gets the same sets :-(
So, it doesn't happen on your site? If so, I will try and remove my caching-module 1-by-1.

[Edit]And I am not sure you understand: it's not about GETting caches, that's OK - it is about SETting caches.
You do, I didn't read #3 carefully enough.

johnv’s picture

O ja,
my Devel Query Log shows only 1 query with 'countr' in it:
1.041
EntityAPIController::queryP A E
SELECT base.cid AS cid, base.iso2 AS iso2, base.iso3 AS iso3, base.name AS name, base.official_name AS official_name, base.numcode AS numcode, base.continent AS continent, base.enabled AS enabled, base.language AS language FROM countries_country base

The countries set()s are like:

DrupalDatabaseCache::set
SELECT 1 AS expression FROM cache_field cache_field WHERE ( (cid = 'field:country:165') ) FOR UPDATE

The 18 other calls are taxonomies (revealed after pressing the P/A/E characters in Devel query log):

DrupalDatabaseCache::set
SELECT 1 AS expression FROM cache_entity_taxonomy_vocabulary cache_entity_taxonomy_vocabulary WHERE ( (cid = '8') ) FOR UPDATE 
webflo’s picture

I think the implementation is good. Loading ~250 at once should not be an issue.

@johnv Have you found the problem? Can reproduce. Everything works as expected.

alan d.’s picture

@John

Nodes, users, files (images included), terms, vocabs, countries, and ..... are entities. Every single one will trigger a cache set if it is loaded for the first time.

load_xxx()
load_fields()
-- foreach field, load field
cache_set()

Then if cached, there should be no cache sets, just

load_xxx()
load_cached_fields()

Or

load_xxx_multiple()
load_cached_fields_multiple()

If you see 253 cache sets on every page (each time you visit "node/add/content-with-country-field") then there is an issue with the caching on your system, ie: the cache is not being used.

johnv’s picture

OK,
I will check my system, thanks.

alan d.’s picture

OK, as promised, here are the results.

Currently, the existing code takes 60ms for the first time (full entity load), and this drops to 9 - 10ms for additional calls.

The widget options list can easily be done via direct DB calls:

function countries_allowed_values($field) {
  $query = db_select('countries_country', 'c');
  $query->distinct();
  $query->fields('c', array('iso2', 'name'));
  $query->orderBy('c.name', 'ASC');
  if (!empty($field['#filters'])) {
    countries_filter_query_alter($query, $field['#filters'], 'c');
  }
  $countries = $query->execute()->fetchAllKeyed(0, 1);

  // Allow other modules to update this list.
  countries_invoke_additional_countries_alter($countries);

  return $countries;
}

Execution time: 2.5 to 3.5 ms

Bypassing the countries module

  $s = microtime_float();
  include_once DRUPAL_ROOT . '/includes/locale.inc';
  $list = country_get_list();
  $e = microtime_float();
  dpm("Executed full load in " . (($e - $s) * 1000) . ' ms');

First time that I ran this, I got an execution time: 160-170ms. But that appeared to be a one off.

Generally, times were around 60-80ms, a few in the 90-120ms range, for the first load, then dropped significantly.

Overview:

Direct DB: x1
First load: x20
Additional loads: x3

No Countries Module
First load: x20 to x23
Additional loads: x2.5

No significant difference between the including the countries module with a full entity load and the iso file that only contains the iso2 code and name! That was totally unexpected! Having the data in the database is faster than reading it off the disk.

I tried to by bypass multiple calls to $countries = entity_load_multiple_by_name('country'); using a local static variable, but there were no noticeable differences.

OK, and looking at loading individual countries rather than mass loading:

function microtime_float() {
  list($usec, $sec) = explode(" ", microtime());
  return ((float)$usec + (float)$sec);
}

  $s = microtime_float();
  for($i = 1; $i <= 250; $i++) {
    entity_load_single('country', $i);
  }
  $e = microtime_float();
  dpm("Executed full load in " . (($e - $s) * 1000 / 250) . ' ms');

This gave a load time of 3.5ms per country, again about 1/20th of the load time of the full countries array.

What to make of all this?

If we display only 1 or 2 countries, then by passing the bulk load is good. After maybe 15 or so, we start seeing additional overhead.

In terms of the widget, well a one off load looks ok to me too. Higher, but uses the tested code.

alan d.’s picture

lol, I knew that there was something fishy. The results were too good!

OK, after removing the countries module country alter hook, the core times dropped by a seventh and additional loads were half that of the direct db call.

Overview:

Direct DB: x1
Full entity load (first time): x20
Additional loads: x3

No Countries Module involvement.
First load: x3
Additional loads: x0.5

alan d.’s picture

Status: Active » Closed (works as designed)

Testing appears to show no issues here. If caching is 100% disabled, then yes, there would be a cache set for every field for every entity on every load. This is a global issue related to the specific developmental tools enabled (ie. Drupal core with no caching caches fields on entities).

Follow-up caching issues in: #1397762: Entity cache integration

johnv’s picture

Status: Closed (works as designed) » Active

Let's keep this open.

ATM I think the function cacheGet($ids) in entity.controller.inc misses the cache in table cache_field. It is called each time by function load($ids) in the same include.

That is because in a node-edit page, the following happens subsequently:
- get 1 country for the set-value
- get all countries (for the options list of the countries widget)
- get all countries again
Each time the cacheGet() is missed (reading static cache); the data is read from DB and the cache is set.

IMO we have 3 levels of caching. I can't figure out exactly when each of the data is read, but IMO the usage of cache_field is corrupt:
1- persistent data in table countries_country
2- cached data in cache_field, which is set very time but never read
3- static cache in $this->entityCache, used in cacheGet()

PS1. countries_load() and countries_load_multiple() are not used . Are they obsolete?
PS2. I am not sure if the error is in Entity or in Countries, since cache_entity_taxonomy_vocabulary has the same behaviour (see#6)

alan d.’s picture

Status: Active » Closed (works as designed)

Can you replicate this on a clean installation? I definitively could not replicate this myself on either a clean installation or a complex one (with 150 modules enabled, yes 150).

You can see if there is a difference between countries_load_multiple() and entity_load_multiple_by_name('country'), both do the same thing and I'm not sure why the second was chosen.

Anyways, this is not related to this module and there is something corrupt with yours for repeatedly resetting the cache, as you said, this also effects other fields. Try disabling all devel modules using the admin menu, then slowly disabling other modules one by one until you find the module responsible. It may not be something directly related to the entity or caching system.

johnv’s picture

Alan,

I checked this again with latest version of countries module and Drupal 7.15.
( and without #1397762: Entity cache integration )
It is OK, now.
I only get the following queries, which seem OK:
EntityAPIController::query SELECT ... FROM countries_country base WHERE (base.iso2 IN ('EG'))
EntityAPIController::query SELECT ... FROM countries_country base

alan d.’s picture

Thanks for the feedback. Nothing has changed here to assist the issue, I'm guessing there were other updates other than just Drupal core and this one, and the issue was fixed here. (Nothing from core that I have tracked would have contributed to this issue either)

But great news. I guess you would be seeing a better performance from your site!