The Authcache Builtin Storage Backend integrates with the Cache Expiration module. This combination allows to selectively remove pages from the cache after a node is edited while a minimum cache lifetime is maintained for other pages.

The Authcache Enum module is responsible for calculating all the possible authcache keys, such that all variants of a page are cleared from the cache. This module currently does not calculate the keys correctly and therefore cache clearing for authenticated users currently does not work.

I think the attached patch should fix cache invalidation even when the site is not installed in the document root but within a subdirectory.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

znerol’s picture

Status: Active » Needs review
Issue tags: +Need tests
FileSize
578 bytes

The attached patch should fix the issue.

znerol’s picture

Issue summary: View changes
znerol’s picture

Status: Needs review » Fixed

Committed and pushed cf5bebb.

RAFA3L’s picture

I already test the last dev version (with the committed patch) and the problem continue, only clear the anonymous cache (same with files or database)

In the Expire module I try with Internal and External, the Node Expiration actions and Rules, and all with the same result

RAFA3L’s picture

I Think the problem is major, now I'm understanding a little more the module. I'm not an expert.

The module does not clear the cache at all, only is cleared by the Expire module when use the internal expiration option, and only the anonymous users cache.

If you put this:

dsm($cids);

at the end of authcache_builtin_expire_cache() and save some node can see that all the urls are bad formed and with the wrong key.

Tomorrow I'll continue...

RAFA3L’s picture

Finally I get it to work...

For some reason $properties_info in authcache_enum_user_keys() is not ordered properly, so I force it to the right order, I don't know if this is the best solution but works.

function authcache_enum_user_keys() {
  $info = &drupal_static(__FUNCTION__);

  if (!isset($info)) {
    $properties_info = module_invoke_all('authcache_enum_key_properties');
    $sets = array();
    foreach ($properties_info as $key => $definition) {
      $sets[$key] = $definition['choices'];
    }

    // reorder array //----------------------------------------------------
    $temp = array();
    $temp['base_root'] = $sets['base_root'];
    $temp['roles'] = $sets['roles'];
    $temp['ajax'] = true;

    $info = array();
    module_load_include('inc', 'authcache_enum', 'authcache_enum.comb');
    foreach (_authcache_enum_cartesian($temp) as $properties) {
      // force ajax value true, i don't know why change to false at this point //----------------------------------------------------
      $properties['ajax'] = true;
      $authcache_key = authcache_user_key($properties);
      $info[$authcache_key] = $properties;
    }

    drupal_alter('authcache_enum_key_properties', $info);
  }

  return $info;
}

And the problem with the absolute urls maybe is in the Expire module side

https://drupal.org/node/2113941

I fix it with this

function authcache_builtin_expire_cache($urls) {
  global $base_root;
  if (!module_exists('authcache_enum')) {
    return;
  }

  $cids = array();

  $keys = authcache_enum_keys();
  foreach ($keys as $key) {
    foreach ($urls as $url) {
      // Expire chops the root-slash from request urls. Therefore we need to
      // add it here between the key and the url.
      $cids[] = $key . '/' . str_replace($base_root.'/', "", $url);
    }
  }
  cache_clear_all($cids, 'cache_page');
}
aheimlich’s picture

@RAFA3L

Regarding your array ordering issue, I think adding a call to ksort at the beginning of authcache_user_key() would solve that problem.

Also, authcache_enum_user_keys() should probably detect whether the current is the superuser or not, like authcache_key() does.

function authcache_user_key($properties, $superuser = FALSE) {
  ksort($properties);
  $data = serialize($properties);
  $hmac = hash_hmac('sha1', $data, drupal_get_private_key(), FALSE);

  // Make sure superuser has its own key not shared with anyone else (though
  // it would be better if superuser would'nt be allowed to use the cache in
  // the first place).
  if ($superuser) {
    $hmac = hash_hmac('sha1', $hmac . $data, drupal_get_private_key(), FALSE);
  }

  $abbrev = variable_get('authcache_key_abbrev', 7);
  return $abbrev ? substr($hmac, 0, $abbrev) : $hmac;
}
function authcache_enum_user_keys() {
  global $user;
  $info = &drupal_static(__FUNCTION__);

  if (!isset($info)) {
    $properties_info = module_invoke_all('authcache_enum_key_properties');

    $sets = array();
    foreach ($properties_info as $key => $definition) {
      $sets[$key] = $definition['choices'];
    }

    $info = array();
    module_load_include('inc', 'authcache_enum', 'authcache_enum.comb');
    foreach (_authcache_enum_cartesian($sets) as $properties) {
      $authcache_key = authcache_user_key($properties, $user->uid == 1);
      $info[$authcache_key] = $properties;
    }

    drupal_alter('authcache_enum_key_properties', $info);
  }

  return $info;
}
znerol’s picture

Status: Fixed » Needs work
FileSize
63.38 KB

Re #6: It is necessary to configure the expire module such that the base URL is not included in the $urls parameter to hook_expire_cache(). Navigate to Administration » Configuration » Development » Performance » Cache Expiration and disable the Option Include base URL in expires (see attached screenshot).

Also please note the documentation introduced by commit 35602cf. I hope it clarifies the inner workings of authcache_enum_user_keys() and especially _authcache_enum_cartesian(). If you wish to stay with your $tmp-hack, you'd need to specify $tmp['ajax'] as an array, i.e. $temp['ajax'] = array(TRUE, FALSE);.

Re #7: Good catch, you are completely right about ksort and the unique key for superuser. The solution for the latter problem however probably needs some more attention. It is necessary that authcache_enum_user_keys() always returns the same results, regardless of whether the superuser is logged in or not. The solution you are proposing will return the complete set of keys prepared for superuser when uid=1 is logged in. When superuser is not logged in, it will also return the complete set of keys, however none of them prepared for superuser. An easy solution would be to simply document the fact that the superuser-feature is not compatible with expire for the moment and develop a proper solution when the need really arises.

aheimlich’s picture

It is necessary that authcache_enum_user_keys() always returns the same results, regardless of whether the superuser is logged in or not. The solution you are proposing will return the complete set of keys prepared for superuser when uid=1 is logged in. When superuser is not logged in, it will also return the complete set of keys, however none of them prepared for superuser. An easy solution would be to simply document the fact that the superuser-feature is not compatible with expire for the moment and develop a proper solution when the need really arises.

I wasn't really planning on using the super-user feature anyway. I just wanted to point that out for consistency's sake.

znerol’s picture

Status: Needs work » Needs review
FileSize
3.48 KB
RAFA3L’s picture

You are right @znerol, I was using the 7.x-2.x-dev of the Expire module. But changing it to 7.x-1.0-beta1 or 7.x-1.x-dev (and applying the patch) doesn't fix the problem. My test site is in a subfolder "localhost/testcache" and with the "Include base URL in expires" disabled I get this cids in authcache_builtin_expire_cache, for example:

7371cb8/node/1
7371cb8/content/test-1

The subfolder is excluded.

And I notice that authcache_builtin_expire_cache get all the url nodes (with and without alias), so I think if the cids were right will clear all the pages cached and not only the updated.

The ksort call work perfect.

znerol’s picture

My test site is in a subfolder "localhost/testcache"

.

Ah, I see. That is actually the problem. The expire module actually passes the paths to hook implementations, therefore we need to add the contents of the $base_path variable to the URL. Patch attached.

RAFA3L’s picture

The patch work using Expire 7.x-1.x-dev, but now clear all the cached pages and would be only the updated node and any other indicated in rules.

Using Expire 7.x-2.x-dev work fine, only pass the selected urls, but the problem is that the option "Include base URL in expires" was discarded , as is explained here...

https://drupal.org/node/2113941

znerol’s picture

Ok, thanks for pointing out the API incompatibilities. I think it is worthwhile to fix this the proper way and extract expire-support into two dedicated modules for Cache Expiration 7.x-1.x and 7.x-2.x respectively. That way we can cut back on requirements checking / soft dependencies and also properly support the new wildcard feature of Cache Expiration v2.

After applying the patch you need to enable one of the new modules, either Authcache Builtin Cache Expiration v1 or Authcache Builtin Cache Expiration v2.

RAFA3L’s picture

Thanks! Only one more thing...

function authcache_builtin_expire_v2_expire_cache($urls, $wildcards, $object_type, $object) {
global $base_root; // added

...

// like v1 must be
//foreach (array_keys($urls) as $path) {
foreach ($urls as $path) {

...

// This line doesn't make the url relative
//$cid = $key . url($path, array('absolute' => FALSE)); 
$cid = $key . '/' . str_replace($base_root.'/', "", $path);

With this changes work perfect

znerol’s picture

Re #15: The structure of the $urls parameter is different for expire 7.x-1.x and 7.x-2.x:

In 7.x-1.x:

$urls = array(
  'node/1',
  'taxonomy/term/2',
);

In 7.x-2.x:

$urls = array(
  'node/1' => 'http://example.com/base-path/node/1',
  'taxonomy/term/2' => 'http://example.com/base-path/taxonomy/term/2',
);

Note that in 2.x the array keys contain the paths and the array values contain the full URL. In 1.x only the array values are supplied and there is a configuration option to switch between full URLs and internal paths (see #8).

I really would like to work with the internal paths and I want to avoid calling str_replace on full URLs. Probably the reason why some paths are still not purged with the patch in #14 is that url() replaces internal paths by their aliases by default. Let's disable this option (by supplying 'alias' => TRUE) and try again...

RAFA3L’s picture

Yes! you are right! I was using an action rule to clear the cache with


[node:url]

and this is the cause of the problem, using rules doesn't pass the right path in the array keys. Using the node actions in the settings of the Expire module (without rules) all work perfect!

Thanks!!!

znerol’s picture

Status: Needs review » Fixed

I just tested action rules and I observed that expiring cache entries using rules only works when internal paths are supplied (instead of full URLs). In order to clear a node it is necessary to specify it using its path node/[node:nid] instead of [node:url].

Thanks @RAFA3L and @aheimlich for pointing out this issues and helping me resolve them. Committed and pushed: 9152d17.

znerol’s picture

Documentation on this feature can be found in a new page of the tuning guide. Contributions to the docs are very welcome as well.

Status: Fixed » Closed (fixed)

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

RAFA3L’s picture

Hello znerol is me again...

I'm playing with the Context Mobile Detect module and I think it work fine with Authcache, but again, the problem is clearing the cache after update a node.

For example, the generated cache file using Blackberry is

...^testcache^node^1%3Fdevice%3D1%26device_type%3Dblackberry

But after update a node the cache file for the specific device is not cleared. Would be nice make it to work with Context Mobile Detect.

znerol’s picture

@RAFA3L: Please open a new feature request.

znerol’s picture

A heads up, there is still an issue with Expire 7.x-2.x: #2177265: Authcache Builtin Cache Expiration v2 does not work on pages with alias

Samlet9908’s picture

Issue tags: -

Sorry but I'm using the the latest builds of both Authcache and Cache Expiration with Memcached as the cache backend. If I enable 'external expiration' in Cache Expiration, when editing a node, it gives me a blank screen and does not save the node. I'm using aliases.

znerol’s picture

This issue is closed, please open a new support request.