When I enable memcached for caching the field cache stops working. I can reproduce this consistently. If I turn on the devel query log I see field_data_field_* queries for every field in the node:

SELECT t.* FROM field_data_field_long_text_sum t WHERE (entity_type = :db_condition_placeholder_0) AND (entity_id IN (:db_condition_placeholder_1)) AND (language IN (:db_condition_placeholder_2)) AND (deleted = :db_condition_placeholder_3) ORDER BY delta ASC

In the memcache admin log I see the following:

Operation Bin Key Hit
get cache_field drupal7-cache_field-field_info_fields 1
get cache_field drupal7-cache_field-field_info_types%3Aen 1
getMulti cache_field drupal7-cache_field-.wildcard- 1
getMulti cache_field drupal7-cache_field-.wildcard-field_info_fields 0
getMulti cache_field drupal7-cache_field-field%3Anode%3A5000 1
getMulti cache_field drupal7-cache_field-.wildcard-field%3Anode%3A5000 0
getMulti cache_field drupal7-cache_field-.wildcard-field_info_types%3A 0
getMulti cache_field drupal7-cache_field-field%3Ataxonomy_term%3A762 1
getMulti cache_field drupal7-cache_field-.wildcard-field%3Ataxonomy_te 0
set cache_field drupal7-cache_field-field%3Anode%3A5000
set cache_field drupal7-cache_field-field%3Ataxonomy_term%3A762

I ran a simple test to see if memcache is working and it seems to be; I successfully set and returned a value in the cache_field bin.

For the time being I'm running a pretty simple setup, no bins or clusters. This is what I have in my settings.php

$conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc';
$conf['cache_default_class'] = 'MemCacheDrupal';
$conf['memcache_key_prefix'] = 'drupal7';

Like I said, memcache seems to work OK. There are hits shown in the table above and I can successfully set and get a simple string.

I've also tried both the memcache (2.2.6) & memcached (1.0.2) PECL extensions with the same result. I'm running PHP 5.2.17 with a default Drupal install (only memcache and devel are enabled, beyond the standard installed modules).

This is a pretty significant performance issue. I ran a load test by loading the same 500 nodes through both node_load() and node_load_multiple(). Here are the results:

PATH MB MEMORY (MAX) MB MEMORY (AVG) Page Load MS QUERY COUNT
node_load() Memcache 88.25 76.06 6,967.00 11017
node_load() Database 32.75 32.56 840.1 2051
node_load_multiple() Memcache 50.5 50.5 2,485.60 36
node_load_multiple() Database 42.5 30.31 266 41

In almost every category the memcache caching is significantly worse than the DB. The only place it's better is the amount of queries when using node_load_multiple() because the other cache bins seem to work.

I can't see running this on a production site considering the above results.

Any suggestions?

Comments

pillarsdotnet’s picture

Title: node_tag_new() timing dramatically affected by DrupalCacheInterface implementation -- why? » Memcache API not working for cache_field
Project: Drupal core » Memcache API and Integration
Version: 7.x-dev » 7.x-1.0-beta3
Component: node.module » Code
Category: support » bug
Priority: Normal » Major

I loaded xhprof to chase this bug down. The big difference appears to be within the node_tag_new function, which updates the 'last viewed' timestamp of the specified node for the current user.

I can't imagine why that code would be so much slower with memcache turned on, but after several re-loads of the same page to prime the cache, the timings are as follows:

node_tag_new()
(µseconds)
Database Memcache
Calls 1 1
Incl. Wall 2,560 116,826
Excl. Wall 28 28
Incl. CPU 3,000 1,000
Excl. CPU 0 0

Drilling down on the call graph, I see this further anomaly:

PDO::commit()
(µseconds)
Database Memcache
Calls 4 3
Wall 2,751 116,339
CPU 0 1,000

The actual query is identical in both cases, except of course for the timestamp:

SELECT 1 AS expression FROM history history WHERE ( (uid = '1') AND (nid = '1064') ) FOR UPDATE;
UPDATE history SET timestamp='1300661521' WHERE ( (uid = '1') AND (nid = '1064') );
EDIT:
It was suggested on #drupal that Memcache might be competing with MySql for memory usage, but I don't see how. The memcached PHP extension is statically-compiled into my version of PHP. The memcached server was running with 64MB allocated during both test runs. The only change was an edit to the settings.php file. And the VPS I was running on showed over 400MB free memory during test execution, which is greater than the size of the tested codebase and database combined. I find it hard to believe that Memcached and MySQL were fighting over memory when neither were memory-constrained.
pillarsdotnet’s picture

Title: Memcache API not working for cache_field » node_tag_new() timing dramatically affected by DrupalCacheInterface implementation -- why?
Project: Memcache API and Integration » Drupal core
Version: 7.x-1.0-beta3 » 7.x-dev
Component: Code » node.module
Category: bug » support
Priority: Major » Normal

Cross-posting for support.

pillarsdotnet’s picture

Well, that was easy. I just dumped the history table and changed "Engine=InnoDB" to "Engine=MEMORY" and reloaded it. Problem solved, and *NOW* I know what was causing my database access to be 51% write, 49% read.

pillarsdotnet’s picture

Title: Memcache API not working for cache_field » node_tag_new() timing dramatically affected by DrupalCacheInterface implementation -- why?
Version: 7.x-1.0-beta3 » 7.x-1.x-dev
Priority: Major » Normal

Per discussion in IRC it is possible that the delay is caused by the PDO::commit() waiting for another transaction. Changing the engine on the history table to anything not supporting transactions would therefore solve this problem.

I'd need to do more testing to determine exactly what transaction is held open longer with MemCacheDrupal than with DrupalDatabaseCache.

Moving back to MemCache...

pillarsdotnet’s picture

Title: node_tag_new() timing dramatically affected by DrupalCacheInterface implementation -- why? » Pages served by MemCacheDrupal hold transactions open longer than the same pages served by DrupalDatabaseCache

Title change.

catch’s picture

Status: Active » Postponed (maintainer needs more info)

SELECT FOR UPDATE could locks the row from being selected in some circumstances. See #910010: Potential race condition on variable_set() due to merge query implementation for some discussion.

Please take a look at that issue and see if it describes your problem. Were you doing this profiling while real traffic was hitting the server? If you were, it's possible that memcache freed up MySQL to the point where this became a bottleneck - then because you were able to handle the additional writes it hit this locking, then slowed the application down overall. However this is just conjecture at the moment.

Berdir’s picture

Status: Postponed (maintainer needs more info) » Fixed

Not sure why the focus of this issue seems to have changed completely, but the originally reported bug is now fixed and was related to a bug in getMulti(), which was completely broken.

And since nothing has happened here for many month, I'm marking this as fixed, feel free to re-open.

Status: Fixed » Closed (fixed)

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