forked from extern/egroupware
Mutexed memcache handler. Traps race and memcache memory conditions on large scale Fastcgi farms.
Attribs: Stephan Becker: Code, identified the Javascript culprits Wim Bonis: Code, Race condition Klaus Leithhoff: Code, mbstring writes a different length to that that it reads Lars Volker: Code, Debug memcache slab memory, memcache add as lock.
This commit is contained in:
parent
cc210e9dae
commit
d81d9bce03
@ -22,6 +22,9 @@
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
define('DEBUG',0);
|
||||
define('DEBUGALL',0);
|
||||
|
||||
function egw_memcache_open($save_path, $session_name)
|
||||
{
|
||||
global $egw_memcache_obj;
|
||||
@ -41,16 +44,22 @@ function egw_memcache_close()
|
||||
return is_object($egw_memcache_obj) ? $egw_memcache_obj->close() : false;
|
||||
}
|
||||
|
||||
define('MEMCACHED_MAX_JUNK',1024*1023); // 1024*1024 is too big, maybe some account-info needs to be added
|
||||
define('MEMCACHED_MAX_JUNK',1024*1000); // 1024*1024 is too big, maybe some account-info needs to be added
|
||||
|
||||
function egw_memcache_read($id)
|
||||
{
|
||||
global $egw_memcache_obj;
|
||||
|
||||
if (DEBUG > 0 && DEBUGALL>0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." READ start $id:");
|
||||
|
||||
if (!_acquire_and_wait($id)) return false;
|
||||
|
||||
for($data=false,$n=0; ($read = $egw_memcache_obj->get($id.($n?'-'.$n:''))); ++$n)
|
||||
{
|
||||
if (DEBUG > 0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." read $id:$n:".print_r(_bytes($read),true));
|
||||
$data .= $read;
|
||||
}
|
||||
_release($id);
|
||||
return $data;
|
||||
}
|
||||
|
||||
@ -64,13 +73,61 @@ function egw_memcache_write($id, $sess_data)
|
||||
{
|
||||
$lifetime = 600;
|
||||
}
|
||||
if (DEBUG > 0 && DEBUGALL>0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." WRITE start $id:");
|
||||
|
||||
if (!_acquire_and_wait($id)) return false;
|
||||
|
||||
for($n=$i=0,$len=_bytes($sess_data); $i < $len; $i += MEMCACHED_MAX_JUNK,++$n)
|
||||
{
|
||||
if (!$egw_memcache_obj->set($id.($n?'-'.$n:''),_cut_bytes($sess_data,$i,MEMCACHED_MAX_JUNK),0,$lifetime)) return false;
|
||||
if (DEBUG > 0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." in :$n write $id:$i:".print_r(_bytes($sess_data),true));
|
||||
|
||||
if (!$egw_memcache_obj->set($id.($n?'-'.$n:''),_cut_bytes($sess_data,$i,MEMCACHED_MAX_JUNK),0,$lifetime)) {
|
||||
_release($id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (DEBUG > 0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." DELETE :$n");
|
||||
for($n=$n; $egw_memcache_obj->delete($id.($n?'-'.$n:'')); ++$n) ;
|
||||
|
||||
_release($id);
|
||||
return true;
|
||||
}
|
||||
|
||||
function _acquire_and_wait($id)
|
||||
{
|
||||
global $egw_memcache_obj;
|
||||
|
||||
if (DEBUG > 0 && DEBUGALL>0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." ACQUIRE :$id");
|
||||
|
||||
$i=0;
|
||||
// Acquire lock for 3 seconds, after that i should have done my job
|
||||
|
||||
while(!$egw_memcache_obj->add($id.'-lock',1,0,3)) {
|
||||
if (DEBUG > 0 && DEBUGALL>0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." ACQUIRE Lock Loop :$id:$i");
|
||||
usleep(100000);
|
||||
$i++;
|
||||
if ($i > 40) {
|
||||
if (DEBUG > 0) error_log("\n memcache ".print_r(getenv('HOSTNAME'),true).$_SERVER["REQUEST_TIME"]." blocked :$id");
|
||||
// Could not acquire lock after 3 seconds, Continue, and pretend the locking process get stuck
|
||||
// return false;A
|
||||
break;
|
||||
}
|
||||
}
|
||||
if($i>1) {
|
||||
if (DEBUG > 0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." ACQUIRE LOOP $i :$id");
|
||||
}
|
||||
if (DEBUG > 0) error_log("\n memcache ".print_r(getenv('HOSTNAME'),true).$_SERVER["REQUEST_TIME"]." Lock ACQUIRED $i:$id");
|
||||
return true;
|
||||
}
|
||||
|
||||
function _release($id)
|
||||
{
|
||||
global $egw_memcache_obj;
|
||||
|
||||
if (DEBUG > 0) error_log("\n memcache ".$_SERVER["REQUEST_TIME"]." RELEASE :$id");
|
||||
return $egw_memcache_obj->delete($id.'-lock');
|
||||
}
|
||||
|
||||
function _test_mbstring_func_overload()
|
||||
{
|
||||
return @extension_loaded('mbstring') && (ini_get('mbstring.func_overload') & 2);
|
||||
@ -80,7 +137,7 @@ function _bytes(&$data)
|
||||
{
|
||||
global $mbstring_func_overload;
|
||||
|
||||
if (is_null($mbstring_func_overload)) _test_mbstring_func_overload();
|
||||
if (is_null($mbstring_func_overload)) $mbstring_func_overload=_test_mbstring_func_overload();
|
||||
|
||||
return $mbstring_func_overload ? mb_strlen($data,'ascii') : strlen($data);
|
||||
}
|
||||
@ -89,7 +146,9 @@ function _cut_bytes(&$data,$offset,$len=null)
|
||||
{
|
||||
global $mbstring_func_overload;
|
||||
|
||||
if (is_null($mbstring_func_overload)) _test_mbstring_func_overload();
|
||||
if (is_null($mbstring_func_overload)) $mbstring_func_overload=_test_mbstring_func_overload();
|
||||
if (DEBUG > 0 && DEBUGALL>0) error_log("\n memcache in cutbyte mb $id:$n:".print_r(mb_substr($data,$offset,$len,'ascii'),true));
|
||||
if (DEBUG > 0 && DEBUGALL>0) error_log("\n memcache in cutbyte norm $id:$n:".print_r(substr($data,$offset,$len),true));
|
||||
|
||||
return $mbstring_func_overload ? mb_substr($data,$offset,$len,'ascii') : substr($data,$offset,$len);
|
||||
}
|
||||
@ -98,8 +157,12 @@ function egw_memcache_destroy($id)
|
||||
{
|
||||
global $egw_memcache_obj;
|
||||
|
||||
if (!_acquire_and_wait($id)) return false;
|
||||
|
||||
error_log("\n memcache destroy $id:$n:");
|
||||
for($n=0; $egw_memcache_obj->delete($id.($n?'-'.$n:'')); ++$n) ;
|
||||
|
||||
_release($id);
|
||||
return $n > 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user