summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrey Andreev <narf@devilix.net>2015-03-04 12:33:39 +0100
committerAndrey Andreev <narf@devilix.net>2015-03-04 12:33:39 +0100
commite1a5bb345b1b30ea777348efa9cade21c1f2e2fb (patch)
tree98d07a56eb201818bdc274ba052d402e13841de1
parentfd08d02b1984d8f27a5e447a5c9d5e190271ab5e (diff)
Fix #3627: Keep timed locks for more than 5 seconds
Emulated locks for Redis and Memcached now have a TTL of 300 seconds (the default HTTP request timeout value on many environments) and 30 attemps, each separated by sleep(1), are made by the blocked request to try and obtain a lock if it has been freed. Additionaly, the blocking time for MySQL's locks, which are also timed, is also set to 300 seconds.
-rw-r--r--system/libraries/Session/drivers/Session_database_driver.php2
-rw-r--r--system/libraries/Session/drivers/Session_memcached_driver.php30
-rw-r--r--system/libraries/Session/drivers/Session_redis_driver.php40
-rw-r--r--user_guide_src/source/libraries/sessions.rst6
4 files changed, 25 insertions, 53 deletions
diff --git a/system/libraries/Session/drivers/Session_database_driver.php b/system/libraries/Session/drivers/Session_database_driver.php
index f496b4fe0..76c1cf34e 100644
--- a/system/libraries/Session/drivers/Session_database_driver.php
+++ b/system/libraries/Session/drivers/Session_database_driver.php
@@ -319,7 +319,7 @@ class CI_Session_database_driver extends CI_Session_driver implements SessionHan
if ($this->_platform === 'mysql')
{
$arg = $session_id.($this->_config['match_ip'] ? '_'.$_SERVER['REMOTE_ADDR'] : '');
- if ($this->_db->query("SELECT GET_LOCK('".$arg."', 10) AS ci_session_lock")->row()->ci_session_lock)
+ if ($this->_db->query("SELECT GET_LOCK('".$arg."', 300) AS ci_session_lock")->row()->ci_session_lock)
{
$this->_lock = $arg;
return TRUE;
diff --git a/system/libraries/Session/drivers/Session_memcached_driver.php b/system/libraries/Session/drivers/Session_memcached_driver.php
index f1a6e2400..938a612d9 100644
--- a/system/libraries/Session/drivers/Session_memcached_driver.php
+++ b/system/libraries/Session/drivers/Session_memcached_driver.php
@@ -204,7 +204,7 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa
if (isset($this->_lock_key))
{
- $this->_memcached->replace($this->_lock_key, time(), 5);
+ $this->_memcached->replace($this->_lock_key, time(), 300);
if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
{
if ($this->_memcached->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
@@ -299,34 +299,21 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa
{
if (isset($this->_lock_key))
{
- return $this->_memcached->replace($this->_lock_key, time(), 5);
+ return $this->_memcached->replace($this->_lock_key, time(), 300);
}
+ // 30 attempts to obtain a lock, in case another request already has it
$lock_key = $this->_key_prefix.$session_id.':lock';
- if ( ! ($ts = $this->_memcached->get($lock_key)))
- {
- if ( ! $this->_memcached->set($lock_key, TRUE, 5))
- {
- log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
- return FALSE;
- }
-
- $this->_lock_key = $lock_key;
- $this->_lock = TRUE;
- return TRUE;
- }
-
- // Another process has the lock, we'll try to wait for it to free itself ...
$attempt = 0;
- while ($attempt++ < 5)
+ do
{
- usleep(((time() - $ts) * 1000000) - 20000);
- if (($ts = $this->_memcached->get($lock_key)) < time())
+ if ($this->_memcached->get($lock_key))
{
+ sleep(1);
continue;
}
- if ( ! $this->_memcached->set($lock_key, time(), 5))
+ if ( ! $this->_memcached->set($lock_key, time(), 300))
{
log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
return FALSE;
@@ -335,8 +322,9 @@ class CI_Session_memcached_driver extends CI_Session_driver implements SessionHa
$this->_lock_key = $lock_key;
break;
}
+ while ($attempt++ < 30);
- if ($attempt === 5)
+ if ($attempt === 30)
{
log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.');
return FALSE;
diff --git a/system/libraries/Session/drivers/Session_redis_driver.php b/system/libraries/Session/drivers/Session_redis_driver.php
index 5fbb5222c..1ce101daf 100644
--- a/system/libraries/Session/drivers/Session_redis_driver.php
+++ b/system/libraries/Session/drivers/Session_redis_driver.php
@@ -205,7 +205,7 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
if (isset($this->_lock_key))
{
- $this->_redis->setTimeout($this->_lock_key, 5);
+ $this->_redis->setTimeout($this->_lock_key, 300);
if ($this->_fingerprint !== ($fingerprint = md5($session_data)))
{
if ($this->_redis->set($this->_key_prefix.$session_id, $session_data, $this->_config['expiration']))
@@ -313,40 +313,21 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
{
if (isset($this->_lock_key))
{
- return $this->_redis->setTimeout($this->_lock_key, 5);
+ return $this->_redis->setTimeout($this->_lock_key, 300);
}
+ // 30 attempts to obtain a lock, in case another request already has it
$lock_key = $this->_key_prefix.$session_id.':lock';
- if (($ttl = $this->_redis->ttl($lock_key)) < 1)
- {
- if ( ! $this->_redis->setex($lock_key, 5, time()))
- {
- log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
- return FALSE;
- }
-
- $this->_lock_key = $lock_key;
-
- if ($ttl === -1)
- {
- log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
- }
-
- $this->_lock = TRUE;
- return TRUE;
- }
-
- // Another process has the lock, we'll try to wait for it to free itself ...
$attempt = 0;
- while ($attempt++ < 5)
+ do
{
- usleep(($ttl * 1000000) - 20000);
if (($ttl = $this->_redis->ttl($lock_key)) > 0)
{
+ sleep(1);
continue;
}
- if ( ! $this->_redis->setex($lock_key, 5, time()))
+ if ( ! $this->_redis->setex($lock_key, 300, time()))
{
log_message('error', 'Session: Error while trying to obtain lock for '.$this->_key_prefix.$session_id);
return FALSE;
@@ -355,12 +336,17 @@ class CI_Session_redis_driver extends CI_Session_driver implements SessionHandle
$this->_lock_key = $lock_key;
break;
}
+ while ($attempt++ < 30);
- if ($attempt === 5)
+ if ($attempt === 30)
{
- log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 5 attempts, aborting.');
+ log_message('error', 'Session: Unable to obtain lock for '.$this->_key_prefix.$session_id.' after 30 attempts, aborting.');
return FALSE;
}
+ elseif ($ttl === -1)
+ {
+ log_message('debug', 'Session: Lock for '.$this->_key_prefix.$session_id.' had no TTL, overriding.');
+ }
$this->_lock = TRUE;
return TRUE;
diff --git a/user_guide_src/source/libraries/sessions.rst b/user_guide_src/source/libraries/sessions.rst
index 9fc33247b..104adb631 100644
--- a/user_guide_src/source/libraries/sessions.rst
+++ b/user_guide_src/source/libraries/sessions.rst
@@ -632,8 +632,7 @@ Redis Driver
.. note:: Since Redis doesn't have a locking mechanism exposed, locks for
this driver are emulated by a separate value that is kept for up
- to 5 seconds. You may experience issues if your page loads take
- longer than that!
+ to 300 seconds.
Redis is a storage engine typically used for caching and popular because
of its high performance, which is also probably your reason to use the
@@ -670,8 +669,7 @@ Memcached Driver
.. note:: Since Memcache doesn't have a locking mechanism exposed, locks
for this driver are emulated by a separate value that is kept for
- up to 5 seconds. You may experience issues if your page loads take
- longer than that!
+ up to 300 seconds.
The 'memcached' driver is very similar to the 'redis' one in all of its
properties, except perhaps for availability, because PHP's `Memcached