Fix getAllKeys() method for memcache instances

This commit is contained in:
Philipp Holzer 2019-08-15 13:58:01 +02:00
parent 41e2031e6b
commit e2e109b8c1
No known key found for this signature in database
GPG Key ID: D8365C3D36B77D90
5 changed files with 108 additions and 17 deletions

View File

@ -21,7 +21,7 @@ class ArrayCache extends Cache implements IMemoryCache
*/ */
public function getAllKeys($prefix = null) public function getAllKeys($prefix = null)
{ {
return $this->filterArrayKeysByPrefix($this->cachedData, $prefix); return $this->filterArrayKeysByPrefix(array_keys($this->cachedData), $prefix);
} }
/** /**

View File

@ -84,19 +84,19 @@ abstract class Cache implements ICache
* Filters the keys of an array with a given prefix * Filters the keys of an array with a given prefix
* Returns the filtered keys as an new array * Returns the filtered keys as an new array
* *
* @param array $array The array, which should get filtered * @param array $keys The keys, which should get filtered
* @param string|null $prefix The prefix (if null, all keys will get returned) * @param string|null $prefix The prefix (if null, all keys will get returned)
* *
* @return array The filtered array with just the keys * @return array The filtered array with just the keys
*/ */
protected function filterArrayKeysByPrefix($array, $prefix = null) protected function filterArrayKeysByPrefix(array $keys, string $prefix = null)
{ {
if (empty($prefix)) { if (empty($prefix)) {
return array_keys($array); return $keys;
} else { } else {
$result = []; $result = [];
foreach (array_keys($array) as $key) { foreach ($keys as $key) {
if (strpos($key, $prefix) === 0) { if (strpos($key, $prefix) === 0) {
array_push($result, $key); array_push($result, $key);
} }

View File

@ -27,6 +27,17 @@ class MemcachedCache extends Cache implements IMemoryCache
*/ */
private $logger; private $logger;
/**
* @var string First server address
*/
private $firstServer;
/**
* @var int First server port
*/
private $firstPort;
/** /**
* Due to limitations of the INI format, the expected configuration for Memcached servers is the following: * Due to limitations of the INI format, the expected configuration for Memcached servers is the following:
* array { * array {
@ -58,6 +69,9 @@ class MemcachedCache extends Cache implements IMemoryCache
} }
}); });
$this->firstServer = $memcached_hosts[0][0] ?? 'localhost';
$this->firstPort = $memcached_hosts[0][1] ?? 11211;
$this->memcached->addServers($memcached_hosts); $this->memcached->addServers($memcached_hosts);
if (count($this->memcached->getServerList()) == 0) { if (count($this->memcached->getServerList()) == 0) {
@ -70,14 +84,94 @@ class MemcachedCache extends Cache implements IMemoryCache
*/ */
public function getAllKeys($prefix = null) public function getAllKeys($prefix = null)
{ {
$keys = $this->getOriginalKeys($this->memcached->getAllKeys()); $keys = $this->getOriginalKeys($this->getMemcachedKeys());
if ($this->memcached->getResultCode() == Memcached::RES_SUCCESS) { return $this->filterArrayKeysByPrefix($keys, $prefix);
return $this->filterArrayKeysByPrefix($keys, $prefix); }
} else {
$this->logger->debug('Memcached \'getAllKeys\' failed', ['result' => $this->memcached->getResultMessage()]); /**
return []; * Get all memcached keys.
* Special function because getAllKeys() is broken since memcached 1.4.23.
*
* cleaned up version of code found on Stackoverflow.com by Maduka Jayalath
*
* @return array|int - all retrieved keys (or negative number on error)
*/
private function getMemcachedKeys()
{
$mem = @fsockopen($this->firstServer, $this->firstPort);
if ($mem === false) {
return -1;
} }
// retrieve distinct slab
$r = @fwrite($mem, 'stats items' . chr(10));
if ($r === false) {
return -2;
}
$slab = [];
while (($l = @fgets($mem, 1024)) !== false) {
// finished?
$l = trim($l);
if ($l == 'END') {
break;
}
$m = [];
// <STAT items:22:evicted_nonzero 0>
$r = preg_match('/^STAT\sitems\:(\d+)\:/', $l, $m);
if ($r != 1) {
return -3;
}
$a_slab = $m[1];
if (!array_key_exists($a_slab, $slab)) {
$slab[$a_slab] = [];
}
}
reset($slab);
foreach ($slab as $a_slab_key => &$a_slab) {
$r = @fwrite($mem, 'stats cachedump ' . $a_slab_key . ' 100' . chr(10));
if ($r === false) {
return -4;
}
while (($l = @fgets($mem, 1024)) !== false) {
// finished?
$l = trim($l);
if ($l == 'END') {
break;
}
$m = [];
// ITEM 42 [118 b; 1354717302 s]
$r = preg_match('/^ITEM\s([^\s]+)\s/', $l, $m);
if ($r != 1) {
return -5;
}
$a_key = $m[1];
$a_slab[] = $a_key;
}
}
// close the connection
@fclose($mem);
unset($mem);
$keys = [];
reset($slab);
foreach ($slab AS &$a_slab) {
reset($a_slab);
foreach ($a_slab AS &$a_key) {
$keys[] = $a_key;
}
}
unset($slab);
return $keys;
} }
/** /**

View File

@ -138,7 +138,7 @@ class DatabaseLock extends Lock
if (empty($prefix)) { if (empty($prefix)) {
$where = ['`expires` >= ?', DateTimeFormat::utcNow()]; $where = ['`expires` >= ?', DateTimeFormat::utcNow()];
} else { } else {
$where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix]; $where = ['`expires` >= ? AND `name` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
} }
$stmt = $this->dba->select('locks', ['name'], $where); $stmt = $this->dba->select('locks', ['name'], $where);

View File

@ -2,7 +2,6 @@
namespace Friendica\Test\src\Core\Cache; namespace Friendica\Test\src\Core\Cache;
use Friendica\Core\Cache\MemcachedCache;
use Friendica\Test\MockedTest; use Friendica\Test\MockedTest;
use Friendica\Util\PidFile; use Friendica\Util\PidFile;
@ -202,10 +201,6 @@ abstract class CacheTest extends MockedTest
*/ */
public function testGetAllKeys($value1, $value2, $value3) public function testGetAllKeys($value1, $value2, $value3)
{ {
if ($this->cache instanceof MemcachedCache) {
$this->markTestSkipped('Memcached doesn\'t support getAllKeys anymore');
}
$this->assertTrue($this->instance->set('value1', $value1)); $this->assertTrue($this->instance->set('value1', $value1));
$this->assertTrue($this->instance->set('value2', $value2)); $this->assertTrue($this->instance->set('value2', $value2));
$this->assertTrue($this->instance->set('test_value3', $value3)); $this->assertTrue($this->instance->set('test_value3', $value3));
@ -219,5 +214,7 @@ abstract class CacheTest extends MockedTest
$list = $this->instance->getAllKeys('test'); $list = $this->instance->getAllKeys('test');
$this->assertContains('test_value3', $list); $this->assertContains('test_value3', $list);
$this->assertNotContains('value1', $list);
$this->assertNotContains('value2', $list);
} }
} }