Download & Extend

Allow a modifier for cache_page bin

Project:Memcache API and Integration
Version:6.x-1.x-dev
Component:Code
Category:feature request
Priority:normal
Assigned:Unassigned
Status:reviewed & tested by the community

Issue Summary

The Problem:

We wanted to be able to support two versions (look and feel) of the site with the same URLs for each. (Yes, we have very good reasons for this.) But we did not want to sacrifice package caching performance. Ideally, what we want is separate page cache variants for the theme.

Potential Solution:

One way of solving this problem is to use cookies to distinguish which of the two versions the client should get. Then, we can route traffic accordingly. To get page cache to store two versions of the page, we need to distinguish the keys for one version from those of the other version.

Attached is a small patch that generates cache variants based on cookies. It should be moderately extensible. While perhaps a core patch would be better suited for this sort of thing, that's not realistic for either D6 r D7 at this point. So I figure that this is the right place to implement such a feature.

Anyone have any thoughts about this method? Pros? Cons? Alternatives that don't involve massive amounts of path/q munging?

About the Patch:

The patch adds a short function to memcache.inc that transforms $cids.

<?php
function memcache_cookie_variant_cid($cid, $table) {
  global
$conf;
 
  if (
$table != 'cache_page') return $cid;
 
  foreach (
$conf['cookie_page_cache_variants'] as $cookie => $prefix) {
    if (!empty(
$_COOKIE[$cookie])) {
      return
$prefix . $cid;
    }
  }
 
  return
$cid;
}
?>

The function is called during cache_get/set/clear calls. In settings.php (or anything called very early in bootstap), one can specify a new variant cookie like this:

<?php
$conf
['cookie_page_cache_variants'] = array('my_cookie_name' => 'my_prefix-');
?>

This would result in a memcache key that would look something like this:

cache_page-my_prefix-http%3A%2F%2Fexample.com%2Ffoo%2Fbar
AttachmentSize
memcache.inc-20101015.patch2.81 KB

Comments

#1

#2

Title:Feature: Cookie-based cache variants (Patch attached)» Allow a modifier for cache_page bin

define function name in settings.php.

<?php
$conf
['memcache_cache_page_cid_modifier'] = 'my_function';
?>
AttachmentSize
memcache-942914.patch 3.75 KB

#3

updated patch to pass the operator along.

below is an example from my settings.php file

<?php
$conf
['memcache_cache_page_cid_modifier'] = 'settings_memcache_cid_modifier';
function
settings_memcache_cid_modifier($cid, $op) {
  if (
$op == 'get') {
   
$name = 'og_oven';
   
// Get Cookie
   
if (isset($_COOKIE[$name]) && is_numeric($_COOKIE[$name])) {
     
$nid = $_COOKIE[$name];
    }
    else {
     
$nid = 0;
    }
   
$cid .= '#oven-' . $nid;
  }
  elseif (
$op == 'set') {
    if (empty(
$_GET['set_default']) && function_exists('og_oven_get_cookie')) {
     
$nid = og_oven_get_cookie();
     
$cid .= '#oven-' . $nid;
    }
  }
  return
$cid;
}
?>

This has other use cases; stripping useless query strings from the URL for higher cache hits, etc... If your wondering I want a cache miss if set_default is set.

AttachmentSize
memcache-942914.patch 3.77 KB

#4

Status:active» needs review

#5

I like this patch and am testing it out. It seems to meet our requirements nicely. And as you pointed out, it can be adapted to a wide variety of similar cases. I'll update the ticket when I've finished reviewing.

#6

Maybe it'd be better to factor the common logic from the three memcache.inc functions into its own function like this (untested):

<?php
/**
* Modify the CID if necessary.
*
* @param $cid
*  The cache ID.
* @param $table
*  The table name.
* @param $op
*  The operation (e.g. get, put, clear)
* @return
*  The CID that should be used to retrieve from the cache.
*/
function memcache_modify_cid($cid, $table, $op = 'get') {
 
$modifier = variable_get("memcache_cache_page_cid_modifier", FALSE);
  if (!empty(
$modifier) && $table == 'cache_page' && function_exists($modifier)) {
   
$cid = $modifier($cid, $op);
  }
  return
$cid;
}
?>

And then call this function in the get/set/clear functions.

This would probably be more maintainable over the long term.

#7

Status:needs review» reviewed & tested by the community

Tested and working.

#8

Apart from early bootstrap (and we could check current bootstrap phase there), is there a reason not to do this as a hook?

Could have hook_memcache_cid_alter(), and it wouldn't hurt to have hook_memcache_bin_alter() - for example to route certain items to the cache_bootstrap bin.

#9

cache_bootstrap bin; looking to backport some D7 magic? Sounds interesting. So in order for this to work the module has to declare a hook_boot?

_drupal_bootstrap() does the cache call before require_once './includes/module.inc'; is ran. This would require a require_once call for every cache operation; or hack core to move this file above the page_get_cache();. How do you see it done?

#10

The idea would be to cache something like field info in the cache_bootstrap bin - however this is both requested and cached after full bootstrap has occurred, so no need to define hook_boot().

Existing stuff that's sent to cache_bootstrap I don't see any particular reason to mess with, or at least the feature could be added without supporting that.

Also I don't have a real use case for this yet, it was just an example ;)

#11

updated the patch to the latest dev version of memcache

AttachmentSize
memcache-942914-11-D6.patch 2 KB