diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index ef50d04..658099e 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -342,17 +342,8 @@ function install_begin_request(&$install_state) { module_list(NULL, $module_list); drupal_load('module', 'system'); - // Load the cache infrastructure using a "fake" cache implementation that - // does not attempt to write to the database. We need this during the initial - // part of the installer because the database is not available yet. We - // continue to use it even when the database does become available, in order - // to preserve consistency between interactive and command-line installations - // (the latter complete in one page request and therefore are forced to - // continue using the cache implementation they started with) and also - // because any data put in the cache during the installer is inherently - // suspect, due to the fact that Drupal is not fully set up yet. require_once DRUPAL_ROOT . '/core/includes/cache.inc'; - $conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\InstallBackend'); + $conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\NullBackend'); // The install process cannot use the database lock backend since the database // is not fully up, so we use a null backend implementation during the diff --git a/core/includes/install.inc b/core/includes/install.inc index 99cfcf6..a3045bf 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -422,6 +422,8 @@ function drupal_verify_profile($install_state) { function drupal_install_system() { // Create tables. drupal_install_schema('system'); + unset($GLOBALS['conf']['cache_classes']['cache']); + drupal_static_reset('cache'); // Immediately boot a kernel to have real services ready. $kernel = new DrupalKernel('install', FALSE, drupal_classloader(), FALSE); $kernel->boot(); diff --git a/core/includes/update.inc b/core/includes/update.inc index 299a81c..6102672 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -136,7 +136,10 @@ function update_prepare_d8_bootstrap() { } // Bootstrap the database. + $GLOBALS['conf']['cache_classes'] = array('cache' => 'Drupal\Core\Cache\NullBackend'); drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); + unset($GLOBALS['conf']['cache_classes']['cache']); + drupal_static_reset('cache'); // If the site has not updated to Drupal 8 yet, check to make sure that it is // running an up-to-date version of Drupal 7 before proceeding. Note this has @@ -195,6 +198,116 @@ function update_prepare_d8_bootstrap() { ); db_create_table('key_value', $specs); } + if (!db_table_exists('cache_tags')) { + $table = array( + 'description' => 'Cache table for tracking cache tags related to the cache bin.', + 'fields' => array( + 'tag' => array( + 'description' => 'Namespace-prefixed tag string.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'invalidations' => array( + 'description' => 'Number incremented when the tag is invalidated.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'deletions' => array( + 'description' => 'Number incremented when the tag is deleted.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'primary key' => array('tag'), + ); + db_create_table('cache_tags', $table); + } + if (!db_table_exists('cache_config')) { + $spec = array( + 'description' => 'Cache table for configuration data.', + 'fields' => array( + 'cid' => array( + 'description' => 'Primary Key: Unique cache ID.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'data' => array( + 'description' => 'A collection of data to cache.', + 'type' => 'blob', + 'not null' => FALSE, + 'size' => 'big', + ), + 'expire' => array( + 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'created' => array( + 'description' => 'A Unix timestamp indicating when the cache entry was created.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'serialized' => array( + 'description' => 'A flag to indicate whether content is serialized (1) or not (0).', + 'type' => 'int', + 'size' => 'small', + 'not null' => TRUE, + 'default' => 0, + ), + 'tags' => array( + 'description' => 'Space-separated list of cache tags for this entry.', + 'type' => 'text', + 'size' => 'big', + 'not null' => FALSE, + ), + 'checksum_invalidations' => array( + 'description' => 'The tag invalidation sum when this entry was saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + 'checksum_deletions' => array( + 'description' => 'The tag deletion sum when this entry was saved.', + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ), + ), + 'indexes' => array( + 'expire' => array('expire'), + ), + 'primary key' => array('cid'), + ); + db_create_table('cache_config', $spec); + } + + require_once DRUPAL_ROOT . '/core/modules/system/system.install'; + $tables = array( + 'cache', + 'cache_bootstrap', + 'cache_block', + 'cache_field', + 'cache_filter', + 'cache_form', + 'cache_image', + 'cache_menu', + 'cache_page', + 'cache_path', + 'cache_update', + ); + + foreach ($tables as $table) { + system_update_8007_add_tag_column($table); + } + // Bootstrap variables so we can update theme while preparing the update // process. drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); diff --git a/core/lib/Drupal/Core/Cache/CacheFactoryNull.php b/core/lib/Drupal/Core/Cache/CacheFactoryNull.php new file mode 100644 index 0000000..cedcf8e --- /dev/null +++ b/core/lib/Drupal/Core/Cache/CacheFactoryNull.php @@ -0,0 +1,9 @@ +bin is a - // variable, this means the call to ::query() here uses a concatenated - // string. This is highly discouraged under any other circumstances, and - // is used here only due to the performance overhead we would incur - // otherwise. When serving an uncached page, the overhead of using - // ::select() is a much smaller proportion of the request. - $result = Database::getConnection()->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . Database::getConnection()->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids)); - $cache = array(); - foreach ($result as $item) { - $item = $this->prepareItem($item, $allow_invalid); - if ($item) { - $cache[$item->cid] = $item; - } + // When serving cached pages, the overhead of using ::select() was found + // to add around 30% overhead to the request. Since $this->bin is a + // variable, this means the call to ::query() here uses a concatenated + // string. This is highly discouraged under any other circumstances, and + // is used here only due to the performance overhead we would incur + // otherwise. When serving an uncached page, the overhead of using + // ::select() is a much smaller proportion of the request. + $result = Database::getConnection()->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . Database::getConnection()->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids)); + $cache = array(); + foreach ($result as $item) { + $item = $this->prepareItem($item, $allow_invalid); + if ($item) { + $cache[$item->cid] = $item; } - $cids = array_diff($cids, array_keys($cache)); - return $cache; - } - catch (Exception $e) { - // If the database is never going to be available, cache requests should - // return FALSE in order to allow exception handling to occur. - return array(); } + $cids = array_diff($cids, array_keys($cache)); + return $cache; } /** @@ -131,34 +124,29 @@ protected function prepareItem($cache, $allow_invalid) { * Implements Drupal\Core\Cache\CacheBackendInterface::set(). */ public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) { - try { - $flat_tags = $this->flattenTags($tags); - $checksum = $this->checksumTags($flat_tags); - $fields = array( - 'serialized' => 0, - 'created' => REQUEST_TIME, - 'expire' => $expire, - 'tags' => implode(' ', $flat_tags), - 'checksum_invalidations' => $checksum['invalidations'], - 'checksum_deletions' => $checksum['deletions'], - ); - if (!is_string($data)) { - $fields['data'] = serialize($data); - $fields['serialized'] = 1; - } - else { - $fields['data'] = $data; - $fields['serialized'] = 0; - } - - Database::getConnection()->merge($this->bin) - ->key(array('cid' => $cid)) - ->fields($fields) - ->execute(); + $flat_tags = $this->flattenTags($tags); + $checksum = $this->checksumTags($flat_tags); + $fields = array( + 'serialized' => 0, + 'created' => REQUEST_TIME, + 'expire' => $expire, + 'tags' => implode(' ', $flat_tags), + 'checksum_invalidations' => $checksum['invalidations'], + 'checksum_deletions' => $checksum['deletions'], + ); + if (!is_string($data)) { + $fields['data'] = serialize($data); + $fields['serialized'] = 1; } - catch (Exception $e) { - // The database may not be available, so we'll ignore cache_set requests. + else { + $fields['data'] = $data; + $fields['serialized'] = 0; } + + Database::getConnection()->merge($this->bin) + ->key(array('cid' => $cid)) + ->fields($fields) + ->execute(); } /** diff --git a/core/lib/Drupal/Core/Cache/InstallBackend.php b/core/lib/Drupal/Core/Cache/InstallBackend.php deleted file mode 100644 index 91d0249..0000000 --- a/core/lib/Drupal/Core/Cache/InstallBackend.php +++ /dev/null @@ -1,187 +0,0 @@ - 'Cache install test', - 'description' => 'Confirm that the cache backend used for installing Drupal works correctly.', - 'group' => 'Cache', - ); - } - - /** - * Tests the behavior of the cache backend used for installing Drupal. - * - * While Drupal is being installed, the cache system must deal with the fact - * that the database is not initially available, and, after it is available, - * the fact that other requests that take place while Drupal is being - * installed (for example, Ajax requests triggered via the installer's user - * interface) may cache data in the database, which needs to be cleared when - * the installer makes changes that would result in it becoming stale. - * - * We cannot test this process directly, so instead we test it by switching - * between the normal database cache (Drupal\Core\Cache\DatabaseBackend) and - * the installer cache (Drupal\Core\Cache\InstallBackend) while setting and - * clearing various items in the cache. - */ - function testCacheInstall() { - $database_cache = new DatabaseBackend('test'); - $install_cache = new InstallBackend('test'); - - // Store an item in the database cache, and confirm that the installer's - // cache backend recognizes that the cache is not empty. - $database_cache->set('cache_one', 'One'); - $this->assertFalse($install_cache->isEmpty()); - $database_cache->delete('cache_one'); - $this->assertTrue($install_cache->isEmpty()); - - // Store an item in the database cache, then use the installer's cache - // backend to delete it. Afterwards, confirm that it is no longer in the - // database cache. - $database_cache->set('cache_one', 'One'); - $this->assertEqual($database_cache->get('cache_one')->data, 'One'); - $install_cache->delete('cache_one'); - $this->assertFalse($database_cache->get('cache_one')); - - // Store multiple items in the database cache, then use the installer's - // cache backend to delete them. Afterwards, confirm that they are no - // longer in the database cache. - $database_cache->set('cache_one', 'One'); - $database_cache->set('cache_two', 'Two'); - $this->assertEqual($database_cache->get('cache_one')->data, 'One'); - $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); - $install_cache->deleteMultiple(array('cache_one', 'cache_two')); - $this->assertFalse($database_cache->get('cache_one')); - $this->assertFalse($database_cache->get('cache_two')); - - // Store multiple items in the database cache, then use the installer's - // cache backend to flush the cache. Afterwards, confirm that they are no - // longer in the database cache. - $database_cache->set('cache_one', 'One'); - $database_cache->set('cache_two', 'Two'); - $this->assertEqual($database_cache->get('cache_one')->data, 'One'); - $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); - $install_cache->deleteAll(); - $this->assertFalse($database_cache->get('cache_one')); - $this->assertFalse($database_cache->get('cache_two')); - - // Invalidate a tag using the installer cache, then check that the - // invalidation was recorded correctly in the database. - $install_cache->invalidateTags(array('tag')); - $invalidations = db_query("SELECT invalidations FROM {cache_tags} WHERE tag = 'tag'")->fetchField(); - $this->assertEqual($invalidations, 1); - - // For each cache clearing event that we tried above, try it again after - // dropping the {cache_test} table. This simulates the early stages of the - // installer (when the database cache tables won't be available yet) and - // thereby confirms that the installer's cache backend does not produce - // errors if the installer ever calls any code early on that tries to clear - // items from the cache. - db_drop_table('cache_test'); - try { - $install_cache->isEmpty(); - $install_cache->delete('cache_one'); - $install_cache->deleteMultiple(array('cache_one', 'cache_two')); - $install_cache->deleteAll(); - $install_cache->deleteExpired(); - $install_cache->garbageCollection(); - $install_cache->invalidateTags(array('tag')); - $this->pass("The installer's cache backend can be used even when the cache database tables are unavailable."); - } - catch (Exception $e) { - $this->fail("The installer's cache backend can be used even when the cache database tables are unavailable."); - } - } -} diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 33df8c2..87997db 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -1608,66 +1608,7 @@ function system_update_8002() { * Creates {cache_config} cache table for the new configuration system. */ function system_update_8003() { - $spec = array( - 'description' => 'Cache table for configuration data.', - 'fields' => array( - 'cid' => array( - 'description' => 'Primary Key: Unique cache ID.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'data' => array( - 'description' => 'A collection of data to cache.', - 'type' => 'blob', - 'not null' => FALSE, - 'size' => 'big', - ), - 'expire' => array( - 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'created' => array( - 'description' => 'A Unix timestamp indicating when the cache entry was created.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'serialized' => array( - 'description' => 'A flag to indicate whether content is serialized (1) or not (0).', - 'type' => 'int', - 'size' => 'small', - 'not null' => TRUE, - 'default' => 0, - ), - 'tags' => array( - 'description' => 'Space-separated list of cache tags for this entry.', - 'type' => 'text', - 'size' => 'big', - 'not null' => FALSE, - ), - 'checksum_invalidations' => array( - 'description' => 'The tag invalidation sum when this entry was saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'checksum_deletions' => array( - 'description' => 'The tag deletion sum when this entry was saved.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'indexes' => array( - 'expire' => array('expire'), - ), - 'primary key' => array('cid'), - ); - db_create_table('cache_config', $spec); + // Moved to update_prepare_d8_bootstrap. } /** @@ -1715,32 +1656,7 @@ function system_update_8005() { * Adds the {cache_tags} table. */ function system_update_8006() { - $table = array( - 'description' => 'Cache table for tracking cache tags related to the cache bin.', - 'fields' => array( - 'tag' => array( - 'description' => 'Namespace-prefixed tag string.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'invalidations' => array( - 'description' => 'Number incremented when the tag is invalidated.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'deletions' => array( - 'description' => 'Number incremented when the tag is deleted.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('tag'), - ); - db_create_table('cache_tags', $table); + // Moved to update_prepare_d8_bootstrap. } /** @@ -1750,7 +1666,7 @@ function system_update_8006() { * Name of the cache table. */ function system_update_8007_add_tag_column($table) { - if (db_table_exists($table)) { + if (db_table_exists($table) && !db_field_exists($table, 'tags')) { db_add_field($table, 'tags', array( 'description' => 'Space-separated list of cache tags for this entry.', 'type' => 'text', @@ -1776,23 +1692,7 @@ function system_update_8007_add_tag_column($table) { * Modifies existing cache tables, adding support for cache tags. */ function system_update_8007() { - $tables = array( - 'cache', - 'cache_bootstrap', - 'cache_form', - 'cache_page', - 'cache_menu', - 'cache_path', - 'cache_filter', - 'cache_image', - 'cache_update', - 'cache_block', - 'cache_field', - ); - - foreach ($tables as $table) { - system_update_8007_add_tag_column($table); - } + // Moved to update_prepare_d8_bootstrap. } /**