awm awm - 29 days ago 13
PHP Question

How to delete items with same prefix key in memcached using PHP memcache extension?

Very similar questions have been posted and answered. However every solution I found so far is using php Memcached extension rather than Memcache. I am trying to delete/expire all memcache items that begin with a predefined key. For example,

so_something
,
so_something_else ...
. Any help is much appreciated.

awm awm
Answer

After some research I figured how to do it using standard Memcache commands. But, unfortunately this will man trips to the memcache server so it is to be used with care; in my case it is used only after deployment:

/**
 * Helper function to expire memcache entries with a specific key prefix.
 *
 * @param string $key_prefix
 *  The memcache key prefix.
 * @param array $servers
 *  Array of used memcache servers.
 * @return array
 *
 */
function memcache_delete_by_key($key_prefix = '', $servers = array()) {
  $mc = new Memcache();
  $delete_keys = array();
  foreach ($servers as $server => $default) {
    list($host, $port) = explode(':', $server);
    $mc->addServer($host, $port);
    // Extract all the keys from Memcache.
    $stats = memcache_command($server, $port, "stats items");
    $stat_lines = explode("\r\n", $stats);
    $slabs = array();
    $keys = array();
    foreach ($stat_lines as $line) {
      if (preg_match("/STAT items:([\d]+):number ([\d]+)/", $line, $matches)) {
        if (isset($matches[1]) && !in_array($matches[1], $slabs)) {
          $slabs[] = $matches[1];
          $string = memcache_command($server, $port, "stats cachedump " . $matches[1] . " " . $matches[2]);
          preg_match_all("/ITEM (.*?) /", $string, $matches);
          $keys[] = $matches[1];
        }
      }
    }
    $delete_keys = call_user_func_array('array_merge', $keys);
    // Locate all keys that begin with our prefix.
    foreach ($delete_keys as $index => $key) {
      // If strpos returns 0 (not false) then we have a match.
      if (strpos($key, $key_prefix) === 0) {
        $mc->delete($key);
      }
    }
  }

  return $delete_keys;
}

/*
 * Helper function to send memcache commands to a given Memcache Server
 */
function memcache_command($server, $port, $command) {
  $s = @fsockopen($server, $port);
  if (!$s) {
    return die("Cant connect to:" . $server . ":" . $port);
  }
  fwrite($s, $command . "\r\n");
  $buf = "";
  while ((!feof($s))) {
    $buf .= fgets($s, 256);
    // Stat says 'END'.
    if (strpos($buf, "END\r\n") !== FALSE) {
      break;
    }
    // Delete says DELETED or NOT_FOUND.
    if (strpos($buf, "DELETED\r\n") !== FALSE || strpos($buf, "NOT_FOUND\r\n") !== FALSE) {
      break;
    }
    // Flush_all says ok.
    if (strpos($buf, "OK\r\n") !== FALSE) {
      break;
    }
  }
  fclose($s);
  return ($buf);
}