From e66ddfb7f9c1515f53287b52ab91c7c00ab97559 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Fri, 9 Dec 2016 15:49:07 +0200 Subject: [PATCH 01/24] Factored-out the state management in designated classes. --- src/APCuL1.php | 72 +++-------------------------- src/L1.php | 44 ++++++++++++++++-- src/SQLiteL1.php | 78 ++------------------------------ src/StateL1APCu.php | 97 ++++++++++++++++++++++++++++++++++++++++ src/StateL1Interface.php | 30 +++++++++++++ src/StateL1Static.php | 63 ++++++++++++++++++++++++++ src/StaticL1.php | 39 +++------------- 7 files changed, 248 insertions(+), 175 deletions(-) create mode 100644 src/StateL1APCu.php create mode 100644 src/StateL1Interface.php create mode 100644 src/StateL1Static.php diff --git a/src/APCuL1.php b/src/APCuL1.php index cd49267..3e0525f 100644 --- a/src/APCuL1.php +++ b/src/APCuL1.php @@ -7,24 +7,17 @@ class APCuL1 extends L1 /** @var string */ private $localKeyPrefix; - /** @var string */ - private $statusKeyHits; - - /** @var string */ - private $statusKeyMisses; - - /** @var string */ - private $statusKeyLastAppliedEventId; - public function __construct($pool = null) { parent::__construct($pool); + // TODO: consifer injecting this... + if (!($this->state instanceof StateL1APCu)) { + $this->state = new StateL1APCu($this->pool); + } + // Using designated variables to speed up key generation during runtime. - $this->localKeyPrefix = 'lcache' . $this->pool . ':'; - $this->statusKeyHits = 'lcache_status:' . $this->pool . ':hits'; - $this->statusKeyMisses = 'lcache_status:' . $this->pool . ':misses'; - $this->statusKeyLastAppliedEventId = 'lcache_status:' . $this->pool . ':last_applied_event_id'; + $this->localKeyPrefix = 'lcache:' . $this->pool . ':'; } protected function getLocalKey($address) @@ -126,6 +119,7 @@ protected function getIterator($pattern, $format = APC_ITER_ALL) public function delete($event_id, Address $address) { if ($address->isEntireCache()) { + $this->state->clear(); // @TODO: Consider flushing only LCache L1 storage by using an iterator. return apcu_clear_cache(); } @@ -149,56 +143,4 @@ public function delete($event_id, Address $address) apcu_delete($apcu_key); return true; } - - protected function recordHit() - { - apcu_inc($this->statusKeyHits, 1, $success); - if (!$success) { - // @TODO: Remove this fallback when we drop APCu 4.x support. - // @codeCoverageIgnoreStart - // Ignore coverage because (1) it's tested with other code and - // (2) APCu 5.x does not use it. - apcu_store($this->statusKeyHits, 1); - // @codeCoverageIgnoreEnd - } - } - - protected function recordMiss() - { - apcu_inc($this->statusKeyMisses, 1, $success); - if (!$success) { - // @TODO: Remove this fallback when we drop APCu 4.x support. - // @codeCoverageIgnoreStart - // Ignore coverage because (1) it's tested with other code and - // (2) APCu 5.x does not use it. - apcu_store($this->statusKeyMisses, 1); - // @codeCoverageIgnoreEnd - } - } - - public function getHits() - { - $value = apcu_fetch($this->statusKeyHits); - return $value ? $value : 0; - } - - public function getMisses() - { - $value = apcu_fetch($this->statusKeyMisses); - return $value ? $value : 0; - } - - public function getLastAppliedEventID() - { - $value = apcu_fetch($this->statusKeyLastAppliedEventId); - if ($value === false) { - $value = null; - } - return $value; - } - - public function setLastAppliedEventID($eid) - { - return apcu_store($this->statusKeyLastAppliedEventId, $eid); - } } diff --git a/src/L1.php b/src/L1.php index e856b19..e017b78 100644 --- a/src/L1.php +++ b/src/L1.php @@ -6,7 +6,10 @@ abstract class L1 extends LX { protected $pool; - public function __construct($pool = null) + /** @var StateL1Interface */ + protected $state; + + public function __construct($pool = null, StateL1Interface $state = null) { if (!is_null($pool)) { $this->pool = $pool; @@ -15,6 +18,14 @@ public function __construct($pool = null) } else { $this->pool = $this->generateUniqueID(); } + + if (!is_null($state)) { + $this->state = $state; + } elseif (function_exists('apcu_fetch')) { + $this->state = new StateL1APCu($this->pool); + } else { + $this->state = new StateL1Static(); + } } protected function generateUniqueID() @@ -23,8 +34,15 @@ protected function generateUniqueID() return uniqid('', true) . '-' . mt_rand(); } - abstract public function getLastAppliedEventID(); - abstract public function setLastAppliedEventID($event_id); + public function getLastAppliedEventID() + { + return $this->state->getLastAppliedEventID(); + } + + public function setLastAppliedEventID($event_id) + { + return $this->state->setLastAppliedEventID($event_id); + } public function getPool() { @@ -40,4 +58,24 @@ abstract public function isNegativeCache(Address $address); abstract public function getKeyOverhead(Address $address); abstract public function setWithExpiration($event_id, Address $address, $value, $created, $expiration = null); abstract public function delete($event_id, Address $address); + + public function getHits() + { + return $this->state->getHits(); + } + + public function getMisses() + { + return $this->state->getMisses(); + } + + protected function recordHit() + { + $this->state->recordHit(); + } + + protected function recordMiss() + { + $this->state->recordMiss(); + } } diff --git a/src/SQLiteL1.php b/src/SQLiteL1.php index 0ce4ee0..1bc4933 100644 --- a/src/SQLiteL1.php +++ b/src/SQLiteL1.php @@ -7,15 +7,6 @@ class SQLiteL1 extends L1 /** @var \PDO */ private $dbh; - /** @var string */ - private $statusKeyHits; - - /** @var string */ - private $statusKeyMisses; - - /** @var string */ - private $statusKeyLastAppliedEventId; - protected static function tableExists(\PDO $dbh, $table_name) { try { @@ -56,9 +47,8 @@ public function __construct($pool = null) parent::__construct($pool); $this->dbh = self::getDatabaseHandle($this->pool); - $this->statusKeyHits = 'lcache_status:' . $this->pool . ':hits'; - $this->statusKeyMisses = 'lcache_status:' . $this->pool . ':misses'; - $this->statusKeyLastAppliedEventId = 'lcache_status:' . $this->pool . ':last_applied_event_id'; + // TODO: Sniff-out APCu presence and use sqlite or null implementation. + $this->state = new StateL1APCu($this->pool); } protected function pruneExpiredEntries() @@ -222,8 +212,8 @@ public function delete($event_id, Address $address) $sth = $this->dbh->prepare('DELETE FROM entries WHERE "address" LIKE :pattern'); $sth->bindValue('pattern', $pattern, \PDO::PARAM_STR); $sth->execute(); - apcu_store($this->statusKeyHits, 0); - apcu_store($this->statusKeyMisses, 0); + + $this->state->clear(); return true; } @@ -233,64 +223,4 @@ public function delete($event_id, Address $address) $sth->execute(); return true; } - - // @TODO: Update hit/miss functions to either record nothing or fall back - // to SQLite storage if APCu is not available. - - protected function recordHit() - { - $success = false; // Make Scrutinizer happy. - apcu_inc($this->statusKeyHits, 1, $success); - if (!$success) { - // @TODO: Remove this fallback when we drop APCu 4.x support. - // @codeCoverageIgnoreStart - // Ignore coverage because (1) it's tested with other code and - // (2) APCu 5.x does not use it. - apcu_store($this->statusKeyHits, 1); - // @codeCoverageIgnoreEnd - } - } - - protected function recordMiss() - { - $success = false; // Make Scrutinizer happy. - apcu_inc($this->statusKeyMisses, 1, $success); - if (!$success) { - // @TODO: Remove this fallback when we drop APCu 4.x support. - // @codeCoverageIgnoreStart - // Ignore coverage because (1) it's tested with other code and - // (2) APCu 5.x does not use it. - apcu_store($this->statusKeyMisses, 1); - // @codeCoverageIgnoreEnd - } - } - - public function getHits() - { - $value = apcu_fetch($this->statusKeyHits); - return $value ? $value : 0; - } - - public function getMisses() - { - $value = apcu_fetch($this->statusKeyMisses); - return $value ? $value : 0; - } - - // @TODO: Update event ID tracking to either record fall back - // to SQLite storage if APCu is not available. - - public function getLastAppliedEventID() - { - $value = apcu_fetch($this->statusKeyLastAppliedEventId); - if ($value === false) { - $value = null; - } - return $value; - } - - public function setLastAppliedEventID($eid) - { - return apcu_store($this->statusKeyLastAppliedEventId, $eid); - } } diff --git a/src/StateL1APCu.php b/src/StateL1APCu.php new file mode 100644 index 0000000..69a2ad9 --- /dev/null +++ b/src/StateL1APCu.php @@ -0,0 +1,97 @@ +pool = $pool; + + // Using designated variables to speed up key generation during runtime. + $this->statusKeyHits = 'lcache_status:' . $this->pool . ':hits'; + $this->statusKeyMisses = 'lcache_status:' . $this->pool . ':misses'; + $this->statusKeyLastAppliedEventId = 'lcache_status:' . $this->pool . ':last_applied_event_id'; + } + + public function recordHit() + { + apcu_inc($this->statusKeyHits, 1, $success); + if (!$success) { + // @TODO: Remove this fallback when we drop APCu 4.x support. + // @codeCoverageIgnoreStart + // Ignore coverage because (1) it's tested with other code and + // (2) APCu 5.x does not use it. + apcu_store($this->statusKeyHits, 1); + // @codeCoverageIgnoreEnd + } + } + + public function recordMiss() + { + apcu_inc($this->statusKeyMisses, 1, $success); + if (!$success) { + // @TODO: Remove this fallback when we drop APCu 4.x support. + // @codeCoverageIgnoreStart + // Ignore coverage because (1) it's tested with other code and + // (2) APCu 5.x does not use it. + apcu_store($this->statusKeyMisses, 1); + // @codeCoverageIgnoreEnd + } + } + + public function getHits() + { + $value = apcu_fetch($this->statusKeyHits); + return $value ? $value : 0; + } + + public function getMisses() + { + $value = apcu_fetch($this->statusKeyMisses); + return $value ? $value : 0; + } + + public function getLastAppliedEventID() + { + $value = apcu_fetch($this->statusKeyLastAppliedEventId); + if ($value === false) { + $value = null; + } + return $value; + } + + public function setLastAppliedEventID($eventId) + { + return apcu_store($this->statusKeyLastAppliedEventId, $eventId); + } + + public function clear() + { + apcu_store($this->statusKeyHits, 0); + apcu_store($this->statusKeyMisses, 0); + // TODO: Decide on how to handle the last applied event state on clear? + } +} diff --git a/src/StateL1Interface.php b/src/StateL1Interface.php new file mode 100644 index 0000000..02797a0 --- /dev/null +++ b/src/StateL1Interface.php @@ -0,0 +1,30 @@ +last_applied_event_id = null; + $this->clear(); + } + + public function recordHit() + { + $this->hits++; + } + + public function recordMiss() + { + $this->misses++; + } + + public function getHits() + { + return $this->hits; + } + + public function getMisses() + { + return $this->misses; + } + + public function getLastAppliedEventID() + { + return $this->last_applied_event_id; + } + + public function setLastAppliedEventID($eventId) + { + $this->last_applied_event_id = $eventId; + return true; + } + + public function clear() + { + $this->hits = $this->misses = 0; + } +} diff --git a/src/StaticL1.php b/src/StaticL1.php index a7a2470..0d866e9 100644 --- a/src/StaticL1.php +++ b/src/StaticL1.php @@ -4,21 +4,15 @@ class StaticL1 extends L1 { - protected $hits; - protected $misses; protected $key_overhead; protected $storage; - protected $last_applied_event_id; public function __construct($pool = null) { - parent::__construct($pool); + parent::__construct($pool, new StateL1Static()); - $this->hits = 0; - $this->misses = 0; $this->key_overhead = []; $this->storage = array(); - $this->last_applied_event_id = null; } public function getKeyOverhead(Address $address) @@ -70,17 +64,18 @@ public function getEntry(Address $address) } if (!array_key_exists($local_key, $this->storage)) { - $this->misses++; + $this->recordMiss(); return null; } $entry = $this->storage[$local_key]; if (!is_null($entry->expiration) && $entry->expiration < $_SERVER['REQUEST_TIME']) { unset($this->storage[$local_key]); - $this->misses++; + $this->recordMiss(); return null; } - $this->hits++; + $this->recordHit(); + return $entry; } @@ -89,8 +84,7 @@ public function delete($event_id, Address $address) $local_key = $address->serialize(); if ($address->isEntireCache()) { $this->storage = array(); - $this->hits = 0; - $this->misses = 0; + $this->state->clear(); return true; } elseif ($address->isEntireBin()) { foreach ($this->storage as $index => $value) { @@ -105,25 +99,4 @@ public function delete($event_id, Address $address) unset($this->storage[$local_key]); return true; } - - public function getHits() - { - return $this->hits; - } - - public function getMisses() - { - return $this->misses; - } - - public function getLastAppliedEventID() - { - return $this->last_applied_event_id; - } - - public function setLastAppliedEventID($eid) - { - $this->last_applied_event_id = $eid; - return true; - } } From 16def5434387c635d0042284fd2e4796874cc07e Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Fri, 9 Dec 2016 16:33:12 +0200 Subject: [PATCH 02/24] Removed excessive logic in L1 constructor. Added hard dependecies between L1 cache implementations and their respective state handlers. --- src/APCuL1.php | 6 ++---- src/L1.php | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/APCuL1.php b/src/APCuL1.php index 3e0525f..863f1cf 100644 --- a/src/APCuL1.php +++ b/src/APCuL1.php @@ -11,10 +11,8 @@ public function __construct($pool = null) { parent::__construct($pool); - // TODO: consifer injecting this... - if (!($this->state instanceof StateL1APCu)) { - $this->state = new StateL1APCu($this->pool); - } + // TODO: Consifer injecting this. + $this->state = new StateL1APCu($this->pool); // Using designated variables to speed up key generation during runtime. $this->localKeyPrefix = 'lcache:' . $this->pool . ':'; diff --git a/src/L1.php b/src/L1.php index e017b78..d67df9e 100644 --- a/src/L1.php +++ b/src/L1.php @@ -21,10 +21,6 @@ public function __construct($pool = null, StateL1Interface $state = null) if (!is_null($state)) { $this->state = $state; - } elseif (function_exists('apcu_fetch')) { - $this->state = new StateL1APCu($this->pool); - } else { - $this->state = new StateL1Static(); } } From d513eaa6a823f9de4ac80b17c3881dd0c438acca Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Sun, 11 Dec 2016 20:55:30 +0200 Subject: [PATCH 03/24] Added docks v1. Added null state manager implementation. --- src/StateL1APCu.php | 69 +++++++++++++++++++++++++++--------- src/StateL1Interface.php | 52 ++++++++++++++++++++++++---- src/StateL1Null.php | 75 ++++++++++++++++++++++++++++++++++++++++ src/StateL1Static.php | 36 ++++++++++++++++--- 4 files changed, 205 insertions(+), 27 deletions(-) create mode 100644 src/StateL1Null.php diff --git a/src/StateL1APCu.php b/src/StateL1APCu.php index 69a2ad9..1771c06 100644 --- a/src/StateL1APCu.php +++ b/src/StateL1APCu.php @@ -1,20 +1,20 @@ pool = $pool; @@ -36,44 +42,67 @@ public function __construct($pool) $this->statusKeyLastAppliedEventId = 'lcache_status:' . $this->pool . ':last_applied_event_id'; } + /** + * {inheritdoc} + */ public function recordHit() { - apcu_inc($this->statusKeyHits, 1, $success); - if (!$success) { - // @TODO: Remove this fallback when we drop APCu 4.x support. - // @codeCoverageIgnoreStart - // Ignore coverage because (1) it's tested with other code and - // (2) APCu 5.x does not use it. - apcu_store($this->statusKeyHits, 1); - // @codeCoverageIgnoreEnd - } + return $this->_recordEvent($this->statusKeyHits); } + /** + * {inheritdoc} + */ public function recordMiss() { - apcu_inc($this->statusKeyMisses, 1, $success); - if (!$success) { + return $this->_recordEvent($this->statusKeyMisses); + } + + /** + * Utility method to reduce code duplication. + * + * @param string $key + * Key to store the evet counters in. + * + * @return bool + * True on success, false otherwise. + */ + private function _recordEvent($key) + { + $success = null; + apcu_inc($key, 1, $success); + if ($success !== null && !$success) { // @TODO: Remove this fallback when we drop APCu 4.x support. // @codeCoverageIgnoreStart // Ignore coverage because (1) it's tested with other code and // (2) APCu 5.x does not use it. - apcu_store($this->statusKeyMisses, 1); + $success = apcu_store($key, 1); // @codeCoverageIgnoreEnd } + return $success; } + /** + * {inheritdoc} + */ public function getHits() { $value = apcu_fetch($this->statusKeyHits); return $value ? $value : 0; } + /** + * {inheritdoc} + */ public function getMisses() { $value = apcu_fetch($this->statusKeyMisses); return $value ? $value : 0; } + /** + * {inheritdoc} + */ public function getLastAppliedEventID() { $value = apcu_fetch($this->statusKeyLastAppliedEventId); @@ -83,11 +112,17 @@ public function getLastAppliedEventID() return $value; } + /** + * {inheritdoc} + */ public function setLastAppliedEventID($eventId) { return apcu_store($this->statusKeyLastAppliedEventId, $eventId); } + /** + * {inheritdoc} + */ public function clear() { apcu_store($this->statusKeyHits, 0); diff --git a/src/StateL1Interface.php b/src/StateL1Interface.php index 02797a0..be836a6 100644 --- a/src/StateL1Interface.php +++ b/src/StateL1Interface.php @@ -1,30 +1,70 @@ last_applied_event_id = null; $this->clear(); } + /** + * {inheritdoc} + */ public function recordHit() { $this->hits++; } + /** + * {inheritdoc} + */ public function recordMiss() { $this->misses++; } + /** + * {inheritdoc} + */ public function getHits() { return $this->hits; } + /** + * {inheritdoc} + */ public function getMisses() { return $this->misses; } + /** + * {inheritdoc} + */ public function getLastAppliedEventID() { return $this->last_applied_event_id; } + /** + * {inheritdoc} + */ public function setLastAppliedEventID($eventId) { $this->last_applied_event_id = $eventId; return true; } + /** + * {inheritdoc} + */ public function clear() { $this->hits = $this->misses = 0; From 3626134970d35525a27bb2a51e1eeeac5386bc94 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Sun, 11 Dec 2016 22:54:05 +0200 Subject: [PATCH 04/24] Refacrored the tests to use the new L1CacheFactory instead of directly instantiating L1 concrete classes. --- src/APCuL1.php | 9 +- src/L1.php | 24 +++--- src/L1CacheFactory.php | 130 +++++++++++++++++++++++++++++ src/SQLiteL1.php | 9 +- src/StateL1Interface.php | 6 ++ src/StaticL1.php | 4 +- tests/LCacheTest.php | 175 +++++++++++++++++++++++++-------------- 7 files changed, 271 insertions(+), 86 deletions(-) create mode 100644 src/L1CacheFactory.php diff --git a/src/APCuL1.php b/src/APCuL1.php index 863f1cf..bff6147 100644 --- a/src/APCuL1.php +++ b/src/APCuL1.php @@ -7,15 +7,12 @@ class APCuL1 extends L1 /** @var string */ private $localKeyPrefix; - public function __construct($pool = null) + public function __construct($pool, StateL1Interface $state) { - parent::__construct($pool); - - // TODO: Consifer injecting this. - $this->state = new StateL1APCu($this->pool); + parent::__construct($pool, $state); // Using designated variables to speed up key generation during runtime. - $this->localKeyPrefix = 'lcache:' . $this->pool . ':'; + $this->localKeyPrefix = 'lcache:' . $pool . ':'; } protected function getLocalKey($address) diff --git a/src/L1.php b/src/L1.php index d67df9e..5bf6311 100644 --- a/src/L1.php +++ b/src/L1.php @@ -9,19 +9,19 @@ abstract class L1 extends LX /** @var StateL1Interface */ protected $state; - public function __construct($pool = null, StateL1Interface $state = null) + /** + * Constructor for all the L1 implementations. + * + * @param string $pool + * Pool ID to group the cache data in. + * @param \LCache\StateL1Interface $state + * State manager class. Used to collect hit/miss statistics as well as + * the ID of the last cache mutation event. + */ + public function __construct($pool, StateL1Interface $state) { - if (!is_null($pool)) { - $this->pool = $pool; - } elseif (isset($_SERVER['SERVER_ADDR']) && isset($_SERVER['SERVER_PORT'])) { - $this->pool = $_SERVER['SERVER_ADDR'] . '-' . $_SERVER['SERVER_PORT']; - } else { - $this->pool = $this->generateUniqueID(); - } - - if (!is_null($state)) { - $this->state = $state; - } + $this->pool = $pool; + $this->state = $state; } protected function generateUniqueID() diff --git a/src/L1CacheFactory.php b/src/L1CacheFactory.php new file mode 100644 index 0000000..1ff2f6e --- /dev/null +++ b/src/L1CacheFactory.php @@ -0,0 +1,130 @@ +getPool($customPool); + $driver = mb_convert_case($driverName, MB_CASE_LOWER); + + $factoryName = 'create' . $driver; + if (!method_exists($this, $factoryName)) { + // TODO: Decide on better fallback (if needed). + $factoryName = 'createStatic'; + } + + $l1CacheInstance = call_user_func([$this, $factoryName], $pool); + return $l1CacheInstance; + } + + /** + * Factory method for the L1 APCu driver. + * + * @param string $pool + * @return \LCache\APCuL1 + */ + protected function createApcu($pool) + { + return new APCuL1($pool, new StateL1APCu($pool)); + } + + /** + * Factory method for the L1 NULL driver. + * + * @param string $pool + * @return \LCache\NullL1 + */ + protected function createNull($pool) + { + return new NullL1($pool, new StateL1Static($pool)); + } + + /** + * Factory method for the L1 static driver. + * + * @param string $pool + * @return \LCache\StaticL1 + */ + protected function createStatic($pool) + { + return new StaticL1($pool, new StateL1Static($pool)); + } + + /** + * Factory method for the L1 static driver. + * + * @param string $pool + * @return \LCache\SQLiteL1 + */ + protected function createSqlite($pool) + { + $hasApcu = function_exists('apcu_fetch'); + // TODO: Maybe implement StateL1SQLite class instead of NULL one. + $state = $hasApcu ? new StateL1APCu($pool) : new StateL1Null($pool); + $cache = new SQLiteL1($pool, $state); + return $cache; + } + + /** + * Pool generator utility. + * + * @param string $pool + * Custom pool to use. Defaults to NULL. If the default is uesed, it will + * atempt to generate a pool value for use. + * + * @return string + * Pool value based on input and/or environment variables / state. + */ + protected function getPool($pool = null) + { + $result = null; + if (!is_null($pool)) { + $result = (string) $pool; + } elseif (isset($_SERVER['SERVER_ADDR']) && isset($_SERVER['SERVER_PORT'])) { + $result = $_SERVER['SERVER_ADDR'] . '-' . $_SERVER['SERVER_PORT']; + } else { + $result = $this->generateUniqueID(); + } + return $result; + } + + /** + * Pool generation utility. + * + * @see L1CacheFactory::getPool() + * + * @return string + */ + protected function generateUniqueID() + { + // @TODO: Replace with a persistent but machine-local (and unique) method. + return uniqid('', true) . '-' . mt_rand(); + } +} diff --git a/src/SQLiteL1.php b/src/SQLiteL1.php index 1bc4933..ef81d19 100644 --- a/src/SQLiteL1.php +++ b/src/SQLiteL1.php @@ -42,13 +42,10 @@ protected static function getDatabaseHandle($pool) return $dbh; } - public function __construct($pool = null) + public function __construct($pool, StateL1Interface $state) { - parent::__construct($pool); - $this->dbh = self::getDatabaseHandle($this->pool); - - // TODO: Sniff-out APCu presence and use sqlite or null implementation. - $this->state = new StateL1APCu($this->pool); + parent::__construct($pool, $state); + $this->dbh = self::getDatabaseHandle($pool); } protected function pruneExpiredEntries() diff --git a/src/StateL1Interface.php b/src/StateL1Interface.php index be836a6..dd2dd6d 100644 --- a/src/StateL1Interface.php +++ b/src/StateL1Interface.php @@ -10,6 +10,12 @@ /** * Interface for the state manager drivers used in L1 driver implementations. * + * This interface is separated from L1 drivers, due to the need to separate L1 + * storage implementations from L1 statistics storage. It was proven that L1 + * SQLite driver implementation is much slower, unless APCu is used for the + * events data tracking. This pushed the need to separate statistics storage + * from the main storage of L1 data and to allow free combinations between them. + * * @author ndobromirov */ interface StateL1Interface diff --git a/src/StaticL1.php b/src/StaticL1.php index 0d866e9..08d2413 100644 --- a/src/StaticL1.php +++ b/src/StaticL1.php @@ -7,9 +7,9 @@ class StaticL1 extends L1 protected $key_overhead; protected $storage; - public function __construct($pool = null) + public function __construct($pool, StateL1Interface $state) { - parent::__construct($pool, new StateL1Static()); + parent::__construct($pool, $state); $this->key_overhead = []; $this->storage = array(); diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index d2766bd..7453d11 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -8,6 +8,19 @@ class LCacheTest extends \PHPUnit_Extensions_Database_TestCase { protected $dbh = null; + private $_factory; + /** + * + * @return \LCache\L1CacheFactory + */ + protected function l1Factory() + { + if ($this->_factory === NULL) { + $this->_factory = new L1CacheFactory(); + } + return $this->_factory; + } + /** * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection */ @@ -33,7 +46,7 @@ protected function createSchema($prefix = '') public function testNullL1() { $event_id = 1; - $cache = new NullL1(); + $cache = $this->l1Factory()->create('null'); $myaddr = new Address('mybin', 'mykey'); $cache->set($event_id++, $myaddr, 'myvalue'); $entry = $cache->get($myaddr); @@ -100,25 +113,27 @@ protected function performSetGetDeleteTest($l1) public function testStaticL1SetGetDelete() { - $l1 = new StaticL1(); + + $l1 = $this->l1Factory()->create('static'); $this->performSetGetDeleteTest($l1); } public function testSQLiteL1SetGetDelete() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performSetGetDeleteTest($l1); } public function testAPCuL1SetGetDelete() { - $l1 = new APCuL1('setGetDelete'); + $l1 = $this->l1Factory()->create('apcu', 'setGetDelete'); + $this->performSetGetDeleteTest($l1); } public function testStaticL1Antirollback() { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $this->performL1AntirollbackTest($l1); } @@ -139,19 +154,19 @@ public function performL1FullDelete($cache) public function testSQLiteL1FullDelete() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performL1FullDelete($l1); } public function testAPCuL1FullDelete() { - $l1 = new APCuL1('setGetDelete'); + $l1 = $this->l1Factory()->create('apcu', 'setGetDelete'); $this->performL1FullDelete($l1); } public function testStaticL1FullDelete() { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $this->performL1FullDelete($l1); } @@ -170,19 +185,19 @@ public function performL1Expiration($l1) public function testSQLiteL1Expiration() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performL1Expiration($l1); } public function testAPCuL1Expiration() { - $l1 = new APCuL1('expiration'); + $l1 = $this->l1Factory()->create('apcu', 'expiration'); $this->performL1Expiration($l1); } public function testStaticL1Expiration() { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $this->performL1Expiration($l1); } @@ -217,7 +232,7 @@ public function testStaticL2Reread() public function testNewPoolSynchronization() { $central = new StaticL2(); - $pool1 = new Integrated(new StaticL1(), $central); + $pool1 = new Integrated($this->l1Factory()->create('static'), $central); $myaddr = new Address('mybin', 'mykey'); @@ -237,7 +252,7 @@ public function testNewPoolSynchronization() // Add a new pool. Sync should return NULL applied changes but should // bump the last applied event ID. - $pool2 = new Integrated(new StaticL1(), $central); + $pool2 = new Integrated($this->l1Factory()->create('static'), $central); $applied = $pool2->synchronize(); $this->assertNull($applied); $this->assertEquals($pool1->getLastAppliedEventID(), $pool2->getLastAppliedEventID()); @@ -269,19 +284,19 @@ protected function performTombstoneTest($l1) public function testStaticL1Tombstone() { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $this->performTombstoneTest($l1); } public function testAPCuL1Tombstone() { - $l1 = new APCuL1('testAPCuL1Tombstone'); + $l1 = $this->l1Factory()->create('apcu', 'testAPCuL1Tombstone'); $this->performTombstoneTest($l1); } public function testSQLiteL1Tombstone() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performTombstoneTest($l1); } @@ -446,13 +461,13 @@ protected function performTaggedSynchronizationTest($central, $first_l1, $second public function testSynchronizationStatic() { $central = new StaticL2(); - $this->performSynchronizationTest($central, new StaticL1(), new StaticL1()); + $this->performSynchronizationTest($central, $this->l1Factory()->create('static'), $this->l1Factory()->create('static')); } public function testTaggedSynchronizationStatic() { $central = new StaticL2(); - $this->performTaggedSynchronizationTest($central, new StaticL1(), new StaticL1()); + $this->performTaggedSynchronizationTest($central, $this->l1Factory()->create('static'), $this->l1Factory()->create('static')); } public function testSynchronizationAPCu() @@ -472,11 +487,23 @@ public function testSynchronizationAPCu() if ($run_test) { $central = new StaticL2(); - $this->performSynchronizationTest($central, new APCuL1('testSynchronizationAPCu1'), new APCuL1('testSynchronizationAPCu2')); + $this->performSynchronizationTest( + $central, + $this->l1Factory()->create('apcu', 'testSynchronizationAPCu1'), + $this->l1Factory()->create('apcu', 'testSynchronizationAPCu2') + ); // Because of how APCu only offers full cache clears, we test against a static cache for the other L1. - $this->performClearSynchronizationTest($central, new APCuL1('testSynchronizationAPCu1b'), new StaticL1()); - $this->performClearSynchronizationTest($central, new StaticL1(), new APCuL1('testSynchronizationAPCu1c')); + $this->performClearSynchronizationTest( + $central, + $this->l1Factory()->create('apcu', 'testSynchronizationAPCu1b'), + $this->l1Factory()->create('static') + ); + $this->performClearSynchronizationTest( + $central, + $this->l1Factory()->create('static'), + $this->l1Factory()->create('apcu', 'testSynchronizationAPCu1c') + ); } else { $this->markTestSkipped('The APCu extension is not installed, enabled (for the CLI), or functional.'); } @@ -485,33 +512,61 @@ public function testSynchronizationAPCu() public function testSynchronizationSQLiteL1() { $central = new StaticL2(); - $this->performSynchronizationTest($central, new SQLiteL1(), new SQLiteL1()); - - $this->performClearSynchronizationTest($central, new SQLiteL1(), new StaticL1()); - $this->performClearSynchronizationTest($central, new StaticL1(), new SQLiteL1()); - $this->performClearSynchronizationTest($central, new SQLiteL1(), new SQLiteL1()); + $this->performSynchronizationTest( + $central, + $this->l1Factory()->create('sqlite'), + $this->l1Factory()->create('sqlite') + ); + + $this->performClearSynchronizationTest( + $central, + $this->l1Factory()->create('sqlite'), + $this->l1Factory()->create('static') + ); + $this->performClearSynchronizationTest( + $central, + $this->l1Factory()->create('static'), + $this->l1Factory()->create('sqlite') + ); + $this->performClearSynchronizationTest( + $central, + $this->l1Factory()->create('sqlite'), + $this->l1Factory()->create('sqlite') + ); } public function testSynchronizationDatabase() { $this->createSchema(); $central = new DatabaseL2($this->dbh); - $this->performSynchronizationTest($central, new StaticL1('testSynchronizationDatabase1'), new StaticL1('testSynchronizationDatabase2')); - $this->performClearSynchronizationTest($central, new StaticL1('testSynchronizationDatabase1a'), new StaticL1('testSynchronizationDatabase2a')); + $this->performSynchronizationTest( + $central, + $this->l1Factory()->create('static', 'testSynchronizationDatabase1'), + $this->l1Factory()->create('static', 'testSynchronizationDatabase2') + ); + $this->performClearSynchronizationTest( + $central, + $this->l1Factory()->create('static', 'testSynchronizationDatabase1a'), + $this->l1Factory()->create('static', 'testSynchronizationDatabase2a') + ); } public function testTaggedSynchronizationDatabase() { $this->createSchema(); $central = new DatabaseL2($this->dbh); - $this->performTaggedSynchronizationTest($central, new StaticL1('testTaggedSynchronizationDatabase1'), new StaticL1('testTaggedSynchronizationDatabase2')); + $this->performTaggedSynchronizationTest( + $central, + $this->l1Factory()->create('static', 'testTaggedSynchronizationDatabase1'), + $this->l1Factory()->create('static', 'testTaggedSynchronizationDatabase2') + ); } public function testBrokenDatabaseFallback() { $this->createSchema(); $l2 = new DatabaseL2($this->dbh, '', true); - $l1 = new StaticL1('first'); + $l1 = $this->l1Factory()->create('static', 'first'); $pool = new Integrated($l1, $l2); $myaddr = new Address('mybin', 'mykey'); @@ -535,7 +590,7 @@ public function testBrokenDatabaseFallback() $this->assertNull($l2->getAddressesForTag('mytag')); // Try applying events to an uninitialized L1. - $this->assertNull($l2->applyEvents(new StaticL1())); + $this->assertNull($l2->applyEvents($this->l1Factory()->create('static'))); // Try garbage collection routines. $pool->collectGarbage(); @@ -547,7 +602,7 @@ public function testDatabaseL2SyncWithNoWrites() { $this->createSchema(); $l2 = new DatabaseL2($this->dbh, '', true); - $l1 = new StaticL1('first'); + $l1 = $this->l1Factory()->create('static', 'first'); $pool = new Integrated($l1, $l2); $pool->synchronize(); } @@ -580,19 +635,19 @@ protected function performExistsTest($l1) public function testExistsAPCuL1() { - $l1 = new APCuL1('first'); + $l1 = $this->l1Factory()->create('apcu', 'first'); $this->performExistsTest($l1); } public function testExistsStaticL1() { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $this->performExistsTest($l1); } public function testExistsSQLiteL1() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performExistsTest($l1); } @@ -600,7 +655,7 @@ public function testExistsIntegrated() { $this->createSchema(); $l2 = new DatabaseL2($this->dbh); - $l1 = new APCuL1('first'); + $l1 = $this->l1Factory()->create('apcu', 'first'); $pool = new Integrated($l1, $l2); $myaddr = new Address('mybin', 'mykey'); $pool->set($myaddr, 'myvalue'); @@ -621,13 +676,13 @@ public function testDatabaseL2Prefix() public function testAPCuL1PoolIDs() { // Test unique ID generation. - $l1 = new APCuL1(); + $l1 = $this->l1Factory()->create('apcu'); $this->assertNotNull($l1->getPool()); // Test host-based generation. $_SERVER['SERVER_ADDR'] = 'localhost'; $_SERVER['SERVER_PORT'] = '80'; - $l1 = new APCuL1(); + $l1 = $this->l1Factory()->create('apcu'); $this->assertEquals('localhost-80', $l1->getPool()); } @@ -646,13 +701,13 @@ protected function performL1AntirollbackTest($l1) public function testAPCuL1Antirollback() { - $l1 = new APCuL1('first'); + $l1 = $this->l1Factory()->create('apcu', 'first'); $this->performL1AntirollbackTest($l1); } public function testSQLite1Antirollback() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performL1AntirollbackTest($l1); } @@ -671,33 +726,33 @@ protected function performL1HitMissTest($l1) public function testStaticL1HitMiss() { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $this->performL1HitMissTest($l1); } public function testAPCuL1HitMiss() { - $l1 = new APCuL1('testAPCuL1HitMiss'); + $l1 = $this->l1Factory()->create('apcu', 'testAPCuL1HitMiss'); $this->performL1HitMissTest($l1); } public function testSQLiteL1HitMiss() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performL1HitMissTest($l1); } public function testPoolIntegrated() { $l2 = new StaticL2(); - $l1 = new APCuL1('first'); + $l1 = $this->l1Factory()->create('apcu', 'first'); $pool = new Integrated($l1, $l2); $this->assertEquals('first', $pool->getPool()); } protected function performFailedUnserializationTest($l2) { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $pool = new Integrated($l1, $l2); $myaddr = new Address('mybin', 'mykey'); @@ -723,7 +778,7 @@ protected function performFailedUnserializationTest($l2) protected function performCaughtUnserializationOnGetTest($l2) { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $pool = new Integrated($l1, $l2); $invalid_object = 'O:10:"HelloWorl":0:{}'; $myaddr = new Address('mybin', 'performCaughtUnserializationOnGetTest'); @@ -759,7 +814,7 @@ public function testStaticL2FailedUnserialization() // Callers should expect an UnserializationException. protected function performFailedUnserializationOnGetTest($l2) { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $pool = new Integrated($l1, $l2); $invalid_object = 'O:10:"HelloWorl":0:{}'; $myaddr = new Address('mybin', 'performFailedUnserializationOnGetTest'); @@ -788,7 +843,7 @@ public function testStaticL2FailedUnserializationOnGet() public function performGarbageCollectionTest($l2) { - $pool = new Integrated(new StaticL1(), $l2); + $pool = new Integrated($this->l1Factory()->create('static'), $l2); $myaddr = new Address('mybin', 'mykey'); $this->assertEquals(0, $l2->countGarbage()); $pool->set($myaddr, 'myvalue', -1); @@ -810,7 +865,7 @@ public function testStaticL2GarbageCollection() $this->performGarbageCollectionTest($l2); // Test item limits. - $pool = new Integrated(new StaticL1(), $l2); + $pool = new Integrated($this->l1Factory()->create('static'), $l2); $myaddr2 = new Address('mybin', 'mykey2'); $myaddr3 = new Address('mybin', 'mykey3'); $pool->collectGarbage(); @@ -844,17 +899,17 @@ protected function performHitSetCounterTest($l1) public function testStaticL1Counters() { - $this->performHitSetCounterTest(new StaticL1()); + $this->performHitSetCounterTest($this->l1Factory()->create('static')); } public function testAPCuL1Counters() { - $this->performHitSetCounterTest(new APCuL1('counters')); + $this->performHitSetCounterTest($this->l1Factory()->create('apcu', 'counters')); } public function testSQLiteL1Counters() { - $this->performHitSetCounterTest(new SQLiteL1()); + $this->performHitSetCounterTest($this->l1Factory()->create('sqlite')); } protected function performExcessiveOverheadSkippingTest($l1) @@ -895,34 +950,34 @@ protected function performExcessiveOverheadSkippingTest($l1) public function testStaticL1ExcessiveOverheadSkipping() { - $this->performExcessiveOverheadSkippingTest(new StaticL1()); + $this->performExcessiveOverheadSkippingTest($this->l1Factory()->create('static')); } public function testAPCuL1ExcessiveOverheadSkipping() { - $this->performExcessiveOverheadSkippingTest(new APCuL1('overhead')); + $this->performExcessiveOverheadSkippingTest($this->l1Factory()->create('apcu', 'overhead')); } public function testSQLiteL1ExcessiveOverheadSkipping() { - $this->performExcessiveOverheadSkippingTest(new SQLiteL1()); + $this->performExcessiveOverheadSkippingTest($this->l1Factory()->create('sqlite')); } public function testAPCuL1IntegratedExpiration() { - $l1 = new APCuL1('expiration'); + $l1 = $this->l1Factory()->create('apcu', 'expiration'); $this->performIntegratedExpiration($l1); } public function testStaticL1IntegratedExpiration() { - $l1 = new StaticL1(); + $l1 = $this->l1Factory()->create('static'); $this->performIntegratedExpiration($l1); } public function testSQLiteL1IntegratedExpiration() { - $l1 = new SQLiteL1(); + $l1 = $this->l1Factory()->create('sqlite'); $this->performIntegratedExpiration($l1); } @@ -963,10 +1018,10 @@ public function testDatabaseL2BatchDeletion() public function testSQLiteL1SchemaErrorHandling() { $pool_name = uniqid('', true) . '-' . mt_rand(); - $l1_a = new SQLiteL1($pool_name); + $l1_a = $this->l1Factory()->create('sqlite', $pool_name); // Opening a second instance of the same pool should work. - $l1_b = new SQLiteL1($pool_name); + $l1_b = $this->l1Factory()->create('sqlite', $pool_name); $myaddr = new Address('mybin', 'mykey'); From 3d4d54d1316ffc513385874cd411254480bb5041 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Sun, 11 Dec 2016 23:42:51 +0200 Subject: [PATCH 05/24] Improvements in StateL1Null implementaion and fixes for scrutinizer CI. --- src/L1CacheFactory.php | 6 +++--- src/StateL1Null.php | 25 +++---------------------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/L1CacheFactory.php b/src/L1CacheFactory.php index 1ff2f6e..2e04cda 100644 --- a/src/L1CacheFactory.php +++ b/src/L1CacheFactory.php @@ -63,7 +63,7 @@ protected function createApcu($pool) */ protected function createNull($pool) { - return new NullL1($pool, new StateL1Static($pool)); + return new NullL1($pool, new StateL1Null()); } /** @@ -74,7 +74,7 @@ protected function createNull($pool) */ protected function createStatic($pool) { - return new StaticL1($pool, new StateL1Static($pool)); + return new StaticL1($pool, new StateL1Static()); } /** @@ -87,7 +87,7 @@ protected function createSqlite($pool) { $hasApcu = function_exists('apcu_fetch'); // TODO: Maybe implement StateL1SQLite class instead of NULL one. - $state = $hasApcu ? new StateL1APCu($pool) : new StateL1Null($pool); + $state = $hasApcu ? new StateL1APCu($pool) : new StateL1Null(); $cache = new SQLiteL1($pool, $state); return $cache; } diff --git a/src/StateL1Null.php b/src/StateL1Null.php index e78e04e..7690225 100644 --- a/src/StateL1Null.php +++ b/src/StateL1Null.php @@ -12,14 +12,11 @@ * * @author ndobromirov */ -class StateL1Null implements StateL1Interface +class StateL1Null extends StateL1Static implements StateL1Interface { - /** - * {inheritdoc} - */ - public function clear() + public function __construct() { - // Nothing to do here. + parent::__construct(); } /** @@ -30,14 +27,6 @@ public function getHits() return 0; } - /** - * {inheritdoc} - */ - public function getMisses() - { - return 0; - } - /** * {inheritdoc} */ @@ -46,14 +35,6 @@ public function recordHit() return false; } - /** - * {inheritdoc} - */ - public function recordMiss() - { - return false; - } - /** * {inheritdoc} */ From 085c65167c4eb9ab500fb04e10a9cc3556e506c3 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Sun, 11 Dec 2016 23:55:18 +0200 Subject: [PATCH 06/24] CS fixes and improvements against scrutnizer. --- src/APCuL1.php | 15 ++++++++++----- src/L1CacheFactory.php | 1 - tests/LCacheTest.php | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/APCuL1.php b/src/APCuL1.php index bff6147..27a4931 100644 --- a/src/APCuL1.php +++ b/src/APCuL1.php @@ -26,9 +26,10 @@ public function getKeyOverhead(Address $address) // decrementing the overhead by existing hits when an item is set. This // would make hits cheaper but writes more expensive. + $success = null; $apcu_key = $this->getLocalKey($address); $overhead = apcu_fetch($apcu_key . ':overhead', $success); - if ($success) { + if ($success === true) { return $overhead; } return 0; @@ -55,9 +56,10 @@ public function setWithExpiration($event_id, Address $address, $value, $created, // If not setting a negative cache entry, increment the key's overhead. if (!is_null($value)) { + $overhead_success = null; $apcu_key_overhead = $apcu_key . ':overhead'; apcu_inc($apcu_key_overhead, 1, $overhead_success); - if (!$overhead_success) { + if ($overhead_success === false) { // @codeCoverageIgnoreStart apcu_store($apcu_key_overhead, 1); // @codeCoverageIgnoreEnd @@ -69,9 +71,10 @@ public function setWithExpiration($event_id, Address $address, $value, $created, public function isNegativeCache(Address $address) { + $success = null; $apcu_key = $this->getLocalKey($address); $entry = apcu_fetch($apcu_key, $success); - return ($success && is_null($entry->value)); + return ($success === true && is_null($entry->value)); } public function getEntry(Address $address) @@ -80,16 +83,18 @@ public function getEntry(Address $address) $apcu_key_overhead = $apcu_key . ':overhead'; // Decrement the key's overhead. + $overhead_success = null; apcu_dec($apcu_key_overhead, 1, $overhead_success); - if (!$overhead_success) { + if ($overhead_success === false) { // @codeCoverageIgnoreStart apcu_store($apcu_key_overhead, -1); // @codeCoverageIgnoreEnd } + $success = null; $entry = apcu_fetch($apcu_key, $success); // Handle failed reads. - if (!$success) { + if ($success === false) { $this->recordMiss(); return null; } diff --git a/src/L1CacheFactory.php b/src/L1CacheFactory.php index 2e04cda..81cf963 100644 --- a/src/L1CacheFactory.php +++ b/src/L1CacheFactory.php @@ -104,7 +104,6 @@ protected function createSqlite($pool) */ protected function getPool($pool = null) { - $result = null; if (!is_null($pool)) { $result = (string) $pool; } elseif (isset($_SERVER['SERVER_ADDR']) && isset($_SERVER['SERVER_PORT'])) { diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 7453d11..17b94ea 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -15,7 +15,7 @@ class LCacheTest extends \PHPUnit_Extensions_Database_TestCase */ protected function l1Factory() { - if ($this->_factory === NULL) { + if ($this->_factory === null) { $this->_factory = new L1CacheFactory(); } return $this->_factory; From 8b0040edba8ea106b2ac92a08f948ae7af370746 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Sun, 11 Dec 2016 23:59:39 +0200 Subject: [PATCH 07/24] Removed dead code. --- src/L1.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/L1.php b/src/L1.php index 5bf6311..3029fe6 100644 --- a/src/L1.php +++ b/src/L1.php @@ -24,12 +24,6 @@ public function __construct($pool, StateL1Interface $state) $this->state = $state; } - protected function generateUniqueID() - { - // @TODO: Replace with a persistent but machine-local (and unique) method. - return uniqid('', true) . '-' . mt_rand(); - } - public function getLastAppliedEventID() { return $this->state->getLastAppliedEventID(); From 247b39813a78ad2bfecb27c4d6d0b8a8a0c4b7bc Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Mon, 12 Dec 2016 00:22:34 +0200 Subject: [PATCH 08/24] Tweak in the L1 Null caches implementation together with L1 Null state manager. --- src/NullL1.php | 7 ------- src/StateL1Null.php | 4 ++-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/NullL1.php b/src/NullL1.php index 922d682..daae096 100644 --- a/src/NullL1.php +++ b/src/NullL1.php @@ -9,11 +9,4 @@ public function setWithExpiration($event_id, Address $address, $value, $created, // Store nothing; always succeed. return true; } - - public function getLastAppliedEventID() - { - // Because we store nothing locally, behave as if all events - // are applied. - return PHP_INT_MAX; - } } diff --git a/src/StateL1Null.php b/src/StateL1Null.php index 7690225..786bd7b 100644 --- a/src/StateL1Null.php +++ b/src/StateL1Null.php @@ -40,8 +40,8 @@ public function recordHit() */ public function getLastAppliedEventID() { - // TODO: Decide on this. - // Current assumtion is that all events were applied already. + // Because we store nothing locally, behave as if all events + // are applied. return PHP_INT_MAX; } From 341377a420496945e833405292b45119d26c170b Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Mon, 12 Dec 2016 00:33:47 +0200 Subject: [PATCH 09/24] Added a test for the invalid driver on the factory. Removed unused methods on the StateL1Null class. --- src/StateL1Null.php | 22 +++------------------- tests/LCacheTest.php | 6 ++++++ 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/src/StateL1Null.php b/src/StateL1Null.php index 786bd7b..913b75b 100644 --- a/src/StateL1Null.php +++ b/src/StateL1Null.php @@ -24,33 +24,17 @@ public function __construct() */ public function getHits() { + // No cache hits on the null stats. return 0; } - /** - * {inheritdoc} - */ - public function recordHit() - { - return false; - } - /** * {inheritdoc} */ public function getLastAppliedEventID() { - // Because we store nothing locally, behave as if all events - // are applied. + // Because we store nothing locally. + // Behave as if all events are applied. return PHP_INT_MAX; } - - /** - * {inheritdoc} - */ - public function setLastAppliedEventID($eventId) - { - // Always success. - return true; - } } diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 17b94ea..35bb38e 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -43,6 +43,12 @@ protected function createSchema($prefix = '') $this->dbh->exec('CREATE INDEX ' . $prefix . 'rewritten_entry ON ' . $prefix . 'lcache_tags ("event_id")'); } + public function testL1Factory() { + $staticL1 = $this->l1Factory()->create('static'); + $invalidL1 = $this->l1Factory()->create('invalid_cache_driver'); + $this->assertEquals(get_class($staticL1), get_class($invalidL1)); + } + public function testNullL1() { $event_id = 1; From c4419bd9d648a6faa294b44dbea9759daa12b933 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Mon, 12 Dec 2016 00:37:09 +0200 Subject: [PATCH 10/24] CS fixes. --- tests/LCacheTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 35bb38e..4968a41 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -43,7 +43,8 @@ protected function createSchema($prefix = '') $this->dbh->exec('CREATE INDEX ' . $prefix . 'rewritten_entry ON ' . $prefix . 'lcache_tags ("event_id")'); } - public function testL1Factory() { + public function testL1Factory() + { $staticL1 = $this->l1Factory()->create('static'); $invalidL1 = $this->l1Factory()->create('invalid_cache_driver'); $this->assertEquals(get_class($staticL1), get_class($invalidL1)); From 1fb91300c9237fe8ca03f438c46631edf1db48eb Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 13 Dec 2016 15:57:55 +0200 Subject: [PATCH 11/24] Inevrted === and !== checks to have constant as first argument. Refactored 2 loops in StaticL2 to reduce code indentation. --- src/APCuL1.php | 10 +++++----- src/Address.php | 2 +- src/DatabaseL2.php | 8 ++++---- src/SQLiteL1.php | 6 +++--- src/StateL1APCu.php | 2 +- src/StaticL2.php | 43 +++++++++++++++++++++++-------------------- 6 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/APCuL1.php b/src/APCuL1.php index 27a4931..e10d2c9 100644 --- a/src/APCuL1.php +++ b/src/APCuL1.php @@ -29,7 +29,7 @@ public function getKeyOverhead(Address $address) $success = null; $apcu_key = $this->getLocalKey($address); $overhead = apcu_fetch($apcu_key . ':overhead', $success); - if ($success === true) { + if (true === $success) { return $overhead; } return 0; @@ -59,7 +59,7 @@ public function setWithExpiration($event_id, Address $address, $value, $created, $overhead_success = null; $apcu_key_overhead = $apcu_key . ':overhead'; apcu_inc($apcu_key_overhead, 1, $overhead_success); - if ($overhead_success === false) { + if (false === $overhead_success) { // @codeCoverageIgnoreStart apcu_store($apcu_key_overhead, 1); // @codeCoverageIgnoreEnd @@ -74,7 +74,7 @@ public function isNegativeCache(Address $address) $success = null; $apcu_key = $this->getLocalKey($address); $entry = apcu_fetch($apcu_key, $success); - return ($success === true && is_null($entry->value)); + return (true === $success && is_null($entry->value)); } public function getEntry(Address $address) @@ -85,7 +85,7 @@ public function getEntry(Address $address) // Decrement the key's overhead. $overhead_success = null; apcu_dec($apcu_key_overhead, 1, $overhead_success); - if ($overhead_success === false) { + if (false === $overhead_success) { // @codeCoverageIgnoreStart apcu_store($apcu_key_overhead, -1); // @codeCoverageIgnoreEnd @@ -94,7 +94,7 @@ public function getEntry(Address $address) $success = null; $entry = apcu_fetch($apcu_key, $success); // Handle failed reads. - if ($success === false) { + if (false === $success) { $this->recordMiss(); return null; } diff --git a/src/Address.php b/src/Address.php index efe5d94..c57d16c 100644 --- a/src/Address.php +++ b/src/Address.php @@ -121,7 +121,7 @@ public function unserialize($serialized) } // @TODO: Remove check against false for PHP 7+ - if ($this->key === false || $this->key === '') { + if (false === $this->key || '' === $this->key) { $this->key = null; } } diff --git a/src/DatabaseL2.php b/src/DatabaseL2.php index c6473c6..d28b8c8 100644 --- a/src/DatabaseL2.php +++ b/src/DatabaseL2.php @@ -162,7 +162,7 @@ public function getEntry(Address $address) //$last_matching_entry = $sth->fetchObject('LCacheEntry'); $last_matching_entry = $sth->fetchObject(); - if ($last_matching_entry === false) { + if (false === $last_matching_entry) { $this->misses++; return null; } @@ -176,7 +176,7 @@ public function getEntry(Address $address) $unserialized_value = @unserialize($last_matching_entry->value); // If unserialization failed, raise an exception. - if ($unserialized_value === false && $last_matching_entry->value !== serialize(false)) { + if (false === $unserialized_value && serialize(false) !== $last_matching_entry->value) { throw new UnserializationException($address, $last_matching_entry->value); } @@ -364,7 +364,7 @@ public function applyEvents(L1 $l1) return null; } $last_event = $sth->fetchObject(); - if ($last_event === false) { + if (false === $last_event) { $l1->setLastAppliedEventID(0); } else { $l1->setLastAppliedEventID($last_event->event_id); @@ -391,7 +391,7 @@ public function applyEvents(L1 $l1) $l1->delete($event->event_id, $address); } else { $unserialized_value = @unserialize($event->value); - if ($unserialized_value === false && $event->value !== serialize(false)) { + if (false === $unserialized_value && serialize(false) !== $event->value) { // Delete the L1 entry, if any, when we fail to unserialize. $l1->delete($event->event_id, $address); } else { diff --git a/src/SQLiteL1.php b/src/SQLiteL1.php index ef81d19..11cc5dd 100644 --- a/src/SQLiteL1.php +++ b/src/SQLiteL1.php @@ -82,7 +82,7 @@ public function getKeyOverhead(Address $address) $sth->execute(); $result = $sth->fetchObject(); - if ($result === false) { + if (false === $result) { return 0; } @@ -175,7 +175,7 @@ public function getEntry(Address $address) // do this to simultaneously track useful overhead data but not unnecessarily // record reads after they massively outweigh writes for an address. // @TODO: Make this adapt to overhead thresholds. - if ($entry === false || $entry->reads < 10 * $entry->writes || $entry->reads < 10) { + if (false === $entry || $entry->reads < 10 * $entry->writes || $entry->reads < 10) { $sth = $this->dbh->prepare('UPDATE entries SET "reads" = "reads" + 1 WHERE "address" = :address'); $sth->bindValue(':address', $address->serialize(), \PDO::PARAM_STR); $sth->execute(); @@ -188,7 +188,7 @@ public function getEntry(Address $address) } } - if ($entry === false) { + if (false === $entry) { $this->recordMiss(); return null; } diff --git a/src/StateL1APCu.php b/src/StateL1APCu.php index 1771c06..3fd253b 100644 --- a/src/StateL1APCu.php +++ b/src/StateL1APCu.php @@ -106,7 +106,7 @@ public function getMisses() public function getLastAppliedEventID() { $value = apcu_fetch($this->statusKeyLastAppliedEventId); - if ($value === false) { + if (false === $value) { $value = null; } return $value; diff --git a/src/StaticL2.php b/src/StaticL2.php index 845cf51..ffb7566 100644 --- a/src/StaticL2.php +++ b/src/StaticL2.php @@ -47,16 +47,17 @@ public function collectGarbage($item_limit = null) // Returns an LCache\Entry public function getEntry(Address $address) { + $events = array_filter($this->events, function (Entry $entry) use ($address) { + return $entry->getAddress()->isMatch($address); + }); $last_matching_entry = null; - foreach ($this->events as $event_id => $entry) { - if ($entry->getAddress()->isMatch($address)) { - if ($entry->getAddress()->isEntireCache() || $entry->getAddress()->isEntireBin()) { - $last_matching_entry = null; - } elseif (!is_null($entry->expiration) && $entry->expiration < $_SERVER['REQUEST_TIME']) { - $last_matching_entry = null; - } else { - $last_matching_entry = clone $entry; - } + foreach ($events as $entry) { + if ($entry->getAddress()->isEntireCache() || $entry->getAddress()->isEntireBin()) { + $last_matching_entry = null; + } elseif (!is_null($entry->expiration) && $entry->expiration < $_SERVER['REQUEST_TIME']) { + $last_matching_entry = null; + } else { + $last_matching_entry = clone $entry; } } // Last event was a deletion, so miss. @@ -68,7 +69,7 @@ public function getEntry(Address $address) $unserialized_value = @unserialize($last_matching_entry->value); // If unserialization failed, miss. - if ($unserialized_value === false && $last_matching_entry->value !== serialize(false)) { + if (false === $unserialized_value && serialize(false) !== $last_matching_entry->value) { throw new UnserializationException($address, $last_matching_entry->value); } @@ -151,20 +152,22 @@ public function applyEvents(L1 $l1) $applied = 0; foreach ($this->events as $event_id => $event) { // Skip events that are too old or were created by the local L1. - if ($event_id > $last_applied_event_id && $event->pool !== $l1->getPool()) { - if (is_null($event->value)) { + if ($event_id <= $last_applied_event_id || $event->pool === $l1->getPool()) { + continue; + } + + if (is_null($event->value)) { + $l1->delete($event->event_id, $event->getAddress()); + } else { + $unserialized_value = @unserialize($event->value); + if (false === $unserialized_value && serialize(false) !== $event->value) { + // Delete the L1 entry, if any, when we fail to unserialize. $l1->delete($event->event_id, $event->getAddress()); } else { - $unserialized_value = @unserialize($event->value); - if ($unserialized_value === false && $event->value !== serialize(false)) { - // Delete the L1 entry, if any, when we fail to unserialize. - $l1->delete($event->event_id, $event->getAddress()); - } else { - $l1->setWithExpiration($event->event_id, $event->getAddress(), $unserialized_value, $event->created, $event->expiration); - } + $l1->setWithExpiration($event->event_id, $event->getAddress(), $unserialized_value, $event->created, $event->expiration); } - $applied++; } + $applied++; } // Just in case there were skipped events, set the high water mark. From cecb2a436e933060518b285144cbf96ee4f8b035 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Sat, 17 Dec 2016 22:00:55 +0200 Subject: [PATCH 12/24] SQLiteL1: Determine filename based on schema definition strings (#75) --- src/SQLiteL1.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/SQLiteL1.php b/src/SQLiteL1.php index 11cc5dd..45a7867 100644 --- a/src/SQLiteL1.php +++ b/src/SQLiteL1.php @@ -26,14 +26,27 @@ protected static function tableExists(\PDO $dbh, $table_name) protected static function initializeSchema(\PDO $dbh) { if (!self::tableExists($dbh, 'entries')) { - $dbh->exec('CREATE TABLE IF NOT EXISTS entries("address" TEXT PRIMARY KEY, "value" BLOB, "expiration" INTEGER, "created" INTEGER, "event_id" INTEGER NOT NULL DEFAULT 0, "reads" INTEGER NOT NULL DEFAULT 0, "writes" INTEGER NOT NULL DEFAULT 0)'); - $dbh->exec('CREATE INDEX IF NOT EXISTS expiration ON entries ("expiration")'); + foreach (self::schemaStatements() as $query) { + $dbh->exec($query); + } } } + protected static function schemaStatements() + { + return [ + // Table creation. + 'CREATE TABLE IF NOT EXISTS entries("address" TEXT PRIMARY KEY, "value" BLOB, "expiration" INTEGER, "created" INTEGER, "event_id" INTEGER NOT NULL DEFAULT 0, "reads" INTEGER NOT NULL DEFAULT 0, "writes" INTEGER NOT NULL DEFAULT 0)', + + // Index creation. + 'CREATE INDEX IF NOT EXISTS expiration ON entries ("expiration")', + ]; + } + protected static function getDatabaseHandle($pool) { - $path = join(DIRECTORY_SEPARATOR, array(sys_get_temp_dir(), 'lcache-' . $pool)); + $schemaId = substr(hash('sha512', implode(';', self::schemaStatements())), 0, 20); + $path = join(DIRECTORY_SEPARATOR, array(sys_get_temp_dir(), "lcache-$pool-$schemaId")); $dbh = new \PDO('sqlite:' . $path . '.sqlite3'); $dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $dbh->exec('PRAGMA synchronous = OFF'); From 7eef3a8fcb4b2266ddc29ce7e4303a7691b3f215 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 16:22:10 +0200 Subject: [PATCH 13/24] Refactorin in L1 related tests. Abstracted L1 tests. Implemented Initial L1 concrete classes for APCu, Static and SQLite. --- src/L1CacheFactory.php | 2 +- tests/L1/APCuTest.php | 24 +++++++++++++ tests/L1/SQLiteTest.php | 24 +++++++++++++ tests/L1/StaticTest.php | 24 +++++++++++++ tests/L1CacheTest.php | 77 +++++++++++++++++++++++++++++++++++++++++ tests/LCacheTest.php | 72 -------------------------------------- 6 files changed, 150 insertions(+), 73 deletions(-) create mode 100644 tests/L1/APCuTest.php create mode 100644 tests/L1/SQLiteTest.php create mode 100644 tests/L1/StaticTest.php create mode 100644 tests/L1CacheTest.php diff --git a/src/L1CacheFactory.php b/src/L1CacheFactory.php index 81cf963..f1d1d82 100644 --- a/src/L1CacheFactory.php +++ b/src/L1CacheFactory.php @@ -87,7 +87,7 @@ protected function createSqlite($pool) { $hasApcu = function_exists('apcu_fetch'); // TODO: Maybe implement StateL1SQLite class instead of NULL one. - $state = $hasApcu ? new StateL1APCu($pool) : new StateL1Null(); + $state = $hasApcu ? new StateL1APCu("sqlite-$pool") : new StateL1Null(); $cache = new SQLiteL1($pool, $state); return $cache; } diff --git a/tests/L1/APCuTest.php b/tests/L1/APCuTest.php new file mode 100644 index 0000000..27f56dc --- /dev/null +++ b/tests/L1/APCuTest.php @@ -0,0 +1,24 @@ +create($this->driverName(), $pool); + } + + public function testSetGetDelete() + { + $l1 = $this->createL1(); + + $event_id = 1; + + $myaddr = new Address('mybin', 'mykey'); + + $this->assertEquals(0, $l1->getHits()); + $this->assertEquals(0, $l1->getMisses()); + + // Try to get an entry from an empty L1. + $entry = $l1->get($myaddr); + $this->assertNull($entry); + $this->assertEquals(0, $l1->getHits()); + $this->assertEquals(1, $l1->getMisses()); + + // Set and get an entry. + $l1->set($event_id++, $myaddr, 'myvalue'); + $entry = $l1->get($myaddr); + $this->assertEquals('myvalue', $entry); + $this->assertEquals(1, $l1->getHits()); + $this->assertEquals(1, $l1->getMisses()); + + // Delete the entry and try to get it again. + $l1->delete($event_id++, $myaddr); + $entry = $l1->get($myaddr); + $this->assertNull($entry); + $this->assertEquals(1, $l1->getHits()); + $this->assertEquals(2, $l1->getMisses()); + + // Clear everything and try to read. + $l1->delete($event_id++, new Address()); + $entry = $l1->get($myaddr); + $this->assertNull($entry); + $this->assertEquals(0, $l1->getHits()); + $this->assertEquals(1, $l1->getMisses()); + + // This is a no-op for most L1 implementations, but it should not + // return false, regardless. + $this->assertTrue(false !== $l1->collectGarbage()); + + // Test complex values that need serialization. + $myarray = [1, 2, 3]; + $l1->set($event_id++, $myaddr, $myarray); + $entry = $l1->get($myaddr); + $this->assertEquals($myarray, $entry); + + // Test creation tracking. + $l1->setWithExpiration($event_id++, $myaddr, 'myvalue', 42); + $entry = $l1->getEntry($myaddr); + $this->assertEquals(42, $entry->created); + } +} diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 4968a41..a32a846 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -66,78 +66,6 @@ public function testNullL1() $this->assertEquals(PHP_INT_MAX, $cache->getLastAppliedEventID()); } - protected function performSetGetDeleteTest($l1) - { - $event_id = 1; - - $myaddr = new Address('mybin', 'mykey'); - - $this->assertEquals(0, $l1->getHits()); - $this->assertEquals(0, $l1->getMisses()); - - // Try to get an entry from an empty L1. - $entry = $l1->get($myaddr); - $this->assertNull($entry); - $this->assertEquals(0, $l1->getHits()); - $this->assertEquals(1, $l1->getMisses()); - - // Set and get an entry. - $l1->set($event_id++, $myaddr, 'myvalue'); - $entry = $l1->get($myaddr); - $this->assertEquals('myvalue', $entry); - $this->assertEquals(1, $l1->getHits()); - $this->assertEquals(1, $l1->getMisses()); - - // Delete the entry and try to get it again. - $l1->delete($event_id++, $myaddr); - $entry = $l1->get($myaddr); - $this->assertNull($entry); - $this->assertEquals(1, $l1->getHits()); - $this->assertEquals(2, $l1->getMisses()); - - // Clear everything and try to read. - $l1->delete($event_id++, new Address()); - $entry = $l1->get($myaddr); - $this->assertNull($entry); - $this->assertEquals(0, $l1->getHits()); - $this->assertEquals(1, $l1->getMisses()); - - // This is a no-op for most L1 implementations, but it should not - // return false, regardless. - $this->assertTrue(false !== $l1->collectGarbage()); - - // Test complex values that need serialization. - $myarray = [1, 2, 3]; - $l1->set($event_id++, $myaddr, $myarray); - $entry = $l1->get($myaddr); - $this->assertEquals($myarray, $entry); - - // Test creation tracking. - $l1->setWithExpiration($event_id++, $myaddr, 'myvalue', 42); - $entry = $l1->getEntry($myaddr); - $this->assertEquals(42, $entry->created); - } - - public function testStaticL1SetGetDelete() - { - - $l1 = $this->l1Factory()->create('static'); - $this->performSetGetDeleteTest($l1); - } - - public function testSQLiteL1SetGetDelete() - { - $l1 = $this->l1Factory()->create('sqlite'); - $this->performSetGetDeleteTest($l1); - } - - public function testAPCuL1SetGetDelete() - { - $l1 = $this->l1Factory()->create('apcu', 'setGetDelete'); - - $this->performSetGetDeleteTest($l1); - } - public function testStaticL1Antirollback() { $l1 = $this->l1Factory()->create('static'); From 3f1e10fef260f4b330b519feb594c966ff7b9746 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 16:37:02 +0200 Subject: [PATCH 14/24] Moved performL1AntirollbackTest to the new L1 tests structure. --- tests/L1CacheTest.php | 45 ++++++++++++++++++++++++++++++++++++++----- tests/LCacheTest.php | 31 ----------------------------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 6e1f719..694ee52 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -1,22 +1,39 @@ create($this->driverName(), $pool); } @@ -74,4 +91,22 @@ public function testSetGetDelete() $entry = $l1->getEntry($myaddr); $this->assertEquals(42, $entry->created); } + + public function testPreventRollback() + { + $l1 = $this->createL1(); + + $myaddr = new Address('mybin', 'mykey'); + $current_event_id = $l1->getLastAppliedEventID(); + if (is_null($current_event_id)) { + $current_event_id = 1; + } + // Write something in the cache. + $l1->set($current_event_id++, $myaddr, 'myvalue'); + $this->assertEquals('myvalue', $l1->get($myaddr)); + + // Atempt to write somthing with older event id. + $l1->set($current_event_id - 2, $myaddr, 'myoldvalue'); + $this->assertEquals('myvalue', $l1->get($myaddr)); + } } diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index a32a846..9d659ce 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -66,12 +66,6 @@ public function testNullL1() $this->assertEquals(PHP_INT_MAX, $cache->getLastAppliedEventID()); } - public function testStaticL1Antirollback() - { - $l1 = $this->l1Factory()->create('static'); - $this->performL1AntirollbackTest($l1); - } - public function performL1FullDelete($cache) { $event_id = 1; @@ -621,31 +615,6 @@ public function testAPCuL1PoolIDs() $this->assertEquals('localhost-80', $l1->getPool()); } - protected function performL1AntirollbackTest($l1) - { - $myaddr = new Address('mybin', 'mykey'); - $current_event_id = $l1->getLastAppliedEventID(); - if (is_null($current_event_id)) { - $current_event_id = 1; - } - $l1->set($current_event_id++, $myaddr, 'myvalue'); - $this->assertEquals('myvalue', $l1->get($myaddr)); - $l1->set($current_event_id - 2, $myaddr, 'myoldvalue'); - $this->assertEquals('myvalue', $l1->get($myaddr)); - } - - public function testAPCuL1Antirollback() - { - $l1 = $this->l1Factory()->create('apcu', 'first'); - $this->performL1AntirollbackTest($l1); - } - - public function testSQLite1Antirollback() - { - $l1 = $this->l1Factory()->create('sqlite'); - $this->performL1AntirollbackTest($l1); - } - protected function performL1HitMissTest($l1) { $myaddr = new Address('mybin', 'mykey'); From 459d23880fc422a2e267aa9732bb678ce9770468 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 16:49:09 +0200 Subject: [PATCH 15/24] Refactored performL1FullDelete. Implemented a TODO from the APCu class to reduce danger in using the library (maybe added overhead on cache clear). --- src/APCuL1.php | 20 +++++++++----------- tests/L1CacheTest.php | 14 ++++++++++++++ tests/LCacheTest.php | 33 --------------------------------- 3 files changed, 23 insertions(+), 44 deletions(-) diff --git a/src/APCuL1.php b/src/APCuL1.php index e10d2c9..45afe7c 100644 --- a/src/APCuL1.php +++ b/src/APCuL1.php @@ -118,14 +118,11 @@ protected function getIterator($pattern, $format = APC_ITER_ALL) public function delete($event_id, Address $address) { - if ($address->isEntireCache()) { - $this->state->clear(); - // @TODO: Consider flushing only LCache L1 storage by using an iterator. - return apcu_clear_cache(); - } - if ($address->isEntireBin()) { - $prefix = $this->getLocalKey($address); - $pattern = '/^' . preg_quote($prefix) . '.*/'; + $localKey = $this->getLocalKey($address); + + if ($address->isEntireCache() || $address->isEntireBin()) { + $localKey = $this->getLocalKey($address); + $pattern = '/^' . preg_quote($localKey) . '.*/'; $matching = $this->getIterator($pattern, APC_ITER_KEY); assert(!is_null($matching), 'Iterator instantiation failed.'); foreach ($matching as $match) { @@ -133,14 +130,15 @@ public function delete($event_id, Address $address) // deleted in another process using the same APCu. apcu_delete($match['key']); } + if ($address->isEntireCache()) { + $this->state->clear(); + } return true; } - $apcu_key = $this->getLocalKey($address); - // Ignore failures of delete because the key may have been // deleted in another process using the same APCu. - apcu_delete($apcu_key); + apcu_delete($localKey); return true; } } diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 694ee52..1b1cab4 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -109,4 +109,18 @@ public function testPreventRollback() $l1->set($current_event_id - 2, $myaddr, 'myoldvalue'); $this->assertEquals('myvalue', $l1->get($myaddr)); } + + public function testFullDelete() + { + $event_id = 1; + $l1 = $this->createL1(); + $myaddr = new Address('mybin', 'mykey'); + + // Set an entry and clear the storage. + $l1->set($event_id++, $myaddr, 'myvalue'); + $l1->delete($event_id++, new Address()); + $this->assertEquals(null, $l1->get($myaddr)); + $this->assertEquals(0, $l1->getHits()); + $this->assertEquals(1, $l1->getMisses()); + } } diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 9d659ce..a5035a5 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -66,39 +66,6 @@ public function testNullL1() $this->assertEquals(PHP_INT_MAX, $cache->getLastAppliedEventID()); } - public function performL1FullDelete($cache) - { - $event_id = 1; - - $myaddr = new Address('mybin', 'mykey'); - - // Set an entry and clear the storage. - $cache->set($event_id++, $myaddr, 'myvalue'); - $cache->delete($event_id++, new Address()); - $entry = $cache->get($myaddr); - $this->assertEquals(null, $entry); - $this->assertEquals(0, $cache->getHits()); - $this->assertEquals(1, $cache->getMisses()); - } - - public function testSQLiteL1FullDelete() - { - $l1 = $this->l1Factory()->create('sqlite'); - $this->performL1FullDelete($l1); - } - - public function testAPCuL1FullDelete() - { - $l1 = $this->l1Factory()->create('apcu', 'setGetDelete'); - $this->performL1FullDelete($l1); - } - - public function testStaticL1FullDelete() - { - $l1 = $this->l1Factory()->create('static'); - $this->performL1FullDelete($l1); - } - public function performL1Expiration($l1) { $event_id = 1; From 96f5c2bafb419fa45b4d8ec53ef616c8b8d0e1d5 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 16:51:29 +0200 Subject: [PATCH 16/24] Refactored L1 expiration tests. --- tests/L1CacheTest.php | 13 +++++++++++++ tests/LCacheTest.php | 31 ------------------------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 1b1cab4..2b1efe1 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -123,4 +123,17 @@ public function testFullDelete() $this->assertEquals(0, $l1->getHits()); $this->assertEquals(1, $l1->getMisses()); } + + public function testExpiration() + { + $event_id = 1; + $l1 = $this->createL1(); + $myaddr = new Address('mybin', 'mykey'); + + // Set and get an entry. + $l1->set($event_id++, $myaddr, 'myvalue', -1); + $this->assertNull($l1->get($myaddr)); + $this->assertEquals(0, $l1->getHits()); + $this->assertEquals(1, $l1->getMisses()); + } } diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index a5035a5..fe523a4 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -66,37 +66,6 @@ public function testNullL1() $this->assertEquals(PHP_INT_MAX, $cache->getLastAppliedEventID()); } - public function performL1Expiration($l1) - { - $event_id = 1; - - $myaddr = new Address('mybin', 'mykey'); - - // Set and get an entry. - $l1->set($event_id++, $myaddr, 'myvalue', -1); - $this->assertNull($l1->get($myaddr)); - $this->assertEquals(0, $l1->getHits()); - $this->assertEquals(1, $l1->getMisses()); - } - - public function testSQLiteL1Expiration() - { - $l1 = $this->l1Factory()->create('sqlite'); - $this->performL1Expiration($l1); - } - - public function testAPCuL1Expiration() - { - $l1 = $this->l1Factory()->create('apcu', 'expiration'); - $this->performL1Expiration($l1); - } - - public function testStaticL1Expiration() - { - $l1 = $this->l1Factory()->create('static'); - $this->performL1Expiration($l1); - } - public function testClearStaticL2() { $l2 = new StaticL2(); From 83f22bd60f3ea6fbeed20705ba607cf9557add0f Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 17:11:39 +0200 Subject: [PATCH 17/24] Refacored pool and existance tests. --- tests/L1CacheTest.php | 22 ++++++++++++++++++++++ tests/LCacheTest.php | 41 +---------------------------------------- 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 2b1efe1..6e72b7b 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -136,4 +136,26 @@ public function testExpiration() $this->assertEquals(0, $l1->getHits()); $this->assertEquals(1, $l1->getMisses()); } + + public function testExists() + { + $l1 = $this->createL1(); + $myaddr = new Address('mybin', 'mykey'); + + $l1->set(1, $myaddr, 'myvalue'); + $this->assertTrue($l1->exists($myaddr)); + $l1->delete(2, $myaddr); + $this->assertFalse($l1->exists($myaddr)); + } + + public function testPoolIDs() + { + // Test unique ID generation. + $this->assertNotNull($this->createL1()->getPool()); + + // Test host-based generation. + $_SERVER['SERVER_ADDR'] = 'localhost'; + $_SERVER['SERVER_PORT'] = '80'; + $this->assertEquals('localhost-80', $this->createL1()->getPool()); + } } diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index fe523a4..865acb8 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -125,6 +125,7 @@ public function testNewPoolSynchronization() protected function performTombstoneTest($l1) { + // This test is not for L1 - this tests integratino logick. $central = new Integrated($l1, new StaticL2()); $dne = new Address('mypool', 'mykey-dne'); @@ -489,33 +490,6 @@ public function testEmptyCleanUpDatabaseL2() $l2 = new DatabaseL2($this->dbh); } - protected function performExistsTest($l1) - { - $myaddr = new Address('mybin', 'mykey'); - $l1->set(1, $myaddr, 'myvalue'); - $this->assertTrue($l1->exists($myaddr)); - $l1->delete(2, $myaddr); - $this->assertFalse($l1->exists($myaddr)); - } - - public function testExistsAPCuL1() - { - $l1 = $this->l1Factory()->create('apcu', 'first'); - $this->performExistsTest($l1); - } - - public function testExistsStaticL1() - { - $l1 = $this->l1Factory()->create('static'); - $this->performExistsTest($l1); - } - - public function testExistsSQLiteL1() - { - $l1 = $this->l1Factory()->create('sqlite'); - $this->performExistsTest($l1); - } - public function testExistsIntegrated() { $this->createSchema(); @@ -538,19 +512,6 @@ public function testDatabaseL2Prefix() $this->assertEquals('myvalue', $l2->get($myaddr)); } - public function testAPCuL1PoolIDs() - { - // Test unique ID generation. - $l1 = $this->l1Factory()->create('apcu'); - $this->assertNotNull($l1->getPool()); - - // Test host-based generation. - $_SERVER['SERVER_ADDR'] = 'localhost'; - $_SERVER['SERVER_PORT'] = '80'; - $l1 = $this->l1Factory()->create('apcu'); - $this->assertEquals('localhost-80', $l1->getPool()); - } - protected function performL1HitMissTest($l1) { $myaddr = new Address('mybin', 'mykey'); From 2bccef6476e6214568680358e0acd0c6a7071f43 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 17:22:11 +0200 Subject: [PATCH 18/24] Refacored L1 hit/miss test. --- tests/L1CacheTest.php | 15 +++++++++++++++ tests/LCacheTest.php | 31 ------------------------------- 2 files changed, 15 insertions(+), 31 deletions(-) diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 6e72b7b..2c24406 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -158,4 +158,19 @@ public function testPoolIDs() $_SERVER['SERVER_PORT'] = '80'; $this->assertEquals('localhost-80', $this->createL1()->getPool()); } + + public function testHitMiss() + { + $event_id = 1; + $l1 = $this->createL1(); + $myaddr = new Address('mybin', 'mykey'); + list($hits, $misses) = [$l1->getHits(), $l1->getMisses()]; + + $l1->get($myaddr); + $this->assertEquals($misses + 1, $l1->getMisses()); + + $l1->set($event_id++, $myaddr, 'myvalue'); + $l1->get($myaddr); + $this->assertEquals($hits + 1, $l1->getHits()); + } } diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 865acb8..7d07aaa 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -512,37 +512,6 @@ public function testDatabaseL2Prefix() $this->assertEquals('myvalue', $l2->get($myaddr)); } - protected function performL1HitMissTest($l1) - { - $myaddr = new Address('mybin', 'mykey'); - $current_hits = $l1->getHits(); - $current_misses = $l1->getMisses(); - $current_event_id = 1; - $l1->get($myaddr); - $this->assertEquals($current_misses + 1, $l1->getMisses()); - $l1->set($current_event_id++, $myaddr, 'myvalue'); - $l1->get($myaddr); - $this->assertEquals($current_hits + 1, $l1->getHits()); - } - - public function testStaticL1HitMiss() - { - $l1 = $this->l1Factory()->create('static'); - $this->performL1HitMissTest($l1); - } - - public function testAPCuL1HitMiss() - { - $l1 = $this->l1Factory()->create('apcu', 'testAPCuL1HitMiss'); - $this->performL1HitMissTest($l1); - } - - public function testSQLiteL1HitMiss() - { - $l1 = $this->l1Factory()->create('sqlite'); - $this->performL1HitMissTest($l1); - } - public function testPoolIntegrated() { $l2 = new StaticL2(); From 0596626a8773e195e94d3c39b227f7346b5eb167 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 17:41:19 +0200 Subject: [PATCH 19/24] Refactored the testSQLiteL1SchemaErrorHandling to be triggered for all L1 drivers. Had to Refactor StaticL1 for that. --- src/StaticL1.php | 10 +++++++++- tests/L1CacheTest.php | 15 +++++++++++++++ tests/LCacheTest.php | 17 ----------------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/StaticL1.php b/src/StaticL1.php index 08d2413..e83e3ce 100644 --- a/src/StaticL1.php +++ b/src/StaticL1.php @@ -4,7 +4,11 @@ class StaticL1 extends L1 { + private static $_cacheData = []; + protected $key_overhead; + + /** @var array Reference to the data array for the instance data pool. */ protected $storage; public function __construct($pool, StateL1Interface $state) @@ -12,7 +16,11 @@ public function __construct($pool, StateL1Interface $state) parent::__construct($pool, $state); $this->key_overhead = []; - $this->storage = array(); + + if (!isset(self::$_cacheData[$this->pool])) { + self::$_cacheData[$this->pool] = []; + } + $this->storage = &self::$_cacheData[$this->pool]; } public function getKeyOverhead(Address $address) diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 2c24406..9e76aa1 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -159,6 +159,21 @@ public function testPoolIDs() $this->assertEquals('localhost-80', $this->createL1()->getPool()); } + public function testPoolSharing() + { + $value = 'myvalue'; + $myaddr = new Address('mybin', 'mykey'); + $poolName = uniqid('', true) . '-' . mt_rand(); + + $l1_a = $this->createL1($poolName); + // Opening a second instance of the same pool should work. + $l1_b = $this->createL1($poolName); + + $l1_a->set(1, $myaddr, $value); + // Reading from the second handle should show the same value. + $this->assertEquals($value, $l1_b->get($myaddr)); + } + public function testHitMiss() { $event_id = 1; diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 7d07aaa..86d3442 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -785,23 +785,6 @@ public function testDatabaseL2BatchDeletion() $this->assertNull($l2->get($myaddr)); } - public function testSQLiteL1SchemaErrorHandling() - { - $pool_name = uniqid('', true) . '-' . mt_rand(); - $l1_a = $this->l1Factory()->create('sqlite', $pool_name); - - // Opening a second instance of the same pool should work. - $l1_b = $this->l1Factory()->create('sqlite', $pool_name); - - $myaddr = new Address('mybin', 'mykey'); - - $l1_a->set(1, $myaddr, 'myvalue'); - - // Reading from the second handle should show the value written to the - // first. - $this->assertEquals('myvalue', $l1_b->get($myaddr)); - } - public function testDatabaseL2CleanupAfterWrite() { $this->createSchema(); From 006bfcb2cc975e18a905d3c06598083baf290106 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 18:00:06 +0200 Subject: [PATCH 20/24] Added key-overhead tracking tests on L1 level. --- tests/L1CacheTest.php | 21 +++++++++++++++++++++ tests/LCacheTest.php | 6 +++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 9e76aa1..5150de2 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -188,4 +188,25 @@ public function testHitMiss() $l1->get($myaddr); $this->assertEquals($hits + 1, $l1->getHits()); } + + public function testStateStorage() + { + $event_id = 1; + $l1 = $this->createL1(); + $myaddr = new Address('mybin', 'mykey'); + + $this->assertEquals(0, $l1->getKeyOverhead($myaddr)); + $l1->set($event_id++, $myaddr, 'myvalue'); + $this->assertEquals(1, $l1->getKeyOverhead($myaddr)); + $l1->get($myaddr); + $this->assertEquals(0, $l1->getKeyOverhead($myaddr)); + $l1->set($event_id++, $myaddr, 'myvalue2'); + $this->assertEquals(1, $l1->getKeyOverhead($myaddr)); + + // An unknown get should create negative overhead, generally + // in anticipation of a set. + $myaddr2 = new Address('mybin', 'mykey2'); + $l1->get($myaddr2); + $this->assertEquals(-1, $l1->getKeyOverhead($myaddr2)); + } } diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 86d3442..33bb789 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -646,6 +646,11 @@ public function testStaticL2GarbageCollection() $this->assertEquals(1, $l2->countGarbage()); } + /** + * @todo Is this still needed, or it can be deleted. + * Same tests are implemented against all L1 drivers directly in + * L1CacheTest::testStateStorage(). + */ protected function performHitSetCounterTest($l1) { $pool = new Integrated($l1, new StaticL2()); @@ -666,7 +671,6 @@ protected function performHitSetCounterTest($l1) $this->assertEquals(-1, $l1->getKeyOverhead($myaddr2)); } - public function testStaticL1Counters() { $this->performHitSetCounterTest($this->l1Factory()->create('static')); From 73a8a6b0d962a589d58265244a2aae3b913f9383 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 18:25:54 +0200 Subject: [PATCH 21/24] Added existence and validity checks for running the APCu tests. --- tests/L1/APCuTest.php | 15 +++++++++++++++ tests/LCacheTest.php | 1 + 2 files changed, 16 insertions(+) diff --git a/tests/L1/APCuTest.php b/tests/L1/APCuTest.php index 27f56dc..e872be3 100644 --- a/tests/L1/APCuTest.php +++ b/tests/L1/APCuTest.php @@ -14,6 +14,21 @@ */ class APCuTest extends \LCache\L1CacheTest { + + protected function setUp() + { + $run_test = false; + if (function_exists('apcu_store')) { + $value_to_store = 'test-value-' . rand(0, PHP_INT_MAX); + apcu_store('test_key', $value_to_store); + $run_test = apcu_fetch('test_key') === $value_to_store; + } + + if (!$run_test) { + $this->markTestSkipped('The APCu extension is not installed, enabled (for the CLI), or functional.'); + } + } + /** * {@inheritDoc} */ diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 33bb789..0ab525e 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -45,6 +45,7 @@ protected function createSchema($prefix = '') public function testL1Factory() { + // TODO: Move to L1CacheTest. $staticL1 = $this->l1Factory()->create('static'); $invalidL1 = $this->l1Factory()->create('invalid_cache_driver'); $this->assertEquals(get_class($staticL1), get_class($invalidL1)); From 5ba363a3f20021278d8cf69a9bd027d014fd0809 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 18:59:06 +0200 Subject: [PATCH 22/24] CS fixes & re-formatting. --- tests/L1CacheTest.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/L1CacheTest.php b/tests/L1CacheTest.php index 5150de2..4095471 100644 --- a/tests/L1CacheTest.php +++ b/tests/L1CacheTest.php @@ -34,18 +34,18 @@ abstract protected function driverName(); * @return L1 * One of the L1 concrete descendants. */ - protected function createL1($pool = null) { + protected function createL1($pool = null) + { return (new L1CacheFactory())->create($this->driverName(), $pool); } public function testSetGetDelete() { - $l1 = $this->createL1(); - $event_id = 1; - + $l1 = $this->createL1(); $myaddr = new Address('mybin', 'mykey'); + // Validate emptyness. $this->assertEquals(0, $l1->getHits()); $this->assertEquals(0, $l1->getMisses()); @@ -165,13 +165,12 @@ public function testPoolSharing() $myaddr = new Address('mybin', 'mykey'); $poolName = uniqid('', true) . '-' . mt_rand(); - $l1_a = $this->createL1($poolName); - // Opening a second instance of the same pool should work. - $l1_b = $this->createL1($poolName); + // Initialize a value in cache. + $this->createL1($poolName)->set(1, $myaddr, $value); - $l1_a->set(1, $myaddr, $value); + // Opening a second instance of the same pool should work. // Reading from the second handle should show the same value. - $this->assertEquals($value, $l1_b->get($myaddr)); + $this->assertEquals($value, $this->createL1($poolName)->get($myaddr)); } public function testHitMiss() From d011c69fac0a26023f1bd0c9fb6ca0579c82721d Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 22:09:47 +0200 Subject: [PATCH 23/24] Fixed the CRs from review. Added tests folder under CS validation in composer. --- composer.json | 2 +- src/L1CacheFactory.php | 10 +++++----- src/NullL1.php | 9 ++++++++- src/StateL1APCu.php | 7 +++---- src/StateL1Null.php | 40 ---------------------------------------- src/StaticL1.php | 8 ++++---- 6 files changed, 21 insertions(+), 55 deletions(-) delete mode 100644 src/StateL1Null.php diff --git a/composer.json b/composer.json index af0f43a..643c3f7 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "squizlabs/php_codesniffer": "2.*" }, "scripts": { - "cs": "phpcs --standard=PSR2 -n src", + "cs": "phpcs --standard=PSR2 -n src tests", "cbf": "phpcbf --standard=PSR2 -n src", "api": "PATH=$HOME/bin:$PATH sami.phar --ansi update sami-config.php", "sami-install": "mkdir -p $HOME/bin && curl --output $HOME/bin/sami.phar http://get.sensiolabs.org/sami.phar && chmod +x $HOME/bin/sami.phar", diff --git a/src/L1CacheFactory.php b/src/L1CacheFactory.php index f1d1d82..251da20 100644 --- a/src/L1CacheFactory.php +++ b/src/L1CacheFactory.php @@ -50,7 +50,7 @@ public function create($driverName = null, $customPool = null) * @param string $pool * @return \LCache\APCuL1 */ - protected function createApcu($pool) + protected function createAPCu($pool) { return new APCuL1($pool, new StateL1APCu($pool)); } @@ -63,7 +63,7 @@ protected function createApcu($pool) */ protected function createNull($pool) { - return new NullL1($pool, new StateL1Null()); + return new NullL1($pool, new StateL1Static()); } /** @@ -78,16 +78,16 @@ protected function createStatic($pool) } /** - * Factory method for the L1 static driver. + * Factory method for the L1 SQLite driver. * * @param string $pool * @return \LCache\SQLiteL1 */ - protected function createSqlite($pool) + protected function createSQLite($pool) { $hasApcu = function_exists('apcu_fetch'); // TODO: Maybe implement StateL1SQLite class instead of NULL one. - $state = $hasApcu ? new StateL1APCu("sqlite-$pool") : new StateL1Null(); + $state = $hasApcu ? new StateL1APCu("sqlite-$pool") : new StateL1Static(); $cache = new SQLiteL1($pool, $state); return $cache; } diff --git a/src/NullL1.php b/src/NullL1.php index daae096..74688f7 100644 --- a/src/NullL1.php +++ b/src/NullL1.php @@ -6,7 +6,14 @@ class NullL1 extends StaticL1 { public function setWithExpiration($event_id, Address $address, $value, $created, $expiration = null) { - // Store nothing; always succeed. + // Store nothing and always succeed. return true; } + + public function getLastAppliedEventID() + { + // Because we store nothing locally. + // Behave as if all events are applied. + return PHP_INT_MAX; + } } diff --git a/src/StateL1APCu.php b/src/StateL1APCu.php index 3fd253b..f2da36f 100644 --- a/src/StateL1APCu.php +++ b/src/StateL1APCu.php @@ -47,7 +47,7 @@ public function __construct($pool) */ public function recordHit() { - return $this->_recordEvent($this->statusKeyHits); + return $this->recordEvent($this->statusKeyHits); } /** @@ -55,7 +55,7 @@ public function recordHit() */ public function recordMiss() { - return $this->_recordEvent($this->statusKeyMisses); + return $this->recordEvent($this->statusKeyMisses); } /** @@ -67,7 +67,7 @@ public function recordMiss() * @return bool * True on success, false otherwise. */ - private function _recordEvent($key) + private function recordEvent($key) { $success = null; apcu_inc($key, 1, $success); @@ -127,6 +127,5 @@ public function clear() { apcu_store($this->statusKeyHits, 0); apcu_store($this->statusKeyMisses, 0); - // TODO: Decide on how to handle the last applied event state on clear? } } diff --git a/src/StateL1Null.php b/src/StateL1Null.php deleted file mode 100644 index 913b75b..0000000 --- a/src/StateL1Null.php +++ /dev/null @@ -1,40 +0,0 @@ -key_overhead = []; - if (!isset(self::$_cacheData[$this->pool])) { - self::$_cacheData[$this->pool] = []; + if (!isset(self::$cacheData[$this->pool])) { + self::$cacheData[$this->pool] = []; } - $this->storage = &self::$_cacheData[$this->pool]; + $this->storage = &self::$cacheData[$this->pool]; } public function getKeyOverhead(Address $address) From 60af8a851b5cfca13bc37f7e3634f3676130eed5 Mon Sep 17 00:00:00 2001 From: Nikolay Dobromirov Date: Tue, 27 Dec 2016 22:37:18 +0200 Subject: [PATCH 24/24] Implemented a separate test class for the NullL1 --- tests/L1/NullTest.php | 63 +++++++++++++++++++++++++++++++++++++++++++ tests/LCacheTest.php | 16 ----------- 2 files changed, 63 insertions(+), 16 deletions(-) create mode 100644 tests/L1/NullTest.php diff --git a/tests/L1/NullTest.php b/tests/L1/NullTest.php new file mode 100644 index 0000000..ffaa22f --- /dev/null +++ b/tests/L1/NullTest.php @@ -0,0 +1,63 @@ +createL1(); + $myaddr = new Address('mybin', 'mykey'); + + $cache->set(1, $myaddr, 'myvalue'); + + $this->assertNull($cache->get($myaddr)); + $this->assertEquals(0, $cache->getHits()); + $this->assertEquals(1, $cache->getMisses()); + } + + public function testStateStorage() + { + $lastEventId = $this->createL1()->getLastAppliedEventID(); + $this->assertEquals(PHP_INT_MAX, $lastEventId); + } + + public function testSetGetDelete() + { + // Not relevant for NullL1 class. + } + + public function testPreventRollback() + { + // Not relevant for NullL1 class. + } + + public function testExists() + { + // Not relevant for NullL1 class. + } + + public function testPoolSharing() + { + // Not relevant for NullL1 class. + } +} diff --git a/tests/LCacheTest.php b/tests/LCacheTest.php index 0ab525e..ce4812d 100644 --- a/tests/LCacheTest.php +++ b/tests/LCacheTest.php @@ -51,22 +51,6 @@ public function testL1Factory() $this->assertEquals(get_class($staticL1), get_class($invalidL1)); } - public function testNullL1() - { - $event_id = 1; - $cache = $this->l1Factory()->create('null'); - $myaddr = new Address('mybin', 'mykey'); - $cache->set($event_id++, $myaddr, 'myvalue'); - $entry = $cache->get($myaddr); - $this->assertNull($entry); - $this->assertEquals(0, $cache->getHits()); - $this->assertEquals(1, $cache->getMisses()); - - // Because this cache stores nothing it should be perpetually - // up-to-date. - $this->assertEquals(PHP_INT_MAX, $cache->getLastAppliedEventID()); - } - public function testClearStaticL2() { $l2 = new StaticL2();