function memcache_wildcards in Memcache API and Integration 6
Utilize multiget to retrieve all possible wildcard matches, storing statically so multiple cache requests for the same item on the same page load doesn't add overhead.
3 calls to memcache_wildcards()
- cache_clear_all in ./
memcache.inc - Expire data from the cache. If called without arguments, expirable entries will be cleared from the cache_page and cache_block tables.
- MemCacheClearCase::clearWildcardPrefixTest in tests/
memcache.test - Test cache clears using wildcard prefixes.
- memcache_wildcard_flushes in ./
memcache.inc - Sum of all matching wildcards. Checking any single cache item's flush value against this single-value sum tells us whether or not a new wildcard flush has affected the cached item.
File
- ./
memcache.inc, line 333
Code
function memcache_wildcards($cid, $table, $flush = FALSE) {
static $wildcards = array();
$matching = array();
if (!is_string($cid) && !is_int($cid)) {
register_shutdown_function('watchdog', 'memcache', 'Invalid cache id received in memcache.inc wildcards() of type !type.', array(
'!type' => gettype($cid),
), WATCHDOG_ERROR);
return $matching;
}
$length = strlen($cid);
$wildcard_flushes = variable_get('memcache_wildcard_flushes', array());
$wildcard_invalidate = variable_get('memcache_wildcard_invalidate', MEMCACHE_WILDCARD_INVALIDATE);
if (isset($wildcard_flushes[$table]) && is_array($wildcard_flushes[$table])) {
// Wildcard flushes per table are keyed by a substring equal to the
// shortest wildcard clear on the table so far. So if the shortest
// wildcard was "links:foo:", and the cid we're checking for is
// "links:bar:bar", then the key will be "links:bar:".
$keys = array_keys($wildcard_flushes[$table]);
// All keys are the same length, so just get the length of the first one.
$wildcard_length = strlen(reset($keys));
$wildcard_key = substr($cid, 0, $wildcard_length);
// Determine which lookups we need to perform to determine whether or not
// our cid was impacted by a wildcard flush.
$lookup = array();
// Find statically cached wildcards, and determine possibly matching
// wildcards for this cid based on a history of the lengths of past valid
// wildcard flushes in this bin.
if (isset($wildcard_flushes[$table][$wildcard_key])) {
foreach ($wildcard_flushes[$table][$wildcard_key] as $flush_length => $timestamp) {
if ($length >= $flush_length && $timestamp >= $_SERVER['REQUEST_TIME'] - $wildcard_invalidate) {
$key = '.wildcard-' . substr($cid, 0, $flush_length);
$wildcard = dmemcache_key($key, $table);
if (isset($wildcards[$table][$wildcard])) {
$matching[$wildcard] = $wildcards[$table][$wildcard];
}
else {
$lookup[$wildcard] = $key;
}
}
}
}
// Do a multi-get to retrieve all possibly matching wildcard flushes.
if (!empty($lookup)) {
$memcache_values = dmemcache_get_multi($lookup, $table);
if (is_array($memcache_values)) {
// Map .wildcard-* to the dmemcache_key().
$values = array();
$rmap = array_flip($lookup);
foreach ($memcache_values as $key => $value) {
$values[$rmap[$key]] = $value;
}
// Build an array of matching wildcards.
$matching = array_merge($matching, $values);
if (isset($wildcards[$table])) {
$wildcards[$table] = array_merge($wildcards[$table], $values);
}
else {
$wildcards[$table] = $values;
}
$lookup = array_diff_key($lookup, $values);
}
// Also store failed lookups in our static cache, so we don't have to
// do repeat lookups on single page loads.
foreach ($lookup as $wildcard => $key) {
$wildcards[$table][$wildcard] = 0;
}
}
}
if ($flush) {
// Avoid too many calls to variable_set() by only recording a flush for a
// fraction of the wildcard invalidation variable, per cid length. Defaults
// to 28 / 4, or one week.
$length = strlen($cid);
if (isset($wildcard_flushes[$table])) {
$wildcard_flushes_keys = array_keys($wildcard_flushes[$table]);
$key_length = strlen(reset($wildcard_flushes_keys));
}
else {
$key_length = $length;
}
$key = substr($cid, 0, $key_length);
if (!isset($wildcard_flushes[$table][$key][$length]) || $_SERVER['REQUEST_TIME'] - $wildcard_flushes[$table][$key][$length] > $wildcard_invalidate / 4) {
// If there are more than 50 different wildcard keys for this table
// shorten the key by one, this should reduce variability by
// an order of magnitude and ensure we don't use too much memory.
if (isset($wildcard_flushes[$table]) && count($wildcard_flushes[$table]) > 50) {
$key = substr($cid, 0, $key_length - 1);
$length = strlen($key);
}
// If this is the shortest key length so far, we need to remove all
// other wildcards lengths recorded so far for this table and start
// again. This is equivalent to a full cache flush for this table, but
// it ensures the minimum possible number of wildcards are requested
// along with cache consistency.
if ($length < $key_length) {
$wildcard_flushes[$table] = array();
memcache_variable_set("cache_flush_{$table}", time());
}
$key = substr($cid, 0, $key_length);
$wildcard_flushes[$table][$key][$length] = $_SERVER['REQUEST_TIME'];
memcache_variable_set('memcache_wildcard_flushes', $wildcard_flushes);
}
$wildcard = dmemcache_key('.wildcard-' . $cid, $table);
if (isset($wildcards[$table][$wildcard]) && $wildcards[$table][$wildcard] != 0) {
$mc = dmemcache_object($table);
if ($mc) {
$mc
->increment($wildcard);
}
$wildcards[$table][$wildcard]++;
}
else {
$wildcards[$table][$wildcard] = 1;
dmemcache_set('.wildcard-' . $cid, '1', 0, $table);
}
}
return $matching;
}