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