Unlike cache_clear_all in cache.inc and memcache.db.inc, the cache_clear_all in memcache.inc does not result in cache_page being cleared of temporary objects when the default system_cron runs (via cron.php).

Basically it is not respecting the call of cache_clear_all(NULL, 'cache_page'), and pages only get flushed if called as cache_clear_all('*', 'cache_page', TRUE)

Was there a specific reason for it to behave this way? before I go digging for the right way to "fix" this issue for me I want to make sure that there is not something that I am missing. I would have expected cache_clear_all to more or less behave exactly as the core one would, and it is even more confusing since is behaves differently than the database backed memcache.

Comments

febbraro’s picture

It should also be noted that my memcache config is

  'cache_inc' => './sites/all/modules/memcache/memcache.inc',
  'session_inc' => './sites/all/modules/memcache/memcache-session.inc',
  'memcache_servers' => array(
    'cache1:11211' => 'default',
    'cache1:11212' => 'menu',
    'cache1:11213' => 'filter',
    'cache1:11214' => 'page',
    'cache1:11215' => 'views',
    'cache1:11216' => 'content',
    'cache1:11217' => 'form',
    'cache1:11218' => 'session',
    'cache1:11219' => 'users',
  ),
  'memcache_bins' => array(
    'cache_page'    => 'page',
    'cache_menu'    => 'menu',
    'cache_filter'  => 'filter',
    'cache_views'   => 'views',
    'cache_content' => 'content',
    'cache_form'    => 'form',
    'session'       => 'session',
    'users'         => 'users',
  ),
febbraro’s picture

Right, so cache_clear_all() still works for clearing the page and block cache after node saves b/c it calls back on itself with cache_clear_all('*', 'cache_page', TRUE). In the core version this would call cache_clear_all(NULL, 'cache_page') as a mechanism for clearing expired entries. system_cron calls to clear the page cache with cache_clear_all(NULL, 'cache_page') but that call no longer clears expired entries, it lands in no mans land and essentially does nothing.

  FROM: memcache.inc

  if (!isset($cid) && !isset($table)) {
    cache_clear_all('*', 'cache_block', TRUE);
    cache_clear_all('*', 'cache_page', TRUE);  <=========== THIS IS HOW PAGE CACHES GET CLEARED
                                                               NOT THE SAME AS system_cron SO A CRON RUN
                                                               WILL NOT CLEAR CACHED PAGES
    return;
  }
  if (empty($cid) || ($cid == '*' && $wildcard !== TRUE)) {  <===== PASSES THIS TEST B/C CID IS NULL

    // don't do anything if cid is unset. this matches the default drupal behavior...
    if ($wildcard && $cid != '*') {                                       <======= FAILS THIS TEST B/C WILDCARD IS FALSE
      if (variable_get('memcache_debug', FALSE)) {
        // call watchdog, since you probably didn't want to flush the entire bin.
        watchdog('memcache', "illegal wildcard in cache_clear_all - not flushing entire bin. table: $table. cid: $cid", WATCHDOG_WARNING);
      }
    }
    <================ DOES NOTHING AND EXITS
  }
  else if ($cid == '*' || $wildcard === TRUE) {
  ...
FROM memcache.db.inc

  // Default behavior for when cache_clear_all() is called without parameters
  // is to clear all of the expirable entries in the block and page caches.
  if (!isset($cid) && !isset($table)) {
    // Clear the block cache first, so stale data will
    // not end up in the page cache.
    cache_clear_all(NULL, 'cache_block');
    cache_clear_all(NULL, 'cache_page');  <=============== DIFFERENT DEFAULT BEHAVIOR THAN memcache.inc
                                                           BUT EXACTLY THE SAME AS system_cron SO CRON WILL
                                                           RESULT IN CACHE CLEARING FOR PAGES/BLOCK
    return;
  }

  if (empty($cid)) {  <======================== PASSES THIS TEST
    if (variable_get('cache_lifetime', 0)) {  <===============FAILS THIS TEST
      // We store the time in the current user's $user->cache variable which
      // will be saved into the sessions table by sess_write(). We then
      // simulate that the cache was flushed for this user by not returning
      // cached data that was cached before the timestamp.
      $user->cache = time();

      $cache_flush = variable_get('cache_flush', 0);
      if ($cache_flush == 0) {
        // This is the first request to clear the cache, start a timer.
        variable_set('cache_flush', time());
      }
      else if (time() > ($cache_flush + variable_get('cache_lifetime', 0))) {
        // Clear the cache for everyone, cache_flush_delay seconds have
        // passed since the first request to clear the cache.
        db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
        variable_set('cache_flush', 0);
        // Note there is no memcache flush here.
        // Items are cached in memcache with an expiry equal to cache_lifetime
        // and memcache will expire these automatically.
      }
    }
    else {
      // No minimum cache lifetime, flush all temporary cache entries now.
      // Note, however, that memcache does not have the ability to discern
      // between CACHE_PERMANENT and CACHE_TEMPORARY items. So having no
      // minimum cache lifetime means that all CACHE_PERMANENT items are
      // lost here, too. To avoid that, set a minimum cache lifetime.
      dmemcache_flush($table);     <==============================FLUSHES CACHE HERE
      db_query("DELETE FROM {". $table ."} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time());
    }
  }
  else {

I hope the above makes sense. :)

febbraro’s picture

Priority: Normal » Critical

Bump. Wondering if anyone has thoughts in this one, this is a pretty major bug if you don't use DB backed memcache.

jeremy’s picture

Status: Active » Closed (works as designed)

Memcache handles cache expiration itself, so we can safely ignore the calls to cache_clear_all from system_cron(). I don't see a bug here.

febbraro’s picture

Status: Closed (works as designed) » Active

The bug, as I'm experiencing it, is that the Drupal page cache is supposed to clear cached pages on cron runs. The memcache only version of cache.inc does not do this (but the db backed version does) thus once a page is cached it will never get flushed from the cache unless there is another event that causes cache purging (like a node save for example). The memcache-only version does not seem to expire these entries by itself, I have done some extensive testing of this. It does not seem right that the 2 different versions of cache.inc provided by this module function differently on cron runs.

How does the memcache-only version of cache.inc expect/handle the expiration of cached pages when no minimum ttl is set?

Thanks for helping.

crea’s picture

Subscribing

crea’s picture

I understand that flushing whole page cache on every cron event could be big performance hit for some sites. Posting here example of breakage for future reference, since there could be other modules expecting page_cache flushing on cron: http://drupal.org/node/504552#comment-2609926
Maybe we should add some configuration variable that allows to select between "flush cache_page on cron as core does" and "flush cache_page only during update events and set cache lifetime to N where N is 30 days by default".

UPDATE: it should be rather "flush expired data in tables immediately on cron as core does" and "flush expired data only during update events and set cache lifetime to N where N is 30 days by default", because this problem is related to all cache tables which are flushed like cache_clear_all(NULL, $table).

crea’s picture

Status: Active » Closed (works as designed)

@febbraro
Memcache-only implementation does expiration too, but is setting expire time to 30 days which is unreachable for most sites. That's why you can't spot this during testing ;)
Btw, the whole idea of DB backed up Memcache was total crap, if you ask me. Why implement lightning fast Memcache and then block yourself in slow DB ? Fail-over ? Bullshit, for that there are Memcached distributed clusters. Waiting for slow DB cache operations together with Memcached operations totally kills the point of having Memcached.
So just skip that, and forget about DB part ;)

jmseigneur’s picture

So is it possible to manually clear the caches with memcache.inc without having to wait 30 days? I've tried the different buttons to clear the caches but it seems that the caches are never cleared manually.

mafioso’s picture

Category: bug » feature
Status: Closed (works as designed) » Active

I'm experiencing the same problem using memcache.inc, so i use memcache.db.inc at the moment.

If Jeremy and crea think that this is not a bug, i would say this is a critical feature request :)

jeremy’s picture

Status: Fixed » Closed (fixed)

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