$MACHE: add support for eaccelerator/memcached, change the $MCACHE calling convention...
authormartinlanghoff <martinlanghoff>
Wed, 27 Dec 2006 22:47:14 +0000 (22:47 +0000)
committermartinlanghoff <martinlanghoff>
Wed, 27 Dec 2006 22:47:14 +0000 (22:47 +0000)
 - this effectively breaks memcached support for the moment

lib/eaccelerator.class.php [new file with mode: 0644]
lib/setup.php
lib/setuplib.php

diff --git a/lib/eaccelerator.class.php b/lib/eaccelerator.class.php
new file mode 100644 (file)
index 0000000..d07f0ee
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+/**
+ ** This class abstracts eaccelerator/turckmmcache
+ ** API to provide
+ ** 
+ ** - get()
+ ** - set()
+ ** - delete()
+ ** - getforfill()
+ ** - releaseforfill()
+ **
+ ** Author: Martin Langhoff <martin@catalyst.net.nz>
+ **
+ **/
+
+
+class eaccelerator {
+
+    function eaccelerator() {
+        global $CFG;
+        if ( function_exists('eaccelerator_get')) {
+            $mode = 'eaccelerator';
+        } elseif (function_exists('mmcache_get')) {
+            $mode = 'mmcache';
+        } else {
+            debugging("\$CFG->eaccelerator is set to true but the required functions are not available. You need to have either eaccelerator or turckmmcache extensions installed, compiled with the shmem keys option enabled.");
+        }
+
+        $this->mode   = $mode;
+        $this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
+    }
+
+    function status() {
+        if (isset($this->mode)) {
+            return true;
+        }
+        return false;
+    }
+
+    function set($key, $value, $ttl=0) {
+        $set    = $this->mode . '_put';
+        $unlock = $this->mode . '_unlock';
+
+        // we may have acquired a lock via getforfill
+        // release if it exists
+        @$unlock($this->prefix . $key . '_forfill');
+
+        return $set($this->prefix . $key, serialize($value), $ttl);
+    }
+
+    function get($key) {
+        $fn = $this->mode . '_get';
+        $rec = $fn($this->prefix . $key);
+        if (is_null($rec)) {
+            return null;
+        }
+        return unserialize($rec);
+    } 
+        
+    function delete($key) {
+        $fn = $this->mode . '_rm';
+        return $fn($this->prefix . $key);
+    }
+
+    /**
+     * In the simple case, this function will 
+     * get the cached value if available. If the entry
+     * is not cached, it will try to get an exclusive
+     * lock that announces that this process will
+     * populate the cache.
+     *
+     * If we fail to get the lock -- this means another
+     * process is doing it. 
+     * so we wait (block) for a few microseconds while we wait for
+     * the cache to be filled or the lock to timeout.
+     * 
+     * If you get a false from this call, you _must_
+     * populate the cache ASAP or indicate that
+     * you won't by calling releaseforfill().
+     *
+     * This technique forces serialisation and so helps deal 
+     * with thundering herd scenarios where a lot of clients 
+     * ask the for the same idempotent (and costly) operation. 
+     * The implementation is based on suggestions in this message
+     * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2
+     *
+     * @param $key string
+     * @return mixed on cache hit, NULL otherwise
+     */
+    function getforfill ($key) {
+        $get    = $this->mode . '_get';
+        $lock   = $this->mode . '_lock';
+        
+        $rec = $get($this->prefix . $key);
+        if (!is_null($rec)) {
+            return unserialize($rec);
+        }
+        if ($lock($this->prefix . $key . '_forfill')) {
+            // we obtained the _forfill lock
+            // our caller will compute and set the value
+            return null;
+        }
+        // someone else has the lock
+        // "block" till we can get the value
+        // actually, loop .05s waiting for it
+        for ($n=0;$n<5;$n++) {
+            usleep(10000);
+            $rec = $get($this->prefix . $key);
+            if (!is_null($rec)) {
+                return unserialize($rec);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Release the exclusive lock obtained by 
+     * getforfill(). See getforfill()
+     * for more details.
+     *
+     * @param $key string
+     * @return bool
+     */
+    function releaseforfill ($key) {
+        $unlock = $this->mode . '_unlock';
+        return $unlock($this->prefix . $key . '_forfill');
+    }
+}
+
+?>
\ No newline at end of file
index fff6683..eae03dd 100644 (file)
@@ -261,6 +261,9 @@ global $HTTPSPAGEREQUIRED;
     if (!empty($CFG->memcached) && !empty($CFG->memcachedhosts)) {
        init_memcached();
     }
+    if (!empty($CFG->eaccelerator)) {
+        init_eaccelerator();
+    }
 
 /// Set a default enrolment configuration (see bug 1598)
     if (!isset($CFG->enrol)) {
index 2fe294a..dd7a43f 100644 (file)
@@ -236,4 +236,18 @@ function init_memcached() {
     return true;
 }
 
+function init_eaccelerator() {
+    global $CFG, $MCACHE;
+
+    include_once($CFG->libdir . '/eaccelerator.class.php');
+    $MCACHE = new eaccelerator;
+    if ($MCACHE->status) {
+        return true;
+    } 
+    unset($MCACHE);
+    return false;
+}
+
+
+
 ?>