diff --git a/core/core.services.yml b/core/core.services.yml
index b015208b669cb75601c558b9c6775b8f31c8b520..72bf10e04312d2764e474b7cda3f8e595c97687f 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -7,7 +7,7 @@ parameters:
 services:
   cache_factory:
     class: Drupal\Core\Cache\CacheFactory
-    arguments: ['@settings']
+    arguments: ['@settings', '%cache_default_bin_backends%']
     calls:
       - [setContainer, ['@service_container']]
   cache_contexts:
@@ -47,14 +47,14 @@ services:
   cache.bootstrap:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
-      - { name: cache.bin }
+      - { name: cache.bin, default_backend: cache.backend.chainedfast }
     factory_method: get
     factory_service: cache_factory
     arguments: [bootstrap]
   cache.config:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
-      - { name: cache.bin }
+      - { name: cache.bin, default_backend: cache.backend.chainedfast }
     factory_method: get
     factory_service: cache_factory
     arguments: [config]
@@ -96,7 +96,7 @@ services:
   cache.discovery:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags:
-      - { name: cache.bin }
+      - { name: cache.bin, default_backend: cache.backend.chainedfast }
     factory_method: get
     factory_service: cache_factory
     arguments: [discovery]
@@ -113,7 +113,8 @@ services:
     class: Drupal\Core\Config\ConfigInstaller
     arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher']
   config.storage:
-    alias: config.storage.active
+    class: Drupal\Core\Config\CachedStorage
+    arguments: ['@config.storage.active', '@cache.config']
   config.storage.active:
     class: Drupal\Core\Config\DatabaseStorage
     arguments: ['@database', 'config']
diff --git a/core/lib/Drupal/Core/Cache/CacheFactory.php b/core/lib/Drupal/Core/Cache/CacheFactory.php
index a3b5a01270fb57d3d91d2d4f41abd8e92e4159b1..0a0a977738f4e7099eee1616ee597a1e05abec7a 100644
--- a/core/lib/Drupal/Core/Cache/CacheFactory.php
+++ b/core/lib/Drupal/Core/Cache/CacheFactory.php
@@ -26,14 +26,31 @@ class CacheFactory implements CacheFactoryInterface,  ContainerAwareInterface {
    */
   protected $settings;
 
+  /**
+   * A map of cache bin to default cache backend service name.
+   *
+   * All mappings in $settings takes precedence over this, but this can be used
+   * to optimize cache storage for a Drupal installation without cache
+   * customizations in settings.php. For example, this can be used to map the
+   * 'bootstrap' bin to 'cache.backend.chainedfast', while allowing other bins
+   * to fall back to the global default of 'cache.backend.database'.
+   *
+   * @var array
+   */
+  protected $defaultBinBackends;
+
   /**
    * Constructs CacheFactory object.
    *
    * @param \Drupal\Core\Site\Settings $settings
    *   The settings array.
+   * @param array $default_bin_backends
+   *   (optional) A mapping of bin to backend service name. Mappings in
+   *   $settings take precedence over this.
    */
-  function __construct(Settings $settings) {
+  public function __construct(Settings $settings, array $default_bin_backends = array()) {
     $this->settings = $settings;
+    $this->defaultBinBackends = $default_bin_backends;
   }
 
   /**
@@ -59,6 +76,9 @@ public function get($bin) {
     elseif (isset($cache_settings['default'])) {
       $service_name = $cache_settings['default'];
     }
+    elseif (isset($this->defaultBinBackends[$bin])) {
+      $service_name = $this->defaultBinBackends[$bin];
+    }
     else {
       $service_name = 'cache.backend.database';
     }
diff --git a/core/lib/Drupal/Core/Cache/ChainedFastBackend.php b/core/lib/Drupal/Core/Cache/ChainedFastBackend.php
index 6a825af35a0b7f7039c27a4dff2df81ac10bd20b..5a9ede4b343dfb7a7091e94dc1c71649031363a7 100644
--- a/core/lib/Drupal/Core/Cache/ChainedFastBackend.php
+++ b/core/lib/Drupal/Core/Cache/ChainedFastBackend.php
@@ -19,9 +19,11 @@
  * Mecached or Redis, and will be used by all web nodes, thus making it
  * consistent, but also require a network round trip for each cache get.
  *
- * It is expected this backend will be used primarily on sites running on
- * multiple web nodes, as single-node configurations can just use the fast
- * cache backend directly.
+ * In addition to being useful for sites running on multiple web nodes, this
+ * backend can also be useful for sites running on a single web node where the
+ * fast backend (e.g., APCu) isn't shareable between the web and CLI processes.
+ * Single-node configurations that don't have that limitation can just use the
+ * fast cache backend directly.
  *
  * We always use the fast backend when reading (get()) entries from cache, but
  * check whether they were created before the last write (set()) to this
@@ -68,7 +70,7 @@ class ChainedFastBackend implements CacheBackendInterface {
   /**
    * The time at which the last write to this cache bin happened.
    *
-   * @var int
+   * @var float
    */
   protected $lastWriteTimestamp;
 
@@ -102,14 +104,47 @@ public function get($cid, $allow_invalid = FALSE) {
    * {@inheritdoc}
    */
   public function getMultiple(&$cids, $allow_invalid = FALSE) {
-    // Retrieve as many cache items as possible from the fast backend. (Some
-    // cache entries may have been created before the last write to this cache
-    // bin and therefore be stale/wrong/inconsistent.)
     $cids_copy = $cids;
     $cache = array();
+
+    // If we can determine the time at which the last write to the consistent
+    // backend occurred (we might not be able to if it has been recently
+    // flushed/restarted), then we can use that to validate items from the fast
+    // backend, so try to get those first. Otherwise, we can't assume that
+    // anything in the fast backend is valid, so don't even bother fetching
+    // from there.
     $last_write_timestamp = $this->getLastWriteTimestamp();
     if ($last_write_timestamp) {
-      foreach ($this->fastBackend->getMultiple($cids, $allow_invalid) as $item) {
+      // Items in the fast backend might be invalid based on their timestamp,
+      // but we can't check the timestamp prior to getting the item, which
+      // includes unserializing it. However, unserializing an invalid item can
+      // throw an exception. For example, a __wakeup() implementation that
+      // receives object properties containing references to code or data that
+      // no longer exists in the application's current state.
+      //
+      // Unserializing invalid data, whether it throws an exception or not, is
+      // a waste of time, but we only incur it while a cache invalidation has
+      // not yet finished propagating to all the fast backend instances.
+      //
+      // Most cache backend implementations should not wrap their internal
+      // get() implementations with a try/catch, because they have no reason to
+      // assume that their data is invalid, and doing so would mask
+      // unserialization errors of valid data. We do so here, only because the
+      // fast backend is non-authoritative, and after discarding its
+      // exceptions, we proceed to check the consistent (authoritative) backend
+      // and allow exceptions from that to bubble up.
+      try {
+        $items = $this->fastBackend->getMultiple($cids, $allow_invalid);
+      }
+      catch (\Exception $e) {
+        $cids = $cids_copy;
+        $items = array();
+      }
+
+      // Even if items were successfully fetched from the fast backend, they
+      // are potentially invalid if older than the last time the bin was
+      // written to in the consistent backend, so only keep ones that aren't.
+      foreach ($items as $item) {
         if ($item->created < $last_write_timestamp) {
           $cids[array_search($item->cid, $cids_copy)] = $item->cid;
         }
@@ -229,6 +264,13 @@ public function removeBin() {
     $this->fastBackend->removeBin();
   }
 
+  /**
+   * @todo Document in https://www.drupal.org/node/2311945.
+   */
+  public function reset() {
+    $this->lastWriteTimestamp = NULL;
+  }
+
   /**
    * Gets the last write timestamp.
    */
diff --git a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
index 8d8fcf43d4b29a3fb0d5f0303f44ec93bdba7a1d..c261b84b0bbf37d3fa90ea392013480721f03d33 100644
--- a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
+++ b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
@@ -6,11 +6,60 @@
  */
 
 namespace Drupal\Core\Cache;
+use Drupal\Core\Site\Settings;
+use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 
 /**
  * Defines the chained fast cache backend factory.
  */
-class ChainedFastBackendFactory extends CacheFactory {
+class ChainedFastBackendFactory implements CacheFactoryInterface {
+
+  use ContainerAwareTrait;
+
+  /**
+   * The service name of the consistent backend factory.
+   *
+   * @var string
+   */
+  protected $consistentServiceName;
+
+  /**
+   * The service name of the fast backend factory.
+   *
+   * @var string
+   */
+  protected $fastServiceName;
+
+  /**
+   * Constructs ChainedFastBackendFactory object.
+   *
+   * @param \Drupal\Core\Site\Settings|NULL $settings
+   *   (optional) The settings object.
+   * @param string|NULL $consistent_service_name
+   *   (optional) The service name of the consistent backend factory. Defaults
+   *   to:
+   *   - $settings->get('cache')['default'] (if specified)
+   *   - 'cache.backend.database' (if the above isn't specified)
+   * @param string|NULL $fast_service_name
+   *   (optional) The service name of the fast backend factory. Defaults to:
+   *   - 'cache.backend.apcu' (if the PHP process has APCu enabled)
+   *   - NULL (if the PHP process doesn't have APCu enabled)
+   */
+  public function __construct(Settings $settings = NULL, $consistent_service_name = NULL, $fast_service_name = NULL) {
+    // Default the consistent backend to the site's default backend.
+    if (!isset($consistent_service_name)) {
+      $cache_settings = isset($settings) ? $settings->get('cache') : array();
+      $consistent_service_name = isset($cache_settings['default']) ? $cache_settings['default'] : 'cache.backend.database';
+    }
+
+    // Default the fast backend to APCu if it's available.
+    if (!isset($fast_service_name) && function_exists('apc_fetch')) {
+      $fast_service_name = 'cache.backend.apcu';
+    }
+
+    $this->consistentServiceName = $consistent_service_name;
+    $this->fastServiceName = $fast_service_name;
+  }
 
   /**
    * Instantiates a chained, fast cache backend class for a given cache bin.
@@ -22,24 +71,18 @@ class ChainedFastBackendFactory extends CacheFactory {
    *   The cache backend object associated with the specified bin.
    */
   public function get($bin) {
-    $consistent_service = 'cache.backend.database';
-    $fast_service = 'cache.backend.apcu';
-
-    $cache_settings = $this->settings->get('cache');
-    if (isset($cache_settings['chained_fast_cache']) && is_array($cache_settings['chained_fast_cache'])) {
-      if (!empty($cache_settings['chained_fast_cache']['consistent'])) {
-        $consistent_service = $cache_settings['chained_fast_cache']['consistent'];
-      }
-      if (!empty($cache_settings['chained_fast_cache']['fast'])) {
-        $fast_service = $cache_settings['chained_fast_cache']['fast'];
-      }
+    // Use the chained backend only if there is a fast backend available;
+    // otherwise, just return the consistent backend directly.
+    if (isset($this->fastServiceName)) {
+      return new ChainedFastBackend(
+        $this->container->get($this->consistentServiceName)->get($bin),
+        $this->container->get($this->fastServiceName)->get($bin),
+        $bin
+      );
+    }
+    else {
+      return $this->container->get($this->consistentServiceName)->get($bin);
     }
-
-    return new ChainedFastBackend(
-      $this->container->get($consistent_service)->get($bin),
-      $this->container->get($fast_service)->get($bin),
-      $bin
-    );
   }
 
 }
diff --git a/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php b/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
index 5381186a15e5d832fe2f02a0ff5adf41fc5f688b..e9e715845f466ca954d26c3009ac266ce1ff23d8 100644
--- a/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
+++ b/core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
@@ -22,9 +22,15 @@ class ListCacheBinsPass implements CompilerPassInterface {
    */
   public function process(ContainerBuilder $container) {
     $cache_bins = array();
+    $cache_default_bin_backends = array();
     foreach ($container->findTaggedServiceIds('cache.bin') as $id => $attributes) {
-      $cache_bins[$id] = substr($id, strpos($id, '.') + 1);
+      $bin = substr($id, strpos($id, '.') + 1);
+      $cache_bins[$id] = $bin;
+      if (isset($attributes[0]['default_backend'])) {
+        $cache_default_bin_backends[$bin] = $attributes[0]['default_backend'];
+      }
     }
     $container->setParameter('cache_bins', $cache_bins);
+    $container->setParameter('cache_default_bin_backends', $cache_default_bin_backends);
   }
 }
diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php
index db5d598877f33c021c9a8faac432adb53c00eb6c..0b089ca4f139025c4729e3b0d4bd2d18ad5a4fa2 100644
--- a/core/lib/Drupal/Core/Config/CachedStorage.php
+++ b/core/lib/Drupal/Core/Config/CachedStorage.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 
 /**
  * Defines the cached storage.
@@ -18,6 +19,7 @@
  * handles cache invalidation.
  */
 class CachedStorage implements StorageInterface, StorageCacheInterface {
+  use DependencySerializationTrait;
 
   /**
    * The configuration storage to be cached.
diff --git a/core/modules/config/src/Tests/ConfigOtherModuleTest.php b/core/modules/config/src/Tests/ConfigOtherModuleTest.php
index 1cfdc1ae373761054421bcd7c755806a5d5503c0..ba497ab53d7614a36d27f14386120031d7886237 100644
--- a/core/modules/config/src/Tests/ConfigOtherModuleTest.php
+++ b/core/modules/config/src/Tests/ConfigOtherModuleTest.php
@@ -16,24 +16,11 @@
  */
 class ConfigOtherModuleTest extends WebTestBase {
 
-  /**
-   * @var \Drupal\Core\Extension\ModuleHandler
-   */
-  protected $moduleHandler;
-
-  /**
-   * Sets up the module handler for enabling and disabling modules.
-   */
-  protected function setUp() {
-    parent::setUp();
-    $this->moduleHandler = $this->container->get('module_handler');
-  }
-
   /**
    * Tests enabling the provider of the default configuration first.
    */
   public function testInstallOtherModuleFirst() {
-    $this->moduleHandler->install(array('config_other_module_config_test'));
+    $this->installModule('config_other_module_config_test');
 
     // Check that the config entity doesn't exist before the config_test module
     // is enabled. We cannot use the entity system because the config_test
@@ -43,18 +30,18 @@ public function testInstallOtherModuleFirst() {
 
     // Install the module that provides the entity type. This installs the
     // default configuration.
-    $this->moduleHandler->install(array('config_test'));
+    $this->installModule('config_test');
     $this->assertTrue(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration has been installed.');
 
     // Uninstall the module that provides the entity type. This will remove the
     // default configuration.
-    $this->moduleHandler->uninstall(array('config_test'));
+    $this->uninstallModule('config_test');
     $config = $this->container->get('config.factory')->get('config_test.dynamic.other_module_test');
     $this->assertTrue($config->isNew(), 'Default configuration for other modules is removed when the config entity provider is disabled.');
 
     // Install the module that provides the entity type again. This installs the
     // default configuration.
-    $this->moduleHandler->install(array('config_test'));
+    $this->installModule('config_test');
     $other_module_config_entity = entity_load('config_test', 'other_module_test', TRUE);
     $this->assertTrue($other_module_config_entity, "Default configuration has been recreated.");
 
@@ -64,14 +51,14 @@ public function testInstallOtherModuleFirst() {
     $other_module_config_entity->save();
 
     // Uninstall the module that provides the default configuration.
-    $this->moduleHandler->uninstall(array('config_other_module_config_test'));
+    $this->uninstallModule('config_other_module_config_test');
     $this->assertTrue(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration for other modules is not removed when the module that provides it is uninstalled.');
 
     // Default configuration provided by config_test should still exist.
     $this->assertTrue(entity_load('config_test', 'dotted.default', TRUE), 'The configuration is not deleted.');
 
     // Re-enable module to test that default config is unchanged.
-    $this->moduleHandler->install(array('config_other_module_config_test'));
+    $this->installModule('config_other_module_config_test');
     $config_entity = entity_load('config_test', 'other_module_test', TRUE);
     $this->assertEqual($config_entity->get('style'), "The piano ain't got no wrong notes.", 'Re-enabling the module does not install default config over the existing config entity.');
   }
@@ -80,10 +67,10 @@ public function testInstallOtherModuleFirst() {
    * Tests enabling the provider of the config entity type first.
    */
   public function testInstallConfigEnityModuleFirst() {
-    $this->moduleHandler->install(array('config_test'));
+    $this->installModule('config_test');
     $this->assertFalse(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration provided by config_other_module_config_test does not exist.');
 
-    $this->moduleHandler->install(array('config_other_module_config_test'));
+    $this->installModule('config_other_module_config_test');
     $this->assertTrue(entity_load('config_test', 'other_module_test', TRUE), 'Default configuration provided by config_other_module_config_test has been installed.');
   }
 
@@ -91,12 +78,34 @@ public function testInstallConfigEnityModuleFirst() {
    * Tests uninstalling Node module removes views which are dependent on it.
    */
   public function testUninstall() {
-    $this->moduleHandler->install(array('views'));
+    $this->installModule('views');
     $this->assertTrue(entity_load('view', 'frontpage', TRUE) === NULL, 'After installing Views, frontpage view which is dependant on the Node and Views modules does not exist.');
-    $this->moduleHandler->install(array('node'));
+    $this->installModule('node');
     $this->assertTrue(entity_load('view', 'frontpage', TRUE) !== NULL, 'After installing Node, frontpage view which is dependant on the Node and Views modules exists.');
-    $this->moduleHandler->uninstall(array('node'));
+    $this->uninstallModule('node');
     $this->assertTrue(entity_load('view', 'frontpage', TRUE) === NULL, 'After uninstalling Node, frontpage view which is dependant on the Node and Views modules does not exist.');
   }
 
+  /**
+   * Installs a module.
+   *
+   * @param string $module
+   *   The module name.
+   */
+  protected function installModule($module) {
+    $this->container->get('module_handler')->install(array($module));
+    $this->container = \Drupal::getContainer();
+  }
+
+  /**
+   * Uninstalls a module.
+   *
+   * @param string $module
+   *   The module name.
+   */
+  protected function uninstallModule($module) {
+    $this->container->get('module_handler')->uninstall(array($module));
+    $this->container = \Drupal::getContainer();
+  }
+
 }
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index e3ab65791035187e6132d238b3e85fb4adaa350d..ea15a3201cf2e22891135e5b0c83b972f9d97b2a 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -257,7 +257,7 @@ public function containerBuild(ContainerBuilder $container) {
     $container->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory');
 
     $container
-      ->register('config.storage.active', 'Drupal\Core\Config\DatabaseStorage')
+      ->register('config.storage', 'Drupal\Core\Config\DatabaseStorage')
       ->addArgument(Database::getConnection())
       ->addArgument('config');
 
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 5dd62c2bfe3b9974f18e8585bf0e04e55045e3f2..c1fe6f70a4f8e6aeab297b553099e9c524c7b442 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -12,6 +12,7 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\String;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\DependencyInjection\YamlFileLoader;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
@@ -1171,9 +1172,17 @@ protected function resetAll() {
    */
   protected function refreshVariables() {
     // Clear the tag cache.
+    // @todo Replace drupal_static() usage within classes and provide a
+    //   proper interface for invoking reset() on a cache backend:
+    //   https://www.drupal.org/node/2311945.
     drupal_static_reset('Drupal\Core\Cache\CacheBackendInterface::tagCache');
     drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::deletedTags');
     drupal_static_reset('Drupal\Core\Cache\DatabaseBackend::invalidatedTags');
+    foreach (Cache::getBins() as $backend) {
+      if (is_callable(array($backend, 'reset'))) {
+        $backend->reset();
+      }
+    }
 
     $this->container->get('config.factory')->reset();
     $this->container->get('state')->resetCache();