It seems the page cache never get's cleared for certain pages, explicitly a views page. I tested like this: decrease cache_lifetime to 1 minute, call "clear cache" and/or `drush php-eval "cache_clear_all();"` and wait a couple of minutes; no change for anonymous users at all :(

Relevant settings.php:

  $conf['cache_backends'][] = './sites/all/modules/contrib/memcache/memcache.inc';
  $conf['cache_default_class'] = 'MemCacheDrupal';
  $conf['cache_class_cache_form'] = 'DrupalDatabaseCache';
  $conf['memcache_key_prefix'] = 'my_cache';
  $conf['memcache_persistent'] = TRUE;
  $conf['memcache_servers'] =
    array('127.0.0.1:11211' => 'default',
      '127.0.0.1:11212' => 'content',
      '127.0.0.1:11213' => 'menu',
      '127.0.0.1:11214' => 'views',
    );
  $conf['memcache_bins'] =
    array('cache' => 'default',
      'cache_page' => 'content',
      'cache_entity_comment' => 'content',
      'cache_entity_file' => 'content',
      'cache_entity_node' => 'content',
      'cache_entity_user' => 'content',
      'cache_entity_taxonomy_term' => 'content',
      'cache_menu' => 'menu',
      'cache_views' => 'views',
      'cache_views_data' => 'views',
    );

  $conf['page_cache_invoke_hooks'] = FALSE;
  $conf['page_cache_without_database'] = TRUE;
  #APC
  /**
   * Add APC Caching.
   */
  $conf['cache_backends'][] = 'sites/all/modules/contrib/apc/drupal_apc_cache.inc';
  $conf['cache_class_cache'] = 'DrupalAPCCache';
  $conf['cache_class_cache_bootstrap'] = 'DrupalAPCCache';
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

cinnamon’s picture

It seems to work if I add this to the clear function for the cache_clear_all() part:

dmemcache_flush($this->bin);

Is there any reason why the clear function should only work for the current user and only if variable cache_lifetime is set? At least this is how I read the current code:

    // It is not possible to detect a cache_clear_all() call other than looking
    // at the backtrace unless http://drupal.org/node/81461 is added.
    $backtrace = debug_backtrace();
    if ($cid == MEMCACHE_CONTENT_CLEAR || (isset($backtrace[2]) && $backtrace[2]['function'] == 'cache_clear_all' && empty($backtrace[2]['args']))) {
      // Update the timestamp of the last global flushing of this bin.  When
      // retrieving data from this bin, we will compare the cache creation
      // time minus the cache_flush time to the cache_lifetime to determine
      // whether or not the cached item is still valid.
      $this->cache_content_flush = time();
      $this->variable_set('cache_content_flush_' . $this->bin, $this->cache_content_flush);
      if (variable_get('cache_lifetime', 0)) {
        // We store the time in the current user's session. We then simulate
        // that the cache was flushed for this user by not returning cached
        // data to this user that was cached before the timestamp.
        if (isset($_SESSION['cache_flush']) && is_array($_SESSION['cache_flush'])) {
          $cache_bins = $_SESSION['cache_flush'];
        }
        else {
          $cache_bins = array();
        }
        // Use time() rather than request time here for correctness.
        $cache_tables[$this->bin] = $this->cache_content_flush;
        $_SESSION['cache_flush'] = $cache_tables;
      }
    }
Jarek Polok’s picture

We see same problem here, drupal 7.14, memcache 7.x-1.0.

paullomax’s picture

We're having the same problem.

If you change
$conf['page_cache_without_database'] = TRUE;
to
$conf['page_cache_without_database'] = FALSE;

Then it works as expected.

But that will no doubt affect performance as Drupal does a bit more bootstrapping even if the page is cached.

I'm guessing it's because when it's set to TRUE, Drupal doesn't load the variables, and memcache is using a variable somewhere when grabbing cached pages?

(I believe the reason it doesn't flush the cache is because it would do this every time cron runs)

girishmuraly’s picture

To confirm #3, when $conf['page_cache_without_database'] = FALSE; drupal bootstraps the variables, as can be seen here http://api.drupal.org/api/drupal/includes!bootstrap.inc/function/_drupal_bootstrap_page_cache/7

ruedu’s picture

I think I'm seeing this exact same issue on our site. Pages cached for anonymous users won't expire. I tested #3 and it doesn't appear to work on my end.

girishmuraly’s picture

The following sort of works for us ('sort-of' because we have other settings & are not sure if this is the real fix). Would be good to get confirmation if it helps others. Basically we found that drupal was not setting the correct expiry via memcache as it relied on the 'Expires' header in drupal_page_set_cache

Put this into a module:

/**
 * Implementation of hook_preprocess_page
 * Adds Expires header based on the configuration of Minimum Cache Lifetime
 */
function mymodule_preprocess_page(&$variables) {
  global $user;

  if ($user->uid == 0) {
    if (variable_get('cache_lifetime', 0)) {
      $expire = REQUEST_TIME + variable_get('cache_lifetime', 0);
      drupal_add_http_header('Expires', gmdate(DATE_RFC1123, $expire));
    }
  }
}

Flush all cache & memcache and try again.

xavier_g’s picture

Hello,

Here is how I understand this bug, or at least the impact of cache_page_without_database:

  • Most content update will trigger cache_clear_all(); (e.g. from node_form_submit())
  • Called in this way, this is equivalent to:
    • _cache_get_object('cache_block')->clear(NULL, FALSE);
    • _cache_get_object('cache_page')->clear(NULL, FALSE);
  • MemCacheDrupal::clear() will simply store the current time into:
    • its cache_content_flush attribute, for next calls to MemCacheDrupal::get() -- the Memcached module does not invalidate entries into Memcached, it simply instructs get() to return false for entries created before the last required flush
    • the variable cache_content_flush_cache_page (or cache_block) is used to make this date persistent
      and here lies the problem: since it is stored as a standard Drupal variable (and potentially cached in another bin, by another cache class or even not cached at all), it is theoretically unreachable without connecting to the database and bootstrapping the variables, even though it is potentially still available within Memcached.

    Still, I wonder whether some or all of the variables mentioned in MemCacheDrupal::reloadVariables() could be systematically stored into Memcached, without any dependency to Drupal variables. Another approach consists in trying to load variables from Memcached, and connecting to the database only if it fails...

    P.S.: by the way, in the code quoted by #3, shouldn't it be $cache_bins instead of $cache_tables?

marcelovani’s picture

Issue tags: +Drupal, +caching, +page cache, +memcache
FileSize
58.26 KB
111.9 KB
134.66 KB

I am attaching a fluxogram of what happens during bootstrap, which means, the variables get loaded during phase 3
Also, attaching a DOC version with links to the actual code and another fluxogram of how memcache works.
ps: all of these are todo only with page cache, other areas of caching havent been covered

cinnamon’s picture

PS it seems APC caching was causing some additional troubels in our case, that is with APC caching we were completely unable to flush the cache when needed. For example after a theme upgrade.

At the moment we are using the memcache module in production again.

andyhu’s picture

I've seen the same issue as well. When using `drush cc all`, the cache isn't cleared. It seems not just for page cache but all caches handled by memcache is not cleared.

marcelovani’s picture

giorgio79’s picture

Maybe an integration with http://drupal.org/project/expire would help...

gstout’s picture

Will this work for d6?

stompeduns’s picture

Comment out:

  $conf['cache_backends'][] = 'sites/all/modules/contrib/apc/drupal_apc_cache.inc';
  $conf['cache_class_cache'] = 'DrupalAPCCache';
  $conf['cache_class_cache_bootstrap'] = 'DrupalAPCCache';
yelgulwa’s picture

Hello,

I am facing a similar problem but I am not sure if the cause is cached variables. Let me explain the symptoms.

* Core - 7.22 Memcache - 7.x-1.0
* When a new article is added, it never appears in a views stream after the cache lifetime.
* I set it to 1 min but it takes much longer to appear unless the cache is explicitly cleared.
* Two particular bins (bootstrap and filter) are dangerously full (close to 99%) and there are 0 evictions from there.

It would be great if someone facing similar issues could post some suggestions.

Thank you,
Swati

fago’s picture

Title: Page cache never expires » Page cache never expires if page_cache_without_database is enabled
Version: 7.x-1.0 » 7.x-1.x-dev
Issue summary: View changes
Priority: Normal » Major

Bumping to major as this affects everyone who uses memcached for page cache and has page_cache_without_database set to TRUE.

Jeremy’s picture

Assigned: Unassigned » Jeremy

Duplicated; agreed it's a major bug. Assigning to myself to fix.

Jeremy’s picture

Status: Active » Needs review
FileSize
4.66 KB

Please test the attached.

catch’s picture

This adds a bit of extra overhead with this setting compared to not having it, but I can't think of another option except storing that information outside the variables system altogether - and that'd still be an extra round trip.

      if ($cached = cache_get('variables', 'cache_bootstrap')) {
+        $variables = $cached->data;
+        foreach ($conf as $name => $value) {
+          $variables[$name] = $value;
+        }
+        $conf = $variables;
+      }

Since we only care about a couple of variables here, what about just picking those out rather than the foreach() over all variables?

Jeremy’s picture

This extra loop only happens when page_cache_without_database is TRUE -- when it's FALSE, this loops still happens but in bootstrap.inc while bootstrapping variables.

I'd not expect this to have a measurable impact, except perhaps on a website with a massive $conf array... and IMO that's a user error anyways. Further, the variables we care about are not static (they depend on configuration), and figuring out on each bootstrap which variables are required is likely comparable overhead -- again, except in the case of an over-sized $conf array.

I suppose the thing to do here is to set up a load test and compare...

fago’s picture

Yes, I do not see a performance problem with this loop either. It's the usual loop keeping $conf overrides and won't affect installations without the variable set.

Attached is an updated patch with variable namings that make more sense to me and some comments explaining whats going on. Then, I removed the manual call to _drupal_bootstrap_page_cache() - not sure why we'd need that?

Patch seems to do its job for me and offers the same performance as before, but with working cache clears.

Jeremy’s picture

Status: Needs review » Fixed

Thanks, fix committed:
http://drupalcode.org/project/memcache.git/commit/a534d3d

I'd hoped for more confirmation from testers; I'll roll a Beta to make this easier.

Status: Fixed » Closed (fixed)

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

drasgardian’s picture

If using APCCache for cache_class_cache_bootstrap (as per #14), this problem still persists when clearing caches with drush.

This is because drush can't clear the APC cache, as discussed here: https://www.drupal.org/node/1565716

Can work around this by commenting out this line in settings.php

$conf['cache_class_cache_bootstrap'] = 'DrupalAPCCache';