summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcatch2011-11-11 04:17:46 (GMT)
committercatch2011-11-11 04:17:46 (GMT)
commit7ccb566a0589dda585915cb00b2a74eef470eae8 (patch)
treef650048c11511026db2b6fc629804f7843bcf236
parent983065517812956bc733dec11b2ee8a99cc92667 (diff)
Issue #1184678 by das-peter: Added Enhance failure tolerance.7.x-1.0-rc2
-rw-r--r--dmemcache.inc19
-rw-r--r--memcache-lock-code.inc161
-rw-r--r--memcache-lock.inc156
-rw-r--r--memcache.inc4
4 files changed, 189 insertions, 151 deletions
diff --git a/dmemcache.inc b/dmemcache.inc
index d671e59..5b0629a 100644
--- a/dmemcache.inc
+++ b/dmemcache.inc
@@ -93,6 +93,7 @@ function dmemcache_add($key, $value, $exp = 0, $bin = 'cache', $mc = NULL, $flag
*/
function dmemcache_get($key, $bin = 'cache', $mc = NULL) {
global $_memcache_statistics;
+ $result = FALSE;
$full_key = dmemcache_key($key, $bin);
$statistics = array('get', $bin, $full_key);
$success = '0';
@@ -276,7 +277,7 @@ function dmemcache_stats($stats_bin = 'cache', $stats_type = 'default', $aggrega
* @return an Memcache object or FALSE.
*/
function dmemcache_object($bin = NULL, $flush = FALSE) {
- static $extension, $memcacheCache = array(), $memcache_servers, $memcache_bins, $memcache_persistent;
+ static $extension, $memcacheCache = array(), $memcache_servers, $memcache_bins, $memcache_persistent, $failed_connection_cache;
if (!isset($extension)) {
// If an extension is specified in settings.php, use that when available.
@@ -361,7 +362,7 @@ function dmemcache_object($bin = NULL, $flush = FALSE) {
// Link all the servers to this cluster.
foreach ($memcache_servers as $s => $c) {
- if ($c == $cluster) {
+ if ($c == $cluster && !isset($failed_connection_cache[$s])) {
list($host, $port) = explode(':', $s);
// Support unix sockets in the format 'unix:///path/to/socket'.
@@ -400,6 +401,20 @@ function dmemcache_object($bin = NULL, $flush = FALSE) {
else if ($memcache->addServer($host, $port) && !$init) {
$init = TRUE;
}
+
+ if (!$init) {
+ // Ensure we use an available t() function.
+ $t = get_t();
+ $error_msg = $t(
+ 'Failed to connect to memcache server: %server',
+ array('%server' => $s)
+ );
+ // We can't use watchdog because this happens in a bootstrap phase
+ // where watchdog is non-functional. Thus use trigger_error() to
+ // start drupal_error_handler().
+ trigger_error($error_msg, E_USER_ERROR);
+ $failed_connection_cache[$s] = FALSE;
+ }
}
}
diff --git a/memcache-lock-code.inc b/memcache-lock-code.inc
new file mode 100644
index 0000000..374d02b
--- /dev/null
+++ b/memcache-lock-code.inc
@@ -0,0 +1,161 @@
+<?php
+
+/**
+ * @file
+ * A memcache based implementation of a locking mechanism.
+ * See includes/lock.inc for documenation
+ *
+ * ATTENTION: Don't include this file directly - use the memcache-lock.inc to
+ * have a failover solution.
+ */
+
+/**
+ * Initialize the locking system.
+ */
+function lock_initialize() {
+ global $locks;
+
+ $locks = array();
+}
+
+/**
+ * Acquire (or renew) a lock, but do not block if it fails.
+ *
+ * @param $name
+ * The name of the lock.
+ * @param $timeout
+ * A number of seconds (int) before the lock expires (minimum of 1).
+ * @return
+ * TRUE if the lock was acquired, FALSE if it failed.
+ */
+function lock_acquire($name, $timeout = 30) {
+ global $locks;
+
+ // Ensure that the timeout is at least 1 sec. This is a limitation
+ // imposed by memcached.
+ $timeout = (int) max($timeout, 1);
+
+ if (dmemcache_add($name, _lock_id(), $timeout, 'semaphore')) {
+ $locks[$name] = _lock_id();
+ }
+ elseif ($result = dmemcache_get($name, 'semaphore') && isset($locks[$name]) && $locks[$name] == _lock_id()) {
+ // Only renew the lock if we already set it and it has not expired.
+ dmemcache_set($name, _lock_id(), $timeout, 'semaphore');
+ }
+ else {
+ // Failed to acquire the lock. Unset the key from the $locks array even if
+ // not set, PHP 5+ allows this without error or warning.
+ unset($locks[$name]);
+ }
+
+ return isset($locks[$name]);
+}
+
+/**
+ * Check if lock acquired by a different process may be available.
+ *
+ * If an existing lock has expired, it is removed.
+ *
+ * @param $name
+ * The name of the lock.
+ * @return
+ * TRUE if there is no lock or it was removed, FALSE otherwise.
+ */
+function lock_may_be_available($name) {
+ return !dmemcache_get($name, 'semaphore');
+}
+
+/**
+ * Wait for a lock to be available.
+ *
+ * This function may be called in a request that fails to acquire a desired
+ * lock. This will block further execution until the lock is available or the
+ * specified delay in seconds is reached. This should not be used with locks
+ * that are acquired very frequently, since the lock is likely to be acquired
+ * again by a different request while waiting.
+ *
+ * @param $name
+ * The name of the lock.
+ * @param $delay
+ * The maximum number of seconds to wait, as an integer.
+ * @return
+ * TRUE if the lock holds, FALSE if it is available.
+ */
+function lock_wait($name, $delay = 30) {
+ // Pause the process for short periods between calling
+ // lock_may_be_available(). This prevents hitting the database with constant
+ // database queries while waiting, which could lead to performance issues.
+ // However, if the wait period is too long, there is the potential for a
+ // large number of processes to be blocked waiting for a lock, especially
+ // if the item being rebuilt is commonly requested. To address both of these
+ // concerns, begin waiting for 25ms, then add 25ms to the wait period each
+ // time until it reaches 500ms. After this point polling will continue every
+ // 500ms until $delay is reached.
+
+ // $delay is passed in seconds, but we will be using usleep(), which takes
+ // microseconds as a parameter. Multiply it by 1 million so that all
+ // further numbers are equivalent.
+ $delay = (int) $delay * 1000000;
+
+ // Begin sleeping at 25ms.
+ $sleep = 25000;
+ while ($delay > 0) {
+ // This function should only be called by a request that failed to get a
+ // lock, so we sleep first to give the parallel request a chance to finish
+ // and release the lock.
+ usleep($sleep);
+ // After each sleep, increase the value of $sleep until it reaches
+ // 500ms, to reduce the potential for a lock stampede.
+ $delay = $delay - $sleep;
+ $sleep = min(500000, $sleep + 25000, $delay);
+ if (!dmemcache_get($name, 'semaphore')) {
+ // No longer need to wait.
+ return FALSE;
+ }
+ }
+ // The caller must still wait longer to get the lock.
+ return TRUE;
+}
+
+/**
+ * Release a lock previously acquired by lock_acquire().
+ *
+ * This will release the named lock if it is still held by the current request.
+ *
+ * @param $name
+ * The name of the lock.
+ */
+function lock_release($name) {
+ global $locks;
+
+ dmemcache_delete($name, 'semaphore');
+ // We unset unconditionally since caller assumes lock is released anyway.
+ unset($locks[$name]);
+}
+
+/**
+ * Generate a unique identifier for locks generated during this request.
+ */
+function _lock_id() {
+ static $lock_id;
+ if (!isset($lock_id)) {
+ $lock_id = uniqid(mt_rand(), TRUE);
+ // We only register a shutdown function if a lock is used.
+ register_shutdown_function('lock_release_all', $lock_id);
+ }
+ return $lock_id;
+}
+
+/**
+ * Release all locks acquired by this request.
+ */
+function lock_release_all($lock_id) {
+ global $locks;
+ foreach ($locks as $name => $id) {
+ $value = dmemcache_get($name, 'semaphore');
+
+ if ($value == $id) {
+ dmemcache_delete($name, 'semaphore');
+ }
+ }
+}
diff --git a/memcache-lock.inc b/memcache-lock.inc
index 0d78cb4..bc0f936 100644
--- a/memcache-lock.inc
+++ b/memcache-lock.inc
@@ -8,153 +8,11 @@
require_once dirname(__FILE__) . '/dmemcache.inc';
-/**
- * Initialize the locking system.
- */
-function lock_initialize() {
- global $locks;
-
- $locks = array();
-}
-
-/**
- * Acquire (or renew) a lock, but do not block if it fails.
- *
- * @param $name
- * The name of the lock.
- * @param $timeout
- * A number of seconds (int) before the lock expires (minimum of 1).
- * @return
- * TRUE if the lock was acquired, FALSE if it failed.
- */
-function lock_acquire($name, $timeout = 30) {
- global $locks;
-
- // Ensure that the timeout is at least 1 sec. This is a limitation
- // imposed by memcached.
- $timeout = (int) max($timeout, 1);
-
- if (dmemcache_add($name, _lock_id(), $timeout, 'semaphore')) {
- $locks[$name] = _lock_id();
- }
- elseif ($result = dmemcache_get($name, 'semaphore') && isset($locks[$name]) && $locks[$name] == _lock_id()) {
- // Only renew the lock if we already set it and it has not expired.
- dmemcache_set($name, _lock_id(), $timeout, 'semaphore');
- }
- else {
- // Failed to acquire the lock. Unset the key from the $locks array even if
- // not set, PHP 5+ allows this without error or warning.
- unset($locks[$name]);
- }
-
- return isset($locks[$name]);
-}
-
-/**
- * Check if lock acquired by a different process may be available.
- *
- * If an existing lock has expired, it is removed.
- *
- * @param $name
- * The name of the lock.
- * @return
- * TRUE if there is no lock or it was removed, FALSE otherwise.
- */
-function lock_may_be_available($name) {
- return !dmemcache_get($name, 'semaphore');
-}
-
-/**
- * Wait for a lock to be available.
- *
- * This function may be called in a request that fails to acquire a desired
- * lock. This will block further execution until the lock is available or the
- * specified delay in seconds is reached. This should not be used with locks
- * that are acquired very frequently, since the lock is likely to be acquired
- * again by a different request while waiting.
- *
- * @param $name
- * The name of the lock.
- * @param $delay
- * The maximum number of seconds to wait, as an integer.
- * @return
- * TRUE if the lock holds, FALSE if it is available.
- */
-function lock_wait($name, $delay = 30) {
- // Pause the process for short periods between calling
- // lock_may_be_available(). This prevents hitting the database with constant
- // database queries while waiting, which could lead to performance issues.
- // However, if the wait period is too long, there is the potential for a
- // large number of processes to be blocked waiting for a lock, especially
- // if the item being rebuilt is commonly requested. To address both of these
- // concerns, begin waiting for 25ms, then add 25ms to the wait period each
- // time until it reaches 500ms. After this point polling will continue every
- // 500ms until $delay is reached.
-
- // $delay is passed in seconds, but we will be using usleep(), which takes
- // microseconds as a parameter. Multiply it by 1 million so that all
- // further numbers are equivalent.
- $delay = (int) $delay * 1000000;
-
- // Begin sleeping at 25ms.
- $sleep = 25000;
- while ($delay > 0) {
- // This function should only be called by a request that failed to get a
- // lock, so we sleep first to give the parallel request a chance to finish
- // and release the lock.
- usleep($sleep);
- // After each sleep, increase the value of $sleep until it reaches
- // 500ms, to reduce the potential for a lock stampede.
- $delay = $delay - $sleep;
- $sleep = min(500000, $sleep + 25000, $delay);
- if (!dmemcache_get($name, 'semaphore')) {
- // No longer need to wait.
- return FALSE;
- }
- }
- // The caller must still wait longer to get the lock.
- return TRUE;
-}
-
-/**
- * Release a lock previously acquired by lock_acquire().
- *
- * This will release the named lock if it is still held by the current request.
- *
- * @param $name
- * The name of the lock.
- */
-function lock_release($name) {
- global $locks;
-
- dmemcache_delete($name, 'semaphore');
- // We unset unconditionally since caller assumes lock is released anyway.
- unset($locks[$name]);
-}
-
-/**
- * Generate a unique identifier for locks generated during this request.
- */
-function _lock_id() {
- static $lock_id;
- if (!isset($lock_id)) {
- $lock_id = uniqid(mt_rand(), TRUE);
- // We only register a shutdown function if a lock is used.
- register_shutdown_function('lock_release_all', $lock_id);
- }
- return $lock_id;
-}
-
-/**
- * Release all locks acquired by this request.
- */
-function lock_release_all($lock_id) {
- global $locks;
- foreach ($locks as $name => $id) {
- $value = dmemcache_get($name, 'semaphore');
-
- if ($value == $id) {
- dmemcache_delete($name, 'semaphore');
- }
- }
+// Check if memcached is available - if not include default lock handler.
+// @todo get rid of this conditional include as soon as this is done:
+// http://drupal.org/node/1225404
+$lock_file = dirname(__FILE__) . '/memcache-lock-code.inc';
+if (!dmemcache_object('semaphore')) {
+ $lock_file = DRUPAL_ROOT . '/includes/lock.inc';
}
+require_once $lock_file; \ No newline at end of file
diff --git a/memcache.inc b/memcache.inc
index f16c442..547d9c4 100644
--- a/memcache.inc
+++ b/memcache.inc
@@ -146,6 +146,10 @@ class MemCacheDrupal implements DrupalCacheInterface {
}
function clear($cid = NULL, $wildcard = FALSE) {
+ if ($this->memcache === FALSE) {
+ // No memcache connection.
+ return;
+ }
// It is not possible to detect a cache_clear_all() call other than looking
// at the backtrace unless http://drupal.org/node/81461 is added.