You are here

protected function MemCacheDrupal::wildcards in Memcache API and Integration 7

Retrieves all matching wildcards for the given cache id.

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.

2 calls to MemCacheDrupal::wildcards()
MemCacheDrupal::clear in ./memcache.inc
Implements DrupalCacheInterface::clear().
MemCacheDrupal::wildcardFlushes in ./memcache.inc
Sum of all matching wildcards.

File

./memcache.inc, line 403

Class

MemCacheDrupal
Implementation of cache.inc with memcache logic included

Code

protected function wildcards($cid, $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);
  if (isset($this->wildcard_flushes[$this->bin]) && is_array($this->wildcard_flushes[$this->bin])) {

    // 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($this->wildcard_flushes[$this->bin]);
    $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($this->wildcard_flushes[$this->bin][$wildcard_key])) {
      foreach ($this->wildcard_flushes[$this->bin][$wildcard_key] as $flush_length => $timestamp) {
        if ($length >= $flush_length && $timestamp >= REQUEST_TIME - $this->invalidate) {
          $wildcard = '.wildcard-' . substr($cid, 0, $flush_length);
          if (isset($wildcards[$this->bin][$wildcard])) {
            $matching[$wildcard] = $wildcards[$this->bin][$wildcard];
          }
          else {
            $lookup[$wildcard] = $wildcard;
          }
        }
      }
    }

    // Do a multi-get to retrieve all possibly matching wildcard flushes.
    if (!empty($lookup)) {
      $values = dmemcache_get_multi($lookup, $this->bin, $this->memcache);
      if (is_array($values)) {

        // Prepare an array of matching wildcards.
        $matching = array_merge($matching, $values);

        // Store matches in the static cache.
        if (isset($wildcards[$this->bin])) {
          $wildcards[$this->bin] = array_merge($wildcards[$this->bin], $values);
        }
        else {
          $wildcards[$this->bin] = $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 $key => $key) {
        $wildcards[$this->bin][$key] = 0;
      }
    }
  }
  if ($flush) {
    $key_length = $length;
    if (isset($this->wildcard_flushes[$this->bin])) {
      $keys = array_keys($this->wildcard_flushes[$this->bin]);
      $key_length = strlen(reset($keys));
    }
    $key = substr($cid, 0, $key_length);

    // 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.
    if (!isset($this->wildcard_flushes[$this->bin][$key][$length]) || REQUEST_TIME - $this->wildcard_flushes[$this->bin][$key][$length] > $this->invalidate / 4) {

      // If there are more than 50 different wildcard keys for this bin
      // 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($this->wildcard_flushes[$this->bin]) && count($this->wildcard_flushes[$this->bin]) > 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 bin 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) {
        $this->wildcard_flushes[$this->bin] = array();
        $this
          ->variable_set("cache_flush_{$this->bin}", time());
        $this->cache_flush = time();
      }
      $key = substr($cid, 0, $key_length);
      $this->wildcard_flushes[$this->bin][$key][$length] = REQUEST_TIME;
      variable_set('memcache_wildcard_flushes', $this->wildcard_flushes);
    }
    $key = '.wildcard-' . $cid;
    if (isset($wildcards[$this->bin][$key])) {
      $wildcards[$this->bin][$key]++;
    }
    else {
      $wildcards[$this->bin][$key] = 1;
    }
    dmemcache_set($key, $wildcards[$this->bin][$key], 0, $this->bin);
  }
  return $matching;
}