From 1714aa6abd6eb4163a108338245094d2bc2b929d Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Dec 2016 18:44:10 +0100 Subject: [PATCH 1/7] Added traceable adapter --- .../Cache/Adapter/TraceableAdapter.php | 206 ++++++++++++++++++ .../Tests/Adapter/TraceableAdapterTest.php | 201 +++++++++++++++++ 2 files changed, 407 insertions(+) create mode 100644 src/Symfony/Component/Cache/Adapter/TraceableAdapter.php create mode 100644 src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php new file mode 100644 index 0000000000000..c4ad188d8a3ec --- /dev/null +++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php @@ -0,0 +1,206 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Psr\Cache\CacheItemInterface; + +/** + * An adapter that collects data about all cache calls. + * + * @author Aaron Scherer + * @author Tobias Nyholm + * @author Nicolas Grekas + */ +class TraceableAdapter implements AdapterInterface +{ + private $pool; + private $calls = array(); + + public function __construct(AdapterInterface $pool) + { + $this->pool = $pool; + } + + /** + * {@inheritdoc} + */ + public function getItem($key) + { + $event = $this->start(__FUNCTION__, $key); + try { + $item = $this->pool->getItem($key); + } finally { + $event->end = microtime(true); + } + if ($item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + $event->result = $item->get(); + + return $item; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + $event = $this->start(__FUNCTION__, $key); + try { + $event->result = $this->pool->hasItem($key); + } finally { + $event->end = microtime(true); + } + + if (!$event->result) { + ++$event->misses; + } + + return $event->result; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + $event = $this->start(__FUNCTION__, $key); + try { + return $event->result = $this->pool->deleteItem($key); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function save(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__, $item); + try { + return $event->result = $this->pool->save($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function saveDeferred(CacheItemInterface $item) + { + $event = $this->start(__FUNCTION__, $item); + try { + return $event->result = $this->pool->saveDeferred($item); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function getItems(array $keys = array()) + { + $event = $this->start(__FUNCTION__, $keys); + try { + $result = $this->pool->getItems($keys); + } finally { + $event->end = microtime(true); + } + $f = function () use ($result, $event) { + $event->result = array(); + foreach ($result as $key => $item) { + if ($item->isHit()) { + ++$event->hits; + } else { + ++$event->misses; + } + $event->result[$key] = $item->get(); + yield $key => $item; + } + }; + + return $f(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->clear(); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function deleteItems(array $keys) + { + $event = $this->start(__FUNCTION__, $keys); + try { + return $event->result = $this->pool->deleteItems($keys); + } finally { + $event->end = microtime(true); + } + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $event = $this->start(__FUNCTION__); + try { + return $event->result = $this->pool->commit(); + } finally { + $event->end = microtime(true); + } + } + + public function getCalls() + { + return $this->calls; + } + + private function start($name, $argument = null) + { + $this->calls[] = $event = new TraceableAdapterEvent(); + $event->name = $name; + $event->argument = $argument; + $event->start = microtime(true); + + return $event; + } +} + +/** + * @internal + */ +class TraceableAdapterEvent +{ + public $name; + public $argument; + public $start; + public $end; + public $result; + public $hits = 0; + public $misses = 0; +} diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php new file mode 100644 index 0000000000000..b8130e863a562 --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php @@ -0,0 +1,201 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Adapter\TraceableAdapter; + +/** + * @group time-sensitive + */ +class TraceableAdapterTest extends AdapterTestCase +{ + private static $redis; + + public static function setupBeforeClass() + { + parent::setupBeforeClass(); + self::$redis = new \Redis(); + self::$redis->connect(getenv('REDIS_HOST')); + } + + public function createCachePool($defaultLifetime = 0) + { + return new TraceableAdapter(new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime)); + } + + public function testGetItemMiss() + { + $pool = $this->createCachePool(); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('getItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(1, $call->misses); + $this->assertNull($call->result); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testGetItemHit() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->getItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertEquals(1, $call->hits); + $this->assertEquals(0, $call->misses); + } + + public function testGetItemsMiss() + { + $pool = $this->createCachePool(); + $arg = array('k0', 'k1'); + $items = $pool->getItems($arg); + foreach ($items as $item) { + } + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('getItems', $call->name); + $this->assertEquals($arg, $call->argument); + $this->assertEquals(2, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemMiss() + { + $pool = $this->createCachePool(); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('hasItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(1, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testHasItemHit() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $pool->hasItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(3, $calls); + + $call = $calls[2]; + $this->assertEquals('hasItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertEquals(1, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItem() + { + $pool = $this->createCachePool(); + $pool->deleteItem('k'); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('deleteItem', $call->name); + $this->assertEquals('k', $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testDeleteItems() + { + $pool = $this->createCachePool(); + $arg = array('k0', 'k1'); + $pool->deleteItems($arg); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('deleteItems', $call->name); + $this->assertEquals($arg, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSave() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->save($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertEquals('save', $call->name); + $this->assertEquals($item, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testSaveDeferred() + { + $pool = $this->createCachePool(); + $item = $pool->getItem('k')->set('foo'); + $pool->saveDeferred($item); + $calls = $pool->getCalls(); + $this->assertCount(2, $calls); + + $call = $calls[1]; + $this->assertEquals('saveDeferred', $call->name); + $this->assertEquals($item, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } + + public function testCommit() + { + $pool = $this->createCachePool(); + $pool->commit(); + $calls = $pool->getCalls(); + $this->assertCount(1, $calls); + + $call = $calls[0]; + $this->assertEquals('commit', $call->name); + $this->assertNull(null, $call->argument); + $this->assertEquals(0, $call->hits); + $this->assertEquals(0, $call->misses); + $this->assertNotEmpty($call->start); + $this->assertNotEmpty($call->end); + } +} From 7ba9b1ce632fdd110fe08d2f3dc44a25854537ea Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Dec 2016 18:45:29 +0100 Subject: [PATCH 2/7] Bugfix --- src/Symfony/Component/Cache/Adapter/TraceableAdapter.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php index c4ad188d8a3ec..8f2d17dc24d39 100644 --- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php @@ -63,7 +63,9 @@ public function hasItem($key) $event->end = microtime(true); } - if (!$event->result) { + if ($event->result) { + ++$event->hits; + } else { ++$event->misses; } From 2f86f679906bf8685caa7bcf44309b701fcec896 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Dec 2016 20:57:09 +0100 Subject: [PATCH 3/7] Using filesystem cache --- .../Cache/Tests/Adapter/TraceableAdapterTest.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php index b8130e863a562..66e699391acdb 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php @@ -11,7 +11,7 @@ namespace Symfony\Component\Cache\Tests\Adapter; -use Symfony\Component\Cache\Adapter\RedisAdapter; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Symfony\Component\Cache\Adapter\TraceableAdapter; /** @@ -19,18 +19,9 @@ */ class TraceableAdapterTest extends AdapterTestCase { - private static $redis; - - public static function setupBeforeClass() - { - parent::setupBeforeClass(); - self::$redis = new \Redis(); - self::$redis->connect(getenv('REDIS_HOST')); - } - public function createCachePool($defaultLifetime = 0) { - return new TraceableAdapter(new RedisAdapter(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime)); + return new TraceableAdapter(new FilesystemAdapter('', $defaultLifetime)); } public function testGetItemMiss() From 1cac2554917efed17b7f3df1f3675da81142adc3 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Dec 2016 21:21:07 +0100 Subject: [PATCH 4/7] Remove hasItem code --- .../Component/Cache/Adapter/TraceableAdapter.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php index 8f2d17dc24d39..39644a20c4894 100644 --- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php @@ -58,18 +58,10 @@ public function hasItem($key) { $event = $this->start(__FUNCTION__, $key); try { - $event->result = $this->pool->hasItem($key); + return $event->result = $this->pool->hasItem($key); } finally { $event->end = microtime(true); } - - if ($event->result) { - ++$event->hits; - } else { - ++$event->misses; - } - - return $event->result; } /** From 19c401da6ad8f744179d12dc0fe9ebeb69bbd2c7 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Wed, 28 Dec 2016 21:34:35 +0100 Subject: [PATCH 5/7] Updated tests --- .../Component/Cache/Tests/Adapter/TraceableAdapterTest.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php index 66e699391acdb..ad55218b0d07a 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/TraceableAdapterTest.php @@ -83,8 +83,7 @@ public function testHasItemMiss() $call = $calls[0]; $this->assertEquals('hasItem', $call->name); $this->assertEquals('k', $call->argument); - $this->assertEquals(0, $call->hits); - $this->assertEquals(1, $call->misses); + $this->assertFalse($call->result); $this->assertNotEmpty($call->start); $this->assertNotEmpty($call->end); } @@ -101,8 +100,7 @@ public function testHasItemHit() $call = $calls[2]; $this->assertEquals('hasItem', $call->name); $this->assertEquals('k', $call->argument); - $this->assertEquals(1, $call->hits); - $this->assertEquals(0, $call->misses); + $this->assertTrue($call->result); $this->assertNotEmpty($call->start); $this->assertNotEmpty($call->end); } From 457c8f76ea27e20369307fc162dfcb84a32c9600 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Thu, 29 Dec 2016 00:17:10 +0100 Subject: [PATCH 6/7] Update TraceableAdapter.php --- src/Symfony/Component/Cache/Adapter/TraceableAdapter.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php index 39644a20c4894..832db7818afeb 100644 --- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php @@ -185,9 +185,6 @@ private function start($name, $argument = null) } } -/** - * @internal - */ class TraceableAdapterEvent { public $name; From be9726a3e49dafac87a44bb340064ff8266ad6f0 Mon Sep 17 00:00:00 2001 From: Tobias Nyholm Date: Thu, 29 Dec 2016 10:42:58 +0100 Subject: [PATCH 7/7] Reset $calls after getCalls() --- src/Symfony/Component/Cache/Adapter/TraceableAdapter.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php index 832db7818afeb..84ecec3dd1961 100644 --- a/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/TraceableAdapter.php @@ -171,7 +171,11 @@ public function commit() public function getCalls() { - return $this->calls; + try { + return $this->calls; + } finally { + $this->calls = array(); + } } private function start($name, $argument = null)