forked from moodle/moodle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmemcached.class.php
135 lines (119 loc) · 3.9 KB
/
memcached.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
<?php
/**
** This class abstracts PHP's PECL memcached
** API to provide
**
** - get()
** - set()
** - delete()
** - getforfill()
** - releaseforfill()
**
** Author: Martin Langhoff <[email protected]>
**
** Note: do NOT store booleans here. With memcached, a false value
** is indistinguisable from a "not found in cache" response.
**/
class memcached {
function memcached() {
global $CFG;
if (!function_exists('memcache_connect')) {
debugging("Memcached is set to true but the memcached extension is not installed");
return false;
}
$this->_cache = new Memcache;
$hosts = split(',', $CFG->memcachedhosts);
if (count($hosts) === 1 && !empty($CFG->memcachedpconn)) {
// the faster pconnect is only available
// for single-server setups
// NOTE: PHP-PECL client is buggy and pconnect()
// will segfault if the server is unavailable
$this->_cache->pconnect($hosts[0]);
} else {
// multi-host setup will share key space
foreach ($hosts as $host) {
$host = trim($host);
$this->_cache->addServer($host);
}
}
$this->prefix = $CFG->dbname .'|' . $CFG->prefix . '|';
}
function status() {
if (is_object($this->_cache)) {
return true;
}
return false;
}
function set($key, $value, $ttl=0) {
// we may have acquired a lock via getforfill
// release if it exists
@$this->_cache->delete($this->prefix . $key . '_forfill');
return $this->_cache->set($this->prefix . $key, $value, false);
}
function get($key) {
$rec = $this->_cache->get($this->prefix . $key);
return $rec;
}
function delete($key) {
return $this->_cache->delete($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) {
$rec = $this->_cache->get($this->prefix . $key);
if ($rec) {
return $rec;
}
if ($this->_cache->add($this->prefix . $key . '_forfill', 'true', false, 1)) {
// we obtained the _forfill lock
// our caller will compute and set the value
return false;
}
// 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 = $this->_cache->get($this->prefix . $key);
if ($rec) {
return $rec;
}
}
return false;
}
/**
* Release the exclusive lock obtained by
* getforfill(). See getforfill()
* for more details.
*
* @param $key string
* @return bool
*/
function releaseforfill ($key) {
return $this->_cache->delete($this->prefix . $key . '_forfill');
}
}
?>