$MCACHE - rework memcached support - some rework on eaccelerator
[moodle.git] / lib / eaccelerator.class.php
1 <?php
2 /**
3  ** This class abstracts eaccelerator/turckmmcache
4  ** API to provide
5  ** 
6  ** - get()
7  ** - set()
8  ** - delete()
9  ** - getforfill()
10  ** - releaseforfill()
11  **
12  ** Author: Martin Langhoff <martin@catalyst.net.nz>
13  **
14  ** Note: do NOT store booleans here. For compatibility with
15  ** memcached, a false value is indistinguisable from a 
16  ** "not found in cache" response.
17  **/
20 class eaccelerator {
22     function eaccelerator() {
23         global $CFG;
24         if ( function_exists('eaccelerator_get')) {
25             $mode = 'eaccelerator';
26         } elseif (function_exists('mmcache_get')) {
27             $mode = 'mmcache';
28         } else {
29             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.");
30         }
32         $this->mode   = $mode;
33         $this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
34     }
36     function status() {
37         if (isset($this->mode)) {
38             return true;
39         }
40         return false;
41     }
43     function set($key, $value, $ttl=0) {
44         $set    = $this->mode . '_put';
45         $unlock = $this->mode . '_unlock';
47         // we may have acquired a lock via getforfill
48         // release if it exists
49         @$unlock($this->prefix . $key . '_forfill');
51         return $set($this->prefix . $key, serialize($value), $ttl);
52     }
54     function get($key) {
55         $fn = $this->mode . '_get';
56         $rec = $fn($this->prefix . $key);
57         if (is_null($rec)) {
58             return false;
59         }
60         return unserialize($rec);
61     } 
62         
63     function delete($key) {
64         $fn = $this->mode . '_rm';
65         return $fn($this->prefix . $key);
66     }
68     /**
69      * In the simple case, this function will 
70      * get the cached value if available. If the entry
71      * is not cached, it will try to get an exclusive
72      * lock that announces that this process will
73      * populate the cache.
74      *
75      * If we fail to get the lock -- this means another
76      * process is doing it. 
77      * so we wait (block) for a few microseconds while we wait for
78      * the cache to be filled or the lock to timeout.
79      * 
80      * If you get a false from this call, you _must_
81      * populate the cache ASAP or indicate that
82      * you won't by calling releaseforfill().
83      *
84      * This technique forces serialisation and so helps deal 
85      * with thundering herd scenarios where a lot of clients 
86      * ask the for the same idempotent (and costly) operation. 
87      * The implementation is based on suggestions in this message
88      * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2
89      *
90      * @param $key string
91      * @return mixed on cache hit, false otherwise
92      */
93     function getforfill ($key) {
94         $get    = $this->mode . '_get';
95         $lock   = $this->mode . '_lock';
96         
97         $rec = $get($this->prefix . $key);
98         if (!is_null($rec)) {
99             return unserialize($rec);
100         }
101         if ($lock($this->prefix . $key . '_forfill')) {
102             // we obtained the _forfill lock
103             // our caller will compute and set the value
104             return false;
105         }
106         // someone else has the lock
107         // "block" till we can get the value
108         // actually, loop .05s waiting for it
109         for ($n=0;$n<5;$n++) {
110             usleep(10000);
111             $rec = $get($this->prefix . $key);
112             if (!is_null($rec)) {
113                 return unserialize($rec);
114             }
115         }
116         return false;
117     }
119     /**
120      * Release the exclusive lock obtained by 
121      * getforfill(). See getforfill()
122      * for more details.
123      *
124      * @param $key string
125      * @return bool
126      */
127     function releaseforfill ($key) {
128         $unlock = $this->mode . '_unlock';
129         return $unlock($this->prefix . $key . '_forfill');
130     }
133 ?>