6ead149e1227f209ae3ed82e0a755c6379662d51
[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             $this->mode = 'eaccelerator';
26         } elseif (function_exists('mmcache_get')) {
27             $this->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->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
33     }
35     function status() {
36         if (isset($this->mode)) {
37             return true;
38         }
39         return false;
40     }
42     function set($key, $value, $ttl=0) {
43         $set    = $this->mode . '_put';
44         $unlock = $this->mode . '_unlock';
46         // we may have acquired a lock via getforfill
47         // release if it exists
48         @$unlock($this->prefix . $key . '_forfill');
50         return $set($this->prefix . $key, serialize($value), $ttl);
51     }
53     function get($key) {
54         $fn = $this->mode . '_get';
55         $rec = $fn($this->prefix . $key);
56         if (is_null($rec)) {
57             return false;
58         }
59         return unserialize($rec);
60     } 
61         
62     function delete($key) {
63         $fn = $this->mode . '_rm';
64         return $fn($this->prefix . $key);
65     }
67     /**
68      * In the simple case, this function will 
69      * get the cached value if available. If the entry
70      * is not cached, it will try to get an exclusive
71      * lock that announces that this process will
72      * populate the cache.
73      *
74      * If we fail to get the lock -- this means another
75      * process is doing it. 
76      * so we wait (block) for a few microseconds while we wait for
77      * the cache to be filled or the lock to timeout.
78      * 
79      * If you get a false from this call, you _must_
80      * populate the cache ASAP or indicate that
81      * you won't by calling releaseforfill().
82      *
83      * This technique forces serialisation and so helps deal 
84      * with thundering herd scenarios where a lot of clients 
85      * ask the for the same idempotent (and costly) operation. 
86      * The implementation is based on suggestions in this message
87      * http://marc.theaimsgroup.com/?l=git&m=116562052506776&w=2
88      *
89      * @param $key string
90      * @return mixed on cache hit, false otherwise
91      */
92     function getforfill ($key) {
93         $get    = $this->mode . '_get';
94         $lock   = $this->mode . '_lock';
95         
96         $rec = $get($this->prefix . $key);
97         if (!is_null($rec)) {
98             return unserialize($rec);
99         }
100         if ($lock($this->prefix . $key . '_forfill')) {
101             // we obtained the _forfill lock
102             // our caller will compute and set the value
103             return false;
104         }
105         // someone else has the lock
106         // "block" till we can get the value
107         // actually, loop .05s waiting for it
108         for ($n=0;$n<5;$n++) {
109             usleep(10000);
110             $rec = $get($this->prefix . $key);
111             if (!is_null($rec)) {
112                 return unserialize($rec);
113             }
114         }
115         return false;
116     }
118     /**
119      * Release the exclusive lock obtained by 
120      * getforfill(). See getforfill()
121      * for more details.
122      *
123      * @param $key string
124      * @return bool
125      */
126     function releaseforfill ($key) {
127         $unlock = $this->mode . '_unlock';
128         return $unlock($this->prefix . $key . '_forfill');
129     }
132 ?>