<?php
//
// memcache alike daemon
//
require_once($INC . "/util.inc.php");
require_once($INC . "/prof.inc.php");
// Client side can disable the cache for various reasons (unit testing etc.)
if (!isset($rg_cache_enable))
$rg_cache_enable = TRUE;
if (!isset($rg_cache_core_enable))
$rg_cache_core_enable = TRUE;
// timeout in miliseconds
$rg_cache_timeout = 500;
$rg_cache_count = 0;
$rg_cache_tries = 3;
if (!isset($rg_cache_socket))
$rg_cache_socket = "/var/lib/rocketgit/sockets/cache.sock";
$rg_cache = array();
$rg_cache_error = "";
if (!isset($rg_cache_debug))
$rg_cache_debug = FALSE;
function rg_cache_set_error($str)
{
global $rg_cache_error;
$rg_cache_error = $str;
rg_log($str);
}
function rg_cache_error()
{
global $rg_cache_error;
return $rg_cache_error;
}
/*
* Clears all core cache
*/
function rg_cache_core_destroy()
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return;
$rg_cache = array();
}
/*
* Dump all tables
*/
function rg_cache_core_dump()
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return '';
return 'rg_cache: ' . rg_array2string($rg_cache);
}
/*
* Sets a variable
* TODO: Prevent cache to grow and grow.
*/
function rg_cache_core_set($ns_var, $value)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return;
//rg_log_ml("cache_core_set: $ns_var = " . print_r($value, TRUE));
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token]))
$tree[$token] = array();
$tree = &$tree[$token];
}
$tree[$var] = $value;
}
/*
* Merges some items into a variable
*/
function rg_cache_core_merge($ns_var, $list)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return;
//rg_log_ml("cache_core_merge: $ns_var = " . print_r($list, TRUE));
$tree = &$rg_cache;
$t = explode("::", $ns_var);
foreach ($t as $token) {
if (!isset($tree[$token]))
$tree[$token] = array();
$tree = &$tree[$token];
}
foreach ($list as $k => $v)
$tree[$k] = $v;
return TRUE;
}
/*
* Increments a variable
*/
function rg_cache_core_inc($ns_var)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return;
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token]))
$tree[$token] = array();
$tree = &$tree[$token];
}
if (!isset($tree[$var]))
$ret = 1;
else
$ret = $tree[$var] + 1;
$tree[$var] = $ret;
return $ret;
}
/*
* Retrieve a variable from cache
*/
function rg_cache_core_get($ns_var)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return FALSE;
//rg_log("cache_core_get: $ns_var");
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token])) {
//rg_log("CHECK: cache_core_get: token '$token' not found, return false");
return FALSE;
}
$tree = &$tree[$token];
}
if (isset($tree[$var])) {
//rg_log_ml("CHECK: cache_core_get: found key in cache: $ns_var = " . print_r($tree[$var], TRUE));
return $tree[$var];
}
//rg_log_ml("CHECK: cache_core_get: [$ns_var] not found in rg_cache. rg_cache: " . print_r($rg_cache, TRUE));
return FALSE;
}
/*
* Unset a variable in cache
* Returns FALSE if not found, else TRUE.
*/
function rg_cache_core_unset($ns_var)
{
global $rg_cache_debug;
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return;
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token])) {
//rg_log("CHECK: cache_core_unset: token [$token] not found");
return FALSE;
}
$tree = &$tree[$token];
}
if (isset($tree[$var])) {
if ($rg_cache_debug)
rg_log('cache_core_unset(' . $ns_var . ')');
unset($tree[$var]);
return TRUE;
}
//rg_log("CHECK: cache_core_unset: key [$var] not found");
return FALSE;
}
/*
* Push a variable in a queue
*/
function rg_cache_core_apush($ns_var, $value)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return;
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token]))
$tree[$token] = array();
$tree = &$tree[$token];
}
if (!isset($tree[$var]))
$tree[$var] = array();
array_push($tree[$var], $value);
}
/*
* Pop a variable from the end of a queue
*/
function rg_cache_core_apop($ns_var)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return FALSE;
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token]))
return FALSE;
$tree = &$tree[$token];
}
if (!isset($tree[$var]))
return FALSE;
if (empty($tree[$var]))
return FALSE;
return array_pop($tree[$var]);
}
/*
* Pops and returns a variable from the begining of a queue
*/
function rg_cache_core_ashift($ns_var)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return FALSE;
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token]))
return FALSE;
$tree = &$tree[$token];
}
if (!isset($tree[$var]))
return FALSE;
if (empty($tree[$var]))
return FALSE;
return array_shift($tree[$var]);
}
/*
* Dumps a queue
*/
function rg_cache_core_adump($ns_var)
{
global $rg_cache;
global $rg_cache_core_enable;
if (!$rg_cache_core_enable)
return FALSE;
$tree = &$rg_cache;
$t = explode("::", $ns_var);
$var = array_pop($t);
foreach ($t as $token) {
if (!isset($tree[$token]))
return FALSE;
$tree = &$tree[$token];
}
if (!isset($tree[$var]))
return FALSE;
return rg_array2string($tree[$var]);
}
/********************************* Client side functions */
/*
* Prepares a string to be send to the wire (key part)
*/
function rg_cache_prepare_key($s)
{
return $s;
}
/*
* Prepares a string to be send to the wire (value part)
*/
function rg_cache_prepare($s)
{
return rg_serialize($s);
}
/*
* Helps to send and to decode a command
*/
function rg_cache_send($cmd, $para, $flags)
{
global $rg_cache_enable;
global $rg_cache_socket;
global $rg_cache_timeout;
global $rg_cache_tries;
global $rg_cache_count;
global $rg_cache_debug;
if ($rg_cache_enable === FALSE)
return FALSE;
$rg_cache_count++;
$f = '';
if ($flags & RG_SOCKET_NO_WAIT)
$f .= 'W';
$xcmd = $cmd . ' F=' . $f . ' I=' . $rg_cache_count . ' ' . $para;
if ($rg_cache_debug)
rg_log('Sending [' . $xcmd . ']...');
$ret = rg_socket($rg_cache_socket, $xcmd . "\n",
$rg_cache_timeout, $rg_cache_tries, $flags);
if ($ret === FALSE)
return FALSE;
if ($rg_cache_debug)
rg_log('Received [' . $ret . ']');
if ($flags & RG_SOCKET_NO_WAIT)
return TRUE;
$a = explode("\n", $ret);
foreach ($a as $line) {
//if ($rg_cache_debug)
// rg_log('Parsing line [' . $line . ']');
$t = explode(' ', $line, 3);
if (!isset($t[1]))
return FALSE;
$status = $t[0];
$id = intval($t[1]);
if ($id < $rg_cache_count) {
if ($rg_cache_debug)
rg_log('DEBUG: id: ' . $id . ' < ' . $rg_cache_count);
continue;
}
//if ($rg_cache_debug)
// rg_log('DEBUG: id: ' . $id . ' == ' . $rg_cache_count);
if (strcmp($status, 'OK') != 0) {
//rg_log('DEBUG: not an OK answer: ' . $status);
return FALSE;
}
if (!isset($t[2]))
return TRUE;
return trim($t[2]);
break;
}
}
/*
* Returns a variable from the cache daemon
* Return FALSE on error.
*/
function rg_cache_get($ns_var)
{
global $rg_cache_debug;
rg_prof_start('cache_get');
$ret = FALSE;
while (1) {
$k = rg_cache_prepare_key($ns_var);
$ret = rg_cache_core_get($k);
if ($ret !== FALSE)
break;
$flags = 0;
$r = rg_cache_send('GET', $k, $flags);
if ($r === FALSE)
break;
$ret = rg_unserialize($r);
if ($ret === FALSE) {
rg_cache_set_error('cannot unserialize answer: '
. rg_util_error());
break;
}
rg_cache_core_set($ns_var, $ret);
break;
}
if ($rg_cache_debug)
rg_log('cache_get[' . $ns_var . '] returns: '
. ($ret === FALSE ? 'FALSE' : rg_array2string($ret)));
rg_prof_end('cache_get');
return $ret;
}
/*
* Sets a variable in the cache daemon
*/
function rg_cache_set($ns_var, $value, $flags)
{
global $rg_cache_debug;
rg_prof_start('cache_set');
while (1) {
$k = rg_cache_prepare_key($ns_var);
rg_cache_core_set($k, $value);
$para = $k . '=' . rg_cache_prepare($value);
$ret = rg_cache_send('SET', $para, $flags);
if ($ret === FALSE)
break;
if ($rg_cache_debug)
rg_log('cache_set[' . $ns_var . '] = ' . rg_array2string($value));
$ret = TRUE;
break;
}
rg_prof_end('cache_set');
return $ret;
}
/*
* Increments a variable in the cache daemon
*/
function rg_cache_inc($ns_var)
{
rg_prof_start('cache_inc');
rg_cache_core_inc($vs_var);
while (1) {
$k = rg_cache_prepare_key($ns_var);
$ret = rg_cache_send('INC', $k, $flags);
if ($ret === FALSE)
break;
if ($ret === TRUE)
break;
$ret = intval($ret);
break;
}
rg_prof_end('cache_inc');
return $ret;
}
/*
* Unsets a variable in the cache daemon
*/
function rg_cache_unset($ns_var, $flags)
{
global $rg_cache_debug;
rg_prof_start('cache_unset');
$k = rg_cache_prepare_key($ns_var);
rg_cache_core_unset($k);
$ret = rg_cache_send('UNSET', $k, $flags);
if ($rg_cache_debug)
rg_log('cache_unset(' . $k . ')');
rg_prof_end('cache_unset');
return $ret;
}
/*
* Merge some k=v pairs into an existing cache
*/
function rg_cache_merge($ns_var, $list, $flags)
{
global $rg_cache_debug;
rg_prof_start('cache_merge');
$k = rg_cache_prepare_key($ns_var);
rg_cache_core_merge($k, $list);
$para = $k . '=' . rg_cache_prepare($list);
$ret = rg_cache_send('MERGE', $para, $flags);
if ($rg_cache_debug)
rg_log('cache_merge[' . $ns_var . '] = ' . rg_array2string($list));
rg_prof_end('cache_merge');
return $ret;
}
/*
* Push an array in a queue
*/
function rg_cache_apush($ns_var, $value, $flags)
{
rg_prof_start('cache_apush');
$k = rg_cache_prepare_key($ns_var);
rg_cache_core_apush($k, $value);
$para = $k . '=' . rg_cache_prepare($value);
$ret = rg_cache_send('APUSH', $para, $flags);
rg_prof_end('cache_apush');
return $ret;
}
/*
* Restarts the cache daemon
*/
function rg_cache_restart()
{
$flags = 0;
$para = '';
$ret = rg_cache_send('SHUTDOWN', $para, $flags);
if ($ret === FALSE)
return FALSE;
rg_log('Cache restarted.');
return TRUE;
}
/*
* Just to debug stuff
*/
function rg_cache_sleep()
{
$flags = RG_SOCKET_NO_WAIT;
$para = '';
$ret = rg_cache_send('SLEEP', $para, $flags);
if ($ret === FALSE)
return FALSE;
return TRUE;
}
?>