From d41e394f183673788190617e945a4693fb957874 Mon Sep 17 00:00:00 2001 From: Bob Vincent Date: Fri, 16 Mar 2012 17:38:03 -0400 Subject: [PATCH] Issue #1015946 by Damien Tournoud, catch, pillarsdotnet: Eliminate $user->cache and {session}.cache in favor of $_SESSION[cache_expiration][$bin] --- core/includes/bootstrap.inc | 1 - core/includes/session.inc | 1 - core/lib/Drupal/Core/Cache/DatabaseBackend.php | 47 ++++++++++++++++-------- core/modules/simpletest/tests/cache.test | 16 ++++++++ core/modules/system/system.install | 13 ++++--- 5 files changed, 55 insertions(+), 23 deletions(-) diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 7869daa66779b76811b644c7f0b7cb4989b56dbe..033122aadb97c6e0f121c4414cebaecbef3cabb8 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -1956,7 +1956,6 @@ function drupal_anonymous_user() { $user->hostname = ip_address(); $user->roles = array(); $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; - $user->cache = 0; return $user; } diff --git a/core/includes/session.inc b/core/includes/session.inc index 2d03a1efc91114b5306155386a41e97a29582d14..b07997cc619c3e0da737c407ed4e9589f8c35e5e 100644 --- a/core/includes/session.inc +++ b/core/includes/session.inc @@ -176,7 +176,6 @@ function _drupal_session_write($sid, $value) { // Either ssid or sid or both will be added from $key below. $fields = array( 'uid' => $user->uid, - 'cache' => isset($user->cache) ? $user->cache : 0, 'hostname' => ip_address(), 'session' => $value, 'timestamp' => REQUEST_TIME, diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index 73c71392bc50a35c8c7792639490bd9a6ea19369..9a1458003a185aaadcc89449448665da88581340 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -95,18 +95,17 @@ class DatabaseBackend implements CacheBackendInterface { if (!isset($cache->data)) { return FALSE; } - // If enforcing a minimum cache lifetime, validate that the data is - // currently valid for this user before we return it by making sure the cache - // entry was created before the timestamp in the current session's cache - // timer. The cache variable is loaded into the $user object by - // _drupal_session_read() in session.inc. If the data is permanent or we're - // not enforcing a minimum cache lifetime always return the cached data. + // If the cached data is temporary and subject to a per-user minimum + // lifetime, compare the cache entry timestamp with the user session + // cache_expiration timestamp. If the cache entry is too old, ignore it. $config = config('system.performance'); - if ($cache->expire != CACHE_PERMANENT && $config->get('cache_lifetime') && $user->cache > $cache->created) { - // This cache data is too old and thus not valid for us, ignore it. + if ($cache->expire != CACHE_PERMANENT && $config->get('cache_lifetime') && isset($_SESSION['cache_expiration'][$this->bin]) && $_SESSION['cache_expiration'][$this->bin] > $cache->created) { + // Ignore cache data that is too old and thus not valid for this user. return FALSE; } + // If the data is permanent or not subject to a minimum cache lifetime, + // unserialize and return the cached data. if ($cache->serialized) { $cache->data = unserialize($cache->data); } @@ -186,11 +185,10 @@ class DatabaseBackend implements CacheBackendInterface { */ function expire() { if (variable_get('cache_lifetime', 0)) { - // We store the time in the current user's $user->cache variable which - // will be saved into the sessions bin by _drupal_session_write(). We then - // simulate that the cache was flushed for this user by not returning - // cached data that was cached before the timestamp. - $GLOBALS['user']->cache = REQUEST_TIME; + // We store the time in the current user's session. We then simulate + // that the cache was flushed for this user by not returning cached + // data that was cached before the timestamp. + $_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME; $cache_flush = variable_get('cache_flush_' . $this->bin, 0); if ($cache_flush == 0) { @@ -220,12 +218,31 @@ class DatabaseBackend implements CacheBackendInterface { * Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection(). */ function garbageCollection() { - global $user; + $cache_lifetime = variable_get('cache_lifetime', 0); + // Clean-up the per-user cache expiration session data, so that the session + // handler can properly clean-up the session data for anonymous users. + if (isset($_SESSION['cache_expiration'])) { + $expire = REQUEST_TIME - $cache_lifetime; + foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) { + if ($timestamp < $expire) { + unset($_SESSION['cache_expiration'][$bin]); + } + } + if (!$_SESSION['cache_expiration']) { + unset($_SESSION['cache_expiration']); + } + } + + // Garbage collection of temporary items is only necessary when enforcing + // a minimum cache lifetime. + if (!$cache_lifetime) { + return; + } // When cache lifetime is in force, avoid running garbage collection too // often since this will remove temporary cache items indiscriminately. $cache_flush = variable_get('cache_flush_' . $this->bin, 0); - if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= REQUEST_TIME)) { + if ($cache_flush && ($cache_flush + $cache_lifetime <= REQUEST_TIME)) { // Reset the variable immediately to prevent a meltdown in heavy load situations. variable_set('cache_flush_' . $this->bin, 0); // Time to flush old cache data diff --git a/core/modules/simpletest/tests/cache.test b/core/modules/simpletest/tests/cache.test index 81f237725631d3776ae11cde5ede5a6b8db8b906..1a7eaada036593a59483c241d178c720da521b35 100644 --- a/core/modules/simpletest/tests/cache.test +++ b/core/modules/simpletest/tests/cache.test @@ -170,6 +170,22 @@ class CacheSavingCase extends CacheTestCase { $this->drupalGet('user/register'); $this->assertFalse(cache()->get(''), t('No cache entry is written with an empty cid.')); } + + /** + * Tests whether a newly-logged-in user can use the cache. + */ + function testNewUser() { + // Set a minimum/maximum cache lifetime to trigger the bug. + $this->setupLifetime(300); + // Login as a newly-created user. + $account = $this->drupalCreateUser(array()); + $this->drupalLogin($account); + // Try to set and retrieve a cache object. + $data = $this->randomName(100); + cache()->set($data, $data, CACHE_TEMPORARY); + $cached = cache()->get($data); + $this->assertTrue(isset($cached->data) && $cached->data === $data, t('Caching works for newly-logged-in user.')); + } } /** diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 2cbb6154f15d99bb9faf0c55e3731327bcf132aa..679ff708102e7638d32335346fe2c5c4e410fde4 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1506,12 +1506,6 @@ function system_schema() { 'not null' => TRUE, 'default' => 0, ), - 'cache' => array( - 'description' => "The time of this user's last post. This is used when the site has specified a minimum_cache_lifetime. See Drupal\Core\Cache\CacheBackendInterface::get().", - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), 'session' => array( 'description' => 'The serialized contents of $_SESSION, an array of name/value pairs that persists across page requests by this session ID. Drupal loads $_SESSION from here at the start of each request and saves it at the end.', 'type' => 'blob', @@ -1760,6 +1754,13 @@ function system_update_8004() { } } + /** + * Remove the obsolete {session}.cache column. + */ +function system_update_8005() { + db_drop_field('session', 'cache'); +} + /** * @} End of "defgroup updates-7.x-to-8.x" * The next series of updates should start at 9000. -- 1.7.5.4