diff --git a/apc.module b/apc.module index 31c5f2c..6ac7b03 100644 --- a/apc.module +++ b/apc.module @@ -122,3 +122,12 @@ function apc_clear_opcode_cache() { drupal_set_message(t('Unable to clear APC opcode cache.'), 'error'); } } + +/** + * Implements hook_exit(). + */ +function apc_exit() { + if (class_exists('DrupalAPCCache')) { + DrupalAPCCache::remoteFlush(); + } +} diff --git a/drupal_apc_cache.inc b/drupal_apc_cache.inc index 2099c55..784e71a 100644 --- a/drupal_apc_cache.inc +++ b/drupal_apc_cache.inc @@ -37,6 +37,12 @@ class DrupalAPCCache implements DrupalCacheInterface { protected $prefix; /** + * @var boolean + */ + protected $drush; + + protected static $remoteClears = array(); + /** * Get prefix for bin using the configuration. * * @param string $bin @@ -74,6 +80,7 @@ class DrupalAPCCache implements DrupalCacheInterface { function __construct($bin) { $this->bin = $bin; + $this->drush = (drupal_is_cli() && function_exists('drush_log')); // First we determine the prefix from a setting. $prefix = self::getPrefixSettingForBin($this->bin); @@ -255,8 +262,10 @@ class DrupalAPCCache implements DrupalCacheInterface { } function clear($cid = NULL, $wildcard = FALSE) { - if (drupal_is_cli() && function_exists('drush_log')) { - drush_log($this->bin . '(' . $cid . ') was not cleared. APC cli uses a different memory storage than the webserver. For more info see: http://drupal.org/node/1278232', 'warning'); + // Take note of all cache clears to notify other APC-nodes + // in a separate HTTP request. + self::$remoteClears[$this->bin][serialize($cid)] = $wildcard; + if ($this->drush) { return; } @@ -288,4 +297,55 @@ class DrupalAPCCache implements DrupalCacheInterface { $iterator = new APCIterator('user', '/^' . $match . '/', APC_ITER_KEY); return 0 === $iterator->getTotalCount(); } + + public static function remoteFlush() { + if (empty($GLOBALS['conf']['apc_nopropagate'])) { + if (!empty(self::$remoteClears)) { + // optimize '*' clears. + $star = serialize('*'); + foreach (self::$remoteClears as $bin => $clears) { + if (!empty($clears[$star])) { + self::$remoteClears[$bin] = array($star => TRUE); + } + } + } else { + return; + } + + $internalCache = new static('apc'); + $nonce = drupal_random_key(32); + $internalCache->set($nonce, 1); + + $nodes = variable_get('apc_nodes', array($GLOBALS['base_url'])); + $path = drupal_get_path('module', 'apc') . '/flush.php'; + $options = array( + 'method' => 'POST', + 'headers' => array('Content-Type' => 'application/x-www-form-urlencoded'), + 'data' => drupal_http_build_query(array( + 'clears' => serialize(self::$remoteClears), + 'nonce' => $nonce, + )), + ); + foreach ($nodes as $node) { + $uri = "$node/$path?key=" . variable_get('cron_key', 'drupal'); + $response = drupal_http_request($uri, $options); + + if ($internalCache->drush) { + if ($response->code != 200) { + drush_log("APC-Remote $node: HTTP error ({$response->code}) {$response->error}", 'error'); + if ($GLOBALS['base_url'] == 'http://' . basename(conf_path())) { + drush_log('The base_url might not be set correctly try using the -l/--uri option for drush.', 'warning'); + } + } + elseif (substr($response->data, 0, 3) == '#0:') { + drush_log("APC-Remote $node: {$response->data}", 'success'); + } + else { + drush_log("APC-Remote $node: {$response->data}", 'error'); + } + } + } + $internalCache->clear($nonce); + } + } } diff --git a/flush.php b/flush.php index e69de29..092b189 100644 --- a/flush.php +++ b/flush.php @@ -0,0 +1,53 @@ +get($nonce)) { + return; +} +$conf['apc_nopropagate'] = TRUE; + +$clears = unserialize($_POST['clears']); +foreach ($clears as $bin => $cids) { + $cache = new DrupalAPCCache($bin); + foreach ($cids as $serialized_cid => $wildcard) { + $cache->clear(unserialize($serialized_cid), $wildcard); + } +} + +echo "#0: Successfully flushed all caches.";