diff --git a/core/lib/Drupal/Core/Cache/StaticCache/LruStaticCache.php b/core/lib/Drupal/Core/Cache/StaticCache/LruStaticCache.php index 7762c70..e4c7693 100644 --- a/core/lib/Drupal/Core/Cache/StaticCache/LruStaticCache.php +++ b/core/lib/Drupal/Core/Cache/StaticCache/LruStaticCache.php @@ -6,9 +6,11 @@ /** - * Defines a static cache implementation. + * Defines a least recently used (LRU) static cache implementation. * - * Stores cache items in memory using a PHP array. + * Stores cache items in memory using a PHP array. The number of cache items is + * limited to a fixed number of slots. When the all slots are full, older items + * are purged based on least recent usage. * * @ingroup cache */ @@ -68,16 +70,22 @@ public function get($cid, $allow_invalid = FALSE) { * {@inheritdoc} */ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) { - // If there are still available slots, decrement the counter. - if ($this->availableSlots) { - $this->availableSlots--; - } - // Otherwise remove the least recently used item. - else { + // If there are no available slots, and none of the slots are already used + // by this cache ID, remove the least recently used item. + $current_position = array_search($cid, $this->positions); + if (!$this->availableSlots && $current_position === FALSE) { $key = array_shift($this->positions); unset($this->cache[$key]); + $this->availableSlots++; + } + // Setting an already-set item moves it to the most recently used spot but + // does not take up any extra slots. + if ($current_position !== FALSE) { + unset($this->positions[$current_position]); + } + else { + $this->availableSlots--; } - // Add this item to the most recently used slot. $this->positions[] = $cid; return parent::set($cid, $data, $expire, $tags); } @@ -86,9 +94,11 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array * {@inheritdoc} */ public function delete($cid) { - $this->availableSlots++; $current_position = array_search($cid, $this->positions); - unset($this->positions[$current_position]); + if ($current_position !== FALSE) { + $this->availableSlots++; + unset($this->positions[$current_position]); + } parent::delete($cid); } diff --git a/core/tests/Drupal/Tests/Core/Cache/LruStaticCacheTest.php b/core/tests/Drupal/Tests/Core/Cache/LruStaticCacheTest.php index 9c8fdb7..d4e93d0 100644 --- a/core/tests/Drupal/Tests/Core/Cache/LruStaticCacheTest.php +++ b/core/tests/Drupal/Tests/Core/Cache/LruStaticCacheTest.php @@ -44,6 +44,29 @@ public function testGetSetDelete() { $cids[2] = ['crow', FALSE]; $cids[5] = ['bigger_cuckoo', 'bigger_cuckoo']; $this->assertCids($cids); + + // Confirm that setting the same item multiple times only uses one slot. + $this->staticCache->set('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->set('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->set('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->set('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->set('bigger_cuckoo', 'bigger_cuckoo'); + $this->assertCids($cids); + + // Confirm that deleting the same item multiple times only frees up one + // slot. + $this->staticCache->delete('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->delete('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->delete('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->delete('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->delete('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->delete('bigger_cuckoo', 'bigger_cuckoo'); + $this->staticCache->set('bigger_cuckoo', 'bigger_cuckoo'); + $this->assertCids($cids); + $this->staticCache->set('crow', 'crow'); + $cids[2] = ['crow', 'crow']; + $cids[1] = ['pidgie', FALSE]; + $this->assertCids($cids); } public function testInvalidate() {