From bf59a44a600db60d109a4f1cd1cf7d1bd2c46c3c Mon Sep 17 00:00:00 2001 From: "Evgeniy V. Kokovikhin" Date: Thu, 26 Apr 2012 11:46:54 +0400 Subject: [PATCH] * + Sequential cache. See changelog for details --- .gitignore | 2 + core/Base/Assert.class.php | 8 ++ core/Cache/PeclMemcached.class.php | 105 +++++++++++--- core/Cache/SequentialCache.class.php | 132 ++++++++++++++++++ doc/ChangeLog | 11 +- main/Monitoring/PinbedPeclMemcached.class.php | 8 +- test/core/SequentialCacheTest.class.php | 80 +++++++++++ test/main/Utils/PinbaTest.class.php | 70 ++++++---- 8 files changed, 362 insertions(+), 54 deletions(-) create mode 100644 core/Cache/SequentialCache.class.php create mode 100644 test/core/SequentialCacheTest.class.php diff --git a/.gitignore b/.gitignore index cd9800c93d..d0eb9d2454 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ test/meta/Proto/ test/main/data/urls/parser.dump.new .idea test/onphp +/nbproject/ + diff --git a/core/Base/Assert.class.php b/core/Base/Assert.class.php index ea375b18d2..1c41da0ffc 100644 --- a/core/Base/Assert.class.php +++ b/core/Base/Assert.class.php @@ -277,6 +277,14 @@ public static function isUnreachable($message = 'unreachable code reached') throw new WrongArgumentException($message); } + public static function isObject($object, $message = null) + { + if (!is_object($object)) + throw new WrongArgumentException( + $message.' not object given' + ); + } + /// exceptionless methods //@{ public static function checkInteger($value) diff --git a/core/Cache/PeclMemcached.class.php b/core/Cache/PeclMemcached.class.php index b8d9a59cf7..e2c41fc41d 100644 --- a/core/Cache/PeclMemcached.class.php +++ b/core/Cache/PeclMemcached.class.php @@ -1,6 +1,6 @@ instance = new Memcache(); - - try { - try { - $this->instance->pconnect($host, $port); - } catch (BaseException $e) { - $this->instance->connect($host, $port); - } - - $this->alive = true; - } catch (BaseException $e) { - // bad luck. - } + $this->host = $host; + $this->port = $port; + $this->connectTimeout = $connectTimeout; } public function __destruct() @@ -66,11 +64,20 @@ public function __destruct() } } + public function isAlive() + { + $this->ensureTriedToConnect(); + + return parent::isAlive(); + } + /** * @return PeclMemcached **/ public function clean() { + $this->ensureTriedToConnect(); + try { $this->instance->flush(); } catch (BaseException $e) { @@ -82,6 +89,8 @@ public function clean() public function increment($key, $value) { + $this->ensureTriedToConnect(); + try { return $this->instance->increment($key, $value); } catch (BaseException $e) { @@ -91,6 +100,8 @@ public function increment($key, $value) public function decrement($key, $value) { + $this->ensureTriedToConnect(); + try { return $this->instance->decrement($key, $value); } catch (BaseException $e) { @@ -100,6 +111,8 @@ public function decrement($key, $value) public function getList($indexes) { + $this->ensureTriedToConnect(); + return ($return = $this->get($indexes)) ? $return @@ -108,6 +121,8 @@ public function getList($indexes) public function get($index) { + $this->ensureTriedToConnect(); + try { return $this->instance->get($index); } catch (BaseException $e) { @@ -124,6 +139,8 @@ public function get($index) public function delete($index) { + $this->ensureTriedToConnect(); + try { // second parameter required, wrt new memcached protocol: // delete key 0 (see process_delete_command in the memcached.c) @@ -138,6 +155,8 @@ public function delete($index) public function append($key, $data) { + $this->ensureTriedToConnect(); + try { return $this->instance->append($key, $data); } catch (BaseException $e) { @@ -147,10 +166,58 @@ public function append($key, $data) Assert::isUnreachable(); } + /** + * @param float $requestTimeout time in seconds + * @return PeclMemcached + */ + public function setTimeout($requestTimeout) + { + $this->ensureTriedToConnect(); + $this->requestTimeout = $requestTimeout; + $this->instance->setServerParams($this->host, $this->port, $requestTimeout); + + return $this; + } + + /** + * @return float + */ + public function getTimeout() + { + return $this->requestTimeout; + } + + protected function ensureTriedToConnect() + { + if ($this->triedConnect) + return $this; + + $this->triedConnect = true; + $this->instance = new Memcache(); + + try { + + try { + $this->instance->pconnect($this->host, $this->port, $this->connectTimeout); + } catch (BaseException $e) { + $this->instance->connect($this->host, $this->port, $this->connectTimeout); + } + + $this->alive = true; + + } catch (BaseException $e) { + // bad luck. + } + + return $this; + } + protected function store( $action, $key, $value, $expires = Cache::EXPIRES_MEDIUM ) { + $this->ensureTriedToConnect(); + try { return $this->instance->$action( @@ -167,5 +234,5 @@ protected function store( Assert::isUnreachable(); } + } -?> \ No newline at end of file diff --git a/core/Cache/SequentialCache.class.php b/core/Cache/SequentialCache.class.php new file mode 100644 index 0000000000..9a58c5e0e5 --- /dev/null +++ b/core/Cache/SequentialCache.class.php @@ -0,0 +1,132 @@ +setMaster($master); + + foreach ($slaves as $cache) { + $this->addPeer($cache); + } + } + + /** + * @param CachePeer $master + * @return \SequentialCache + */ + public function setMaster(CachePeer $master) + { + $this->master = $master; + $this->list = $this->slaves; + array_unshift($this->list, $this->master); + + return $this; + } + + /** + * @param CachePeer $master + * @return \SequentialCache + */ + public function addPeer(CachePeer $peer) + { + $this->list[] = $peer; + $this->slaves[] = $peer; + + return $this; + } + + public function get($key) + { + foreach ($this->list as $val) { + /* @var $val CachePeer */ + $result = $val->get($key); + + if ( + !empty($result) + || $val->isAlive() + ) { + return $result; + } + } + + throw new RuntimeException('All peers are dead'); + } + + public function append($key, $data) + { + return $this->foreachItem(__METHOD__, func_get_args()); + } + + public function decrement($key, $value) + { + throw new UnsupportedMethodException('decrement is not supported'); + } + + public function delete($key) + { + return $this->foreachItem(__METHOD__, func_get_args()); + } + + public function increment($key, $value) + { + throw new UnsupportedMethodException('increment is not supported'); + } + + protected function store($action, $key, $value, $expires = Cache::EXPIRES_MEDIUM) + { + return $this->foreachItem(__METHOD__, func_get_args()); + } + + private function foreachItem($method, array $args) + { + $result = true; + + foreach ($this->list as $peer) { + /* @var $peer CachePeer */ + $result = call_user_func_array(array($peer, $method), $args) && $result; + } + + return $result; + } + } diff --git a/doc/ChangeLog b/doc/ChangeLog index c991ecbb11..0644cc5f20 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,8 +1,17 @@ +2012-04-26 Artem A. Naumenko, Evgeny V. Kokovikhin + + * core/Base/Assert.class.php, core/Cache/PeclMemcached.class.php, + main/Monitoring/PinbedPeclMemcached.class.php, test/main/Utils/PinbaTest.class.php, + core/Cache/SequentialCache.class.php, + test/core/SequentialCacheTest.class.php: + Assert::isObject added, lazy ability to PeclMemcached, Pinba's tests tune, + Addd SequentialCache with tests. + 2012-04-22 Georgiy T. Kutsurua * core/OSQL/DBColumn.class.php: Fix DBColumn -2012-04-20 Timofey A. Anisimov +2012-04-20 Timofey A. Anisimov * test/main/OsqlSelectTest.class.php, core/SQLFullOuterJoin.class.php, diff --git a/main/Monitoring/PinbedPeclMemcached.class.php b/main/Monitoring/PinbedPeclMemcached.class.php index c1df435384..2d5a81890d 100644 --- a/main/Monitoring/PinbedPeclMemcached.class.php +++ b/main/Monitoring/PinbedPeclMemcached.class.php @@ -22,7 +22,8 @@ final class PinbedPeclMemcached extends PeclMemcached **/ public static function create( $host = Memcached::DEFAULT_HOST, - $port = Memcached::DEFAULT_PORT + $port = Memcached::DEFAULT_PORT, + $connectTimeout = PeclMemcached::DEFAULT_TIMEOUT ) { return new self($host, $port); @@ -30,7 +31,8 @@ public static function create( public function __construct( $host = Memcached::DEFAULT_HOST, - $port = Memcached::DEFAULT_PORT + $port = Memcached::DEFAULT_PORT, + $connectTimeout = PeclMemcached::DEFAULT_TIMEOUT ) { $this->host = $host; @@ -42,7 +44,7 @@ public function __construct( array('pecl_memcached_connect' => $host.'_'.$port) ); - parent::__construct($host, $port); + parent::__construct($host, $port, $connectTimeout); if (PinbaClient::isEnabled()) PinbaClient::me()->timerStop( diff --git a/test/core/SequentialCacheTest.class.php b/test/core/SequentialCacheTest.class.php new file mode 100644 index 0000000000..531ff17cd1 --- /dev/null +++ b/test/core/SequentialCacheTest.class.php @@ -0,0 +1,80 @@ +set('some_key', 'some_value'); + + $deadPeer = new Memcached("165.42.42.42", "11211"); //some not existing memcache + + $slave1 = new PeclMemcached("35.143.65.241", "11211"); //some not existing memcache + + $slave2 = + AggregateCache::create()-> + addPeer('dead', new PeclMemcached("165.34.176.221", "11211"))-> //some not existing memcache + addPeer('dead_too', new PeclMemcached("165.34.176.222", "11211")); //some not existing memcache + + $cache = new SequentialCache($deadPeer, array($slave1, $slave2, $alifePeer)); + + $result = $cache->get("some_key"); + + $this->assertEquals($result, 'some_value'); + } + + public function testMultiCacheAliveFirst() + { + $alifePeer = new Memcached("127.0.0.1", "11211"); //some existing memcached + $alifePeer->set('some_key', 'some_value'); + + $slave1 = new PeclMemcached("35.143.65.241", "11211"); //some not existing memcache + + $slave2 = new PeclMemcached("165.34.176.221", "11211"); //some not existing memcache + + $cache = new SequentialCache($alifePeer, array($slave1, $slave1, $slave2)); + + $result = $cache->get("some_key"); + + $this->assertEquals($result, 'some_value'); + } + + public function testMultiCacheAliveOnly() + { + $alifePeer = + CyclicAggregateCache::create()-> //some existing memcached + setSummaryWeight(42)-> + addPeer('first', new PeclMemcached("127.0.0.1", "11211"), 0)-> + addPeer('second', new Memcached("127.0.0.1", "11211"), 21); + + $alifePeer->set('some_key', 'some_value'); + + $cache = new SequentialCache($alifePeer); + + $result = $cache->get("some_key"); + + $this->assertEquals($result, 'some_value'); + } + + /** + * @expectedException RuntimeException + */ + public function testMultiCacheNoAlive() + { + $dead1 = new PeclMemcached("35.143.65.241", "11211", 0.01); //some not existing memcache + $dead2 = new PeclMemcached("165.34.176.221", "11211", 0.01); //some not existing memcache + + $cache = new SequentialCache($dead1, array($dead2)); + + $result = $cache->get("some_key"); //will throw RuntimeException + } + } diff --git a/test/main/Utils/PinbaTest.class.php b/test/main/Utils/PinbaTest.class.php index 87995dfa7c..73d9c42653 100644 --- a/test/main/Utils/PinbaTest.class.php +++ b/test/main/Utils/PinbaTest.class.php @@ -2,28 +2,22 @@ final class PinbaTest extends TestCase { - protected function setUp() + protected static $skipMessage = 'unknown error'; + protected static $skipped = false; + + public static function setUpBeforeClass() { if (!extension_loaded('pinba')) - $this->markTestSkipped( - 'The pinba extension is not available.' - ); + return self::skip('The pinba extension is not available.'); if (!PinbaClient::isEnabled()) - $this->markTestSkipped( - 'The pinba is not enabled at php.ini (pinba.enabled=1).' - ); - - if (!extension_loaded('runkit')) { - $this->markTestSkipped( - 'The runkit extension is not available.' - ); - } + return self::skip('The pinba is not enabled at php.ini (pinba.enabled=1).'); + + if (!extension_loaded('runkit')) + return self::skip('The runkit extension is not available.'); if (!ini_get('runkit.internal_override')) - $this->markTestSkipped( - 'The runkit.internal_override is not enabled (enabled it at php.ini).' - ); + return self::skip('The runkit.internal_override is not enabled (enabled it at php.ini).'); runkit_function_rename('pinba_timer_start', 'pinba_timer_start_bak'); runkit_function_rename('pinba_timer_stop', 'pinba_timer_stop_bak'); @@ -32,12 +26,9 @@ protected function setUp() runkit_function_rename('pinba_timer_stop_callback', 'pinba_timer_stop'); } - public static function tearDownAfterClass(){ - - if ( - !extension_loaded('runkit') - || !ini_get('runkit.internal_override') - ) + public static function tearDownAfterClass() + { + if (self::$skipped) return; runkit_function_rename('pinba_timer_start', 'pinba_timer_start_callback'); @@ -47,6 +38,13 @@ public static function tearDownAfterClass(){ runkit_function_rename('pinba_timer_stop_bak', 'pinba_timer_stop'); } + public function setUp() + { + if (self::$skipped) { + $this->markTestSkipped(self::$skipMessage); + } + } + public function testTreeLog() { PinbaClient::me()->setTreeLogEnabled(); @@ -55,14 +53,14 @@ public function testTreeLog() PinbaClient::me()->timerStart( 'test', - array("test" => "main") + array("test" => 'main') ); $this->assertEquals(count(PinbaClient::me()->getTreeQueue()), 1); PinbaClient::me()->timerStart( 'subtest', - array("test" => "submain") + array("test" => 'submain') ); $this->assertEquals(count(PinbaClient::me()->getTreeQueue()), 2); @@ -76,6 +74,12 @@ public function testTreeLog() $this->assertEquals(count(PinbaClient::me()->getTreeQueue()), 0); } + + protected static function skip($message) + { + self::$skipMessage = $message; + self::$skipped = true; + } } final class RunkitCallback @@ -83,14 +87,17 @@ final class RunkitCallback public static $queue = array(); public static $log = array(); - public static function start($tags, $data = array()) + public static function start($tags, array $data = array()) { self::$log[] = $tags; end(self::$log); - if (!empty($tags['treeParentId']) && $tags['treeParentId'] != "root") { + if ( + !empty($tags['treeParentId']) + && $tags['treeParentId'] != "root" + ) { if ($tags['treeParentId'] != end(self::$queue)) { - throw new Exception("Error generatin tree"); + throw new Exception('Error generatin tree'); } } @@ -107,7 +114,7 @@ public static function stop($id) $tree_id = $current['treeId']; if (end(self::$queue) != $tree_id) { - throw new Exception("Error generatin tree"); + throw new Exception('Error generatin tree'); } array_pop(self::$queue); @@ -115,11 +122,12 @@ public static function stop($id) } } - function pinba_timer_start_callback ($tags, $data = array()) { + function pinba_timer_start_callback ($tags, array $data = array()) + { return RunkitCallback::start($tags, $data); } - function pinba_timer_stop_callback($id){ + function pinba_timer_stop_callback($id) + { return RunkitCallback::stop($id); } -?> \ No newline at end of file