-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmemcache.install
376 lines (351 loc) · 14.2 KB
/
memcache.install
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
<?php
/**
* @file
* Install, update and uninstall functions for the memcache module.
*/
const MEMCACHE_PECL_RECOMMENDED = '3.0.6';
const MEMCACHE_PECL_PHP7_RECOMMENDED = '4.0.5';
const MEMCACHED_PECL_RECOMMENDED = '2.0.1';
const MEMCACHED_ASCII_AUTH_MINIMUM = '1.5.15';
const MEMCACHED_ASCII_AUTH_RECOMMENDED = '1.6.4';
/**
* Implements hook_enable().
*/
function memcache_enable() {
$error = FALSE;
$warning = FALSE;
$severity = 'status';
$memcache = extension_loaded('memcache');
$memcached = extension_loaded('memcached');
if (!$memcache && !$memcached) {
$error = TRUE;
}
if (!function_exists('dmemcache_object')) {
// dmemcache.inc isn't loaded.
$error = TRUE;
}
else {
if (!_memcache_pecl_version_valid()) {
$warning = TRUE;
}
// If ASCII protocol authentication is enabled, perform extra tests.
if (_dmemcache_use_ascii_auth()) {
// Verify minimum required memcached version for ASCII protocol
// authentication is installed.
$version = _memcached_ascii_auth_version_valid(MEMCACHED_ASCII_AUTH_MINIMUM);
// TRUE means a valid version, FALSE means we failed to connect and will
// throw a different error.
if ($version !== TRUE && $version !== FALSE) {
$error = TRUE;
}
else {
$version = _memcached_ascii_auth_version_valid(MEMCACHED_ASCII_AUTH_RECOMMENDED);
// TRUE means a valid version, FALSE means we failed to connect and will
// throw a different error.
if ($version !== TRUE && $version !== FALSE) {
$warning = TRUE;
}
}
}
// Make a test connection to all configured memcache servers.
$memcache_servers = settings_get('memcache_servers', ['127.0.0.1:11211' => 'default']);
foreach ($memcache_servers as $server => $bin) {
if ($cluster = dmemcache_object_cluster($bin)) {
$memcache = dmemcache_instance($cluster['cluster']);
if (dmemcache_connect($memcache, $server, $cluster['weight']) === FALSE) {
$error = TRUE;
}
else {
if (!settings_get('memcache_persistent', TRUE)) {
dmemcache_close($memcache);
}
}
}
}
}
if ($error) {
$severity = 'error';
}
elseif ($warning) {
$severity = 'warning';
}
if ($error || $warning) {
backdrop_set_message(t('There are problems with your Memcache configuration. Please review %readme and visit the Backdrop admin !status page for more information.', [
'%readme' => 'README.md',
'!status' => l(t('status report'), 'admin/reports/status'),
]), $severity);
}
}
/**
* Implements hook_requirements().
*/
function memcache_requirements($phase) {
$requirements = [];
$t = get_t();
$memcache = extension_loaded('memcache');
$memcached = extension_loaded('memcached');
if ($phase == 'install' || $phase == 'runtime') {
$requirements['memcache_extension']['title'] = $t('Memcache');
if (!$memcache && !$memcached) {
$requirements['memcache_extension']['severity'] = REQUIREMENT_ERROR;
$requirements['memcache_extension']['value'] = $t('Required PHP extension not found. Install the <a href="https://www.php.net/manual/en/book.memcache.php">memcache</a> (recommended) or <a href="https://www.php.net/manual/en/book.memcached.php">memcached</a> extension.');
}
else {
$requirements['memcache_extension']['value'] = $t('PHP %extension Extension', ['%extension' => $memcache ? $t('Memcache') : $t('Memcached')]);
}
}
if ($phase == 'runtime') {
$errors = [];
$warnings = [];
if (!$memcache && !$memcached) {
$errors[] = $t('Required PHP extension not found. Install the <a href="https://www.php.net/manual/en/book.memcache.php">memcache</a> (recommended) or <a href="https://www.php.net/manual/en/book.memcached.php">memcached</a> extension.');
}
if (!function_exists('dmemcache_set')) {
// dmemcache.inc isn't loaded.
$errors[] = $t('Failed to load required file %dmemcache.', ['%dmemcache' => backdrop_get_path('module', 'memcache') . '/' . 'dmemcache.inc']);
$requirements['memcache_extension']['value'] = $t('Unknown');
}
else {
$extension = dmemcache_extension();
$recommended = $t('Unknown');
if ($extension == 'Memcache') {
$version = phpversion('memcache');
if (version_compare(phpversion(), '7', '>=')) {
$recommended = MEMCACHE_PECL_PHP7_RECOMMENDED;
}
else {
$recommended = MEMCACHE_PECL_RECOMMENDED;
}
}
elseif ($extension == 'Memcached') {
$version = phpversion('memcached');
$recommended = MEMCACHED_PECL_RECOMMENDED;
}
if (empty($version)) {
$version = $t('Unknown');
}
$requirements['memcache_extension']['value'] = $version . _memcache_statistics_link();
if (!_memcache_pecl_version_valid()) {
$warnings[] = $t('PECL !extension version %version is unsupported. Please update to %recommended or newer.', [
'!extension' => $extension,
'%version' => $version,
'%recommended' => $recommended,
]);
}
// If ASCII protocol authentication is enabled, perform extra tests.
if (_dmemcache_use_ascii_auth()) {
// Verify minimum required memcached version for ASCII protocol authentication is installed.
$version = _memcached_ascii_auth_version_valid(MEMCACHED_ASCII_AUTH_MINIMUM);
// TRUE means version valid, FALSE means we failed to connect and will throw a different error.
if ($version !== TRUE && $version !== FALSE) {
$errors[] = $t('ASCII protocol authentication is enabled but requires memcached v%minimum or greater. One or more memcached instances detected running memcache v%version.' . _memcache_statistics_link(), [
'%version' => $version,
'%minimum' => MEMCACHED_ASCII_AUTH_MINIMUM,
]);
}
else {
$version = _memcached_ascii_auth_version_valid(MEMCACHED_ASCII_AUTH_RECOMMENDED);
// TRUE means a valid version, FALSE means we failed to connect and
// will throw a different error.
if ($version !== TRUE && $version !== FALSE) {
$warnings[] = $t('Memcached version %recommended or newer is recommended when using ASCII protocol authentication, to avoid a CPU race. One or more memcached instances detected are running memcache v%version.' . _memcache_statistics_link(), [
'%version' => $version,
'%recommended' => MEMCACHED_ASCII_AUTH_RECOMMENDED,
]);
}
}
// Confirm ASCII authentication works on all memcached servers.
$memcache_bins = settings_get('memcache_bins', ['cache' => 'default']);
foreach ($memcache_bins as $bin => $_) {
if ($mc = dmemcache_object($bin)) {
$ascii_auth = _check_ascii_auth($mc);
if ($ascii_auth !== TRUE) {
$s = $ascii_auth[0];
$message = $ascii_auth[1];
$errors[] = $t('ASCII protocol authentication failed: %message (%host:%port).' . _memcache_statistics_link(), [
'%host' => $s['host'],
'%port' => $s['port'],
'%message' => $message,
]);
}
}
}
}
// Make a test connection to all configured memcache servers.
$memcache_servers = settings_get('memcache_servers', ['127.0.0.1:11211' => 'default']);
foreach ($memcache_servers as $server => $bin) {
if ($cluster = dmemcache_object_cluster($bin)) {
$memcache = dmemcache_instance($cluster['cluster']);
if (dmemcache_connect($memcache, $server, $cluster['weight']) === FALSE) {
$errors[] = $t('Failed to connect to memcached server instance at %server.', ['%server' => $server]);
}
else {
if (!settings_get('memcache_persistent', TRUE)) {
dmemcache_close($memcache);
}
}
}
}
// Set up a temporary bin to see if we can store a value and then
// successfully retrieve it.
try {
$cid = 'memcache_requirements_test';
$value = 'OK';
// Temporarily store a test value in memcache.
cache()->set($cid, $value);
// Retrieve the test value from memcache.
$data = cache()->get($cid);
if (!isset($data->data) || $data->data !== $value) {
$errors[] = $t('Failed to store to then retrieve data from memcache.');
}
else {
// Test a delete as well.
cache()->delete($cid);
$result = cache()->get($cid);
if ($result) {
$errors[] = $t('Stored cache data was not deleted.');
}
}
} catch (Exception $e) {
// An unexpected exception occurred.
$errors[] = $t('Unexpected failure when testing memcache configuration.');
}
// Core's lock implementation can cause worse performance together with
// stampede protection. Plus, long keys will be truncated resulting in
// dropped locks.
if (settings_get('memcache_stampede_protection', FALSE) && strpos(settings_get('lock_inc', 'includes/lock.inc'), 'includes/lock.inc') !== FALSE) {
$warnings[] = $t('Backdrop\'s core lock implementation (%core) is not supported by memcache stampede protection. Enable the memcache lock implementation (%memcache) or disable memcache stampede protection.', [
'%core' => 'includes/lock.inc',
'%memcache' => backdrop_get_path('module', 'memcache') . "/memcache-lock.inc",
]);
}
}
if (!empty($errors)) {
// Error takes precedence over warning, stack together errors and
// warnings and display as error messages.
$errors = array_merge($errors, $warnings);
unset($warnings);
$requirements['memcache_extension']['severity'] = REQUIREMENT_ERROR;
$requirements['memcache_extension']['description'] = $t('There is a problem with your memcache configuration, check the Backdrop logs for additional errors. Please review %readme for help resolving the following !issue: !errors', [
'%readme' => backdrop_get_path('module', 'memcache') . '/' . 'README.md',
'!issue' => format_plural(count($errors), 'issue', 'issues'),
'!errors' => '<ul><li>' . implode('<li>', $errors),
]);
}
elseif (!empty($warnings)) {
$requirements['memcache_extension']['severity'] = REQUIREMENT_WARNING;
$requirements['memcache_extension']['description'] = $t('There is a problem with your memcache configuration. Please review %readme for help resolving the following !issue: !warnings', [
'%readme' => backdrop_get_path('module', 'memcache') . '/' . 'README.md',
'!issue' => format_plural(count($warnings), 'issue', 'issues'),
'!warnings' => '<ul><li>' . implode('<li>', $warnings),
]);
}
else {
$requirements['memcache_extension']['severity'] = REQUIREMENT_OK;
}
}
return $requirements;
}
/**
* Add "(more information)" link after memcache version if memcache_admin
* module is enabled and user has access to memcache statistics.
*/
function _memcache_statistics_link() {
$t = get_t();
if (module_exists('memcache_admin') && user_access('access memcache statistics')) {
return ' (' . l($t('more information'), 'admin/reports/memcache') . ')';
}
else {
return '';
}
}
/**
* Validate whether the current PECL version is supported.
*/
function _memcache_pecl_version_valid() {
$extension = dmemcache_extension();
if ($extension == 'Memcache') {
// The latest PECL Memcache releases are using a 4 digit version
// which version_compare doesn't support. At this time the 4th
// digit isn't important to us, so we just look at the first 3
// digits. If a future release requires 4 digit precision
// it will require a replacement for version_compare().
$full_version = phpversion('memcache');
$version = implode('.', array_slice(explode('.', $full_version), 0, 3));
if (version_compare(phpversion(), '7', '>=')) {
return version_compare($version, MEMCACHE_PECL_PHP7_RECOMMENDED, '>=');
}
else {
return version_compare($version, MEMCACHE_PECL_RECOMMENDED, '>=');
}
}
elseif ($extension == 'Memcached') {
return version_compare(phpversion('memcached'), MEMCACHED_PECL_RECOMMENDED, '>=');
}
return FALSE;
}
/**
* If ASCII protocol authentication is enabled, validate whether the current
* memcached version meets the minimum and/or recommended requirements.
*/
function _memcached_ascii_auth_version_valid($version) {
if (_dmemcache_use_ascii_auth()) {
$stats = dmemcache_stats();
foreach ($stats as $bin => $servers) {
if (!empty($servers)) {
foreach ($servers as $server => $value) {
$installed_version = $value['version'];
if (version_compare($installed_version, $version, '<')) {
// Return detected invalid version of memcached.
return $installed_version;
}
}
}
else {
return FALSE;
}
}
}
// Fall through if no unsupported version of memcached was detected, or ascii auth is
// not enabled.
return TRUE;
}
/**
* Check that ascii authentication is setup correctly.
*
* @param Memcache|Memcached $memcache_original
* The memcache object whose servers will be checked.
*
* @return TRUE|array
* TRUE on success, the failed server and the error message
* on failure.
*/
function _check_ascii_auth($memcache_original) {
$servers = $memcache_original->getServerList();
foreach ($servers as $s) {
// Note: This intentionally does not use a persistent id.
$mc = new Memcached();
$mc->addServer($s['host'], $s['port']);
// Check that a set() / get() with a fresh connection fails.
$mc->set(BACKDROP_MEMCACHE_ASCII_AUTH_LIFETIME_KEY, TRUE);
$rc = $mc->get(BACKDROP_MEMCACHE_ASCII_AUTH_LIFETIME_KEY);
if ($rc) {
return [$s, t('ASCII authentication not enabled on server')];
}
$rc = _dmemcache_ensure_ascii_auth("0", $mc);
if (!$rc) {
return [$s, t('ASCII authentication failed')];
}
}
return TRUE;
}
/**
* Clean up old memcache variables.
*/
function memcache_update_1000() {
if (db_table_exists('variable')) {
db_query("DELETE FROM {variable} WHERE name LIKE :memcache", [
':memcache' => 'memcache_%',
]);
}
}