Merge pull request #3934 from dhensby/pulls/cofig-lru-fix

Change the LRU cache to a simple in-memory model
This commit is contained in:
Loz Calver 2015-03-09 09:31:04 +00:00
commit 93dca9aabd
2 changed files with 115 additions and 12 deletions

View File

@ -250,7 +250,7 @@ class Config {
* leak through to other instances. * leak through to other instances.
*/ */
public function __construct() { public function __construct() {
$this->cache = new Config_LRU(); $this->cache = new Config_MemCache();
} }
public function __clone() { public function __clone() {
@ -681,6 +681,7 @@ class Config {
/** /**
* @package framework * @package framework
* @subpackage core * @subpackage core
* @deprecated 3.2
*/ */
class Config_LRU { class Config_LRU {
const SIZE = 1000; const SIZE = 1000;
@ -692,6 +693,7 @@ class Config_LRU {
protected $c = 0; protected $c = 0;
public function __construct() { public function __construct() {
Deprecation::notice('3.2', 'Please use Config_MemCache instead', Deprecation::SCOPE_CLASS);
if (version_compare(PHP_VERSION, '5.3.7', '<')) { if (version_compare(PHP_VERSION, '5.3.7', '<')) {
// SplFixedArray causes seg faults before PHP 5.3.7 // SplFixedArray causes seg faults before PHP 5.3.7
$this->cache = array(); $this->cache = array();
@ -787,6 +789,69 @@ class Config_LRU {
} }
} }
/**
* @package framework
* @subpackage core
*/
class Config_MemCache {
protected $cache;
protected $i = 0;
protected $c = 0;
protected $tags = array();
public function __construct() {
$this->cache = array();
}
public function set($key, $val, $tags = array()) {
foreach($tags as $t) {
if(!isset($this->tags[$t])) {
$this->tags[$t] = array();
}
$this->tags[$t][$key] = true;
}
$this->cache[$key] = array($val, $tags);
}
private $hit = 0;
private $miss = 0;
public function stats() {
return $this->miss ? ($this->hit / $this->miss) : 0;
}
public function get($key) {
if(isset($this->cache[$key])) {
++$this->hit;
return $this->cache[$key][0];
}
++$this->miss;
return false;
}
public function clean($tag = null) {
if($tag) {
if(isset($this->tags[$tag])) {
foreach($this->tags[$tag] as $k => $dud) {
// Remove the key from everywhere else it is tagged
$ts = $this->cache[$k][1];
foreach($ts as $t) {
unset($this->tags[$t][$k]);
}
unset($this->cache[$k]);
}
unset($this->tags[$tag]);
}
} else {
$this->cache = array();
$this->tags = array();
}
}
}
/** /**
* @package framework * @package framework
* @subpackage core * @subpackage core

View File

@ -254,54 +254,92 @@ class ConfigTest extends SapphireTest {
$this->markTestIncomplete(); $this->markTestIncomplete();
} }
public function testLRUDiscarding() { public function testCacheCleaning() {
$cache = new ConfigTest_Config_LRU(); $cache = new ConfigTest_Config_MemCache();
for ($i = 0; $i < 1000; $i++) $cache->set($i, $i);
$this->assertEquals(1000, count($cache->cache));
$cache->clean();
$this->assertEquals(0, count($cache->cache), 'Clean clears all items');
$this->assertFalse($cache->get(1), 'Clean clears all items');
$cache->set(1, 1, array('Foo'));
$this->assertEquals(1, count($cache->cache));
$this->assertEquals(1, count($cache->tags));
$cache->clean('Foo');
$this->assertEquals(0, count($cache->tags), 'Clean items with matching tag');
$this->assertFalse($cache->get(1), 'Clean items with matching tag');
$cache->set(1, 1, array('Foo', 'Bar'));
$this->assertEquals(2, count($cache->tags));
$this->assertEquals(1, count($cache->cache));
$cache->clean('Bar');
$this->assertEquals(1, count($cache->tags));
$this->assertEquals(0, count($cache->cache), 'Clean items with any single matching tag');
$this->assertFalse($cache->get(1), 'Clean items with any single matching tag');
}
public function testLRUDiscarding() {
$depSettings = Deprecation::dump_settings();
Deprecation::restore_settings(array(
'level' => false,
'version' => false,
'moduleVersions' => false,
));
$cache = new ConfigTest_Config_LRU();
for ($i = 0; $i < Config_LRU::SIZE*2; $i++) $cache->set($i, $i); for ($i = 0; $i < Config_LRU::SIZE*2; $i++) $cache->set($i, $i);
$this->assertEquals( $this->assertEquals(
Config_LRU::SIZE, count($cache->indexing), Config_LRU::SIZE, count($cache->indexing),
'Homogenous usage gives exact discarding' 'Homogenous usage gives exact discarding'
); );
$cache = new ConfigTest_Config_LRU(); $cache = new ConfigTest_Config_LRU();
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i); for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i);
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set(-1, -1); for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set(-1, -1);
$this->assertLessThan( $this->assertLessThan(
Config_LRU::SIZE, count($cache->indexing), Config_LRU::SIZE, count($cache->indexing),
'Heterogenous usage gives sufficient discarding' 'Heterogenous usage gives sufficient discarding'
); );
Deprecation::restore_settings($depSettings);
} }
public function testLRUCleaning() { public function testLRUCleaning() {
$depSettings = Deprecation::dump_settings();
Deprecation::restore_settings(array(
'level' => false,
'version' => false,
'moduleVersions' => false,
));
$cache = new ConfigTest_Config_LRU(); $cache = new ConfigTest_Config_LRU();
for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i); for ($i = 0; $i < Config_LRU::SIZE; $i++) $cache->set($i, $i);
$this->assertEquals(Config_LRU::SIZE, count($cache->indexing)); $this->assertEquals(Config_LRU::SIZE, count($cache->indexing));
$cache->clean(); $cache->clean();
$this->assertEquals(0, count($cache->indexing), 'Clean clears all items'); $this->assertEquals(0, count($cache->indexing), 'Clean clears all items');
$this->assertFalse($cache->get(1), 'Clean clears all items'); $this->assertFalse($cache->get(1), 'Clean clears all items');
$cache->set(1, 1, array('Foo')); $cache->set(1, 1, array('Foo'));
$this->assertEquals(1, count($cache->indexing)); $this->assertEquals(1, count($cache->indexing));
$cache->clean('Foo'); $cache->clean('Foo');
$this->assertEquals(0, count($cache->indexing), 'Clean items with matching tag'); $this->assertEquals(0, count($cache->indexing), 'Clean items with matching tag');
$this->assertFalse($cache->get(1), 'Clean items with matching tag'); $this->assertFalse($cache->get(1), 'Clean items with matching tag');
$cache->set(1, 1, array('Foo', 'Bar')); $cache->set(1, 1, array('Foo', 'Bar'));
$this->assertEquals(1, count($cache->indexing)); $this->assertEquals(1, count($cache->indexing));
$cache->clean('Bar'); $cache->clean('Bar');
$this->assertEquals(0, count($cache->indexing), 'Clean items with any single matching tag'); $this->assertEquals(0, count($cache->indexing), 'Clean items with any single matching tag');
$this->assertFalse($cache->get(1), 'Clean items with any single matching tag'); $this->assertFalse($cache->get(1), 'Clean items with any single matching tag');
Deprecation::restore_settings($depSettings);
} }
} }
class ConfigTest_Config_LRU extends Config_LRU implements TestOnly { class ConfigTest_Config_LRU extends Config_LRU implements TestOnly {
public $cache; public $cache;
public $indexing; public $indexing;
}
class ConfigTest_Config_MemCache extends Config_MemCache implements TestOnly {
public $cache;
public $tags;
} }