diff --git a/composer.json b/composer.json
index 727e031..c55a9a3 100644
--- a/composer.json
+++ b/composer.json
@@ -8,7 +8,7 @@
         "wikimedia/composer-merge-plugin": "~1.3"
     },
     "replace": {
-        "drupal/core": "~8.2"
+        "drupal/core": "~8.3"
     },
     "minimum-stability": "dev",
     "prefer-stable": true,
diff --git a/composer.lock b/composer.lock
index 8d2b325..d1a3af0 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
         "This file is @generated automatically"
     ],
-    "hash": "7d101b08e5ae002d827cd42ae9a4e344",
-    "content-hash": "60f7057617c6d995bf9946d0b12f0b5d",
+    "hash": "64b08387a4402f685cc35b1ad9197380",
+    "content-hash": "0e7de9d6c3256344615aad4b059a850a",
     "packages": [
         {
             "name": "asm89/stack-cors",
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 5d1588d..f62e914 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -81,7 +81,7 @@ class Drupal {
   /**
    * The current system version.
    */
-  const VERSION = '8.2.0-dev';
+  const VERSION = '8.3.0-dev';
 
   /**
    * Core API compatibility.
diff --git a/core/lib/Drupal/Core/Config/DatabaseStorage.php b/core/lib/Drupal/Core/Config/DatabaseStorage.php
index 4e0c184..ad5a645 100644
--- a/core/lib/Drupal/Core/Config/DatabaseStorage.php
+++ b/core/lib/Drupal/Core/Config/DatabaseStorage.php
@@ -181,7 +181,7 @@ protected function ensureTableExists()  {
   /**
    * Defines the schema for the configuration table.
    */
-  protected static function schemaDefinition() {
+  public static function schemaDefinition() {
     $schema = array(
       'description' => 'The base table for configuration data.',
       'fields' => array(
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index 08e7e19..72460a2 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -323,12 +323,14 @@ public function preSave(EntityStorageInterface $storage) {
 
     // Ensure this entity's UUID does not exist with a different ID, regardless
     // of whether it's new or updated.
-    $matching_entities = $storage->getQuery()
-      ->condition('uuid', $this->uuid())
-      ->execute();
-    $matched_entity = reset($matching_entities);
-    if (!empty($matched_entity) && ($matched_entity != $this->id()) && $matched_entity != $this->getOriginalId()) {
-      throw new ConfigDuplicateUUIDException("Attempt to save a configuration entity '{$this->id()}' with UUID '{$this->uuid()}' when this UUID is already used for '$matched_entity'");
+    if ($uuid = $this->uuid()) {
+      $matching_entities = $storage->getQuery()
+        ->condition('uuid', $uuid)
+        ->execute();
+      $matched_entity = reset($matching_entities);
+      if (!empty($matched_entity) && ($matched_entity != $this->id()) && $matched_entity != $this->getOriginalId()) {
+        throw new ConfigDuplicateUUIDException("Attempt to save a configuration entity '{$this->id()}' with UUID '$uuid' when this UUID is already used for '$matched_entity'");
+      }
     }
 
     // If this entity is not new, load the original entity for comparison.
diff --git a/core/lib/Drupal/Core/Field/FieldItemInterface.php b/core/lib/Drupal/Core/Field/FieldItemInterface.php
index a257dd0..14cf9e9 100644
--- a/core/lib/Drupal/Core/Field/FieldItemInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemInterface.php
@@ -98,7 +98,7 @@ public function getEntity();
   /**
    * Gets the langcode of the field values held in the object.
    *
-   * @return string
+   * @return $langcode
    *   The langcode.
    */
   public function getLangcode();
diff --git a/core/lib/Drupal/Core/Field/FieldItemListInterface.php b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
index 2266c9c..e8bba02 100644
--- a/core/lib/Drupal/Core/Field/FieldItemListInterface.php
+++ b/core/lib/Drupal/Core/Field/FieldItemListInterface.php
@@ -44,7 +44,7 @@ public function setLangcode($langcode);
   /**
    * Gets the langcode of the field values held in the object.
    *
-   * @return string
+   * @return $langcode
    *   The langcode.
    */
   public function getLangcode();
diff --git a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php
index aca4f31..569d807 100644
--- a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php
+++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorage.php
@@ -5,6 +5,7 @@
 use Drupal\Component\Serialization\SerializationInterface;
 use Drupal\Core\Database\Query\Merge;
 use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\SchemaObjectExistsException;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 
 /**
@@ -61,10 +62,16 @@ public function __construct($collection, SerializationInterface $serializer, Con
    * {@inheritdoc}
    */
   public function has($key) {
-    return (bool) $this->connection->query('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :key', array(
-      ':collection' => $this->collection,
-      ':key' => $key,
-    ))->fetchField();
+    try {
+      return (bool) $this->connection->query('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :key', array(
+        ':collection' => $this->collection,
+        ':key' => $key,
+      ))->fetchField();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+      return FALSE;
+    }
   }
 
   /**
@@ -84,6 +91,7 @@ public function getMultiple(array $keys) {
       // @todo: Perhaps if the database is never going to be available,
       // key/value requests should return FALSE in order to allow exception
       // handling to occur but for now, keep it an array, always.
+      $this->catchException($e);
     }
     return $values;
   }
@@ -92,7 +100,14 @@ public function getMultiple(array $keys) {
    * {@inheritdoc}
    */
   public function getAll() {
-    $result = $this->connection->query('SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection));
+    try {
+      $result = $this->connection->query('SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection', array(':collection' => $this->collection));
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+      $result = [];
+    }
+
     $values = array();
 
     foreach ($result as $item) {
@@ -107,40 +122,75 @@ public function getAll() {
    * {@inheritdoc}
    */
   public function set($key, $value) {
-    $this->connection->merge($this->table)
-      ->keys(array(
-        'name' => $key,
-        'collection' => $this->collection,
-      ))
-      ->fields(array('value' => $this->serializer->encode($value)))
-      ->execute();
+    $try_again = FALSE;
+    try {
+      $this->connection->merge($this->table)
+        ->keys(array(
+          'name' => $key,
+          'collection' => $this->collection,
+        ))
+        ->fields(array('value' => $this->serializer->encode($value)))
+        ->execute();
+    }
+    catch (\Exception $e) {
+      // If there was an exception, try to create the table.
+      if (!$try_again = $this->ensureTableExists()) {
+        // If the exception happened for other reason than the missing bin
+        // table, propagate the exception.
+        throw $e;
+      }
+    }
+    // Now that the bin has been created, try again if necessary.
+    if ($try_again) {
+      $this->set($key, $value);
+    }
   }
 
   /**
    * {@inheritdoc}
    */
   public function setIfNotExists($key, $value) {
-    $result = $this->connection->merge($this->table)
-      ->insertFields(array(
-        'collection' => $this->collection,
-        'name' => $key,
-        'value' => $this->serializer->encode($value),
-      ))
-      ->condition('collection', $this->collection)
-      ->condition('name', $key)
-      ->execute();
-    return $result == Merge::STATUS_INSERT;
+    $try_again = FALSE;
+    try {
+      $result = $this->connection->merge($this->table)
+        ->insertFields(array(
+          'collection' => $this->collection,
+          'name' => $key,
+          'value' => $this->serializer->encode($value),
+        ))
+        ->condition('collection', $this->collection)
+        ->condition('name', $key)
+        ->execute();
+      return $result == Merge::STATUS_INSERT;
+    }
+    catch (\Exception $e) {
+      // If there was an exception, try to create the table.
+      if (!$try_again = $this->ensureTableExists()) {
+        // If the exception happened for other reason than the missing bin
+        // table, propagate the exception.
+        throw $e;
+      }
+    }
+    // Now that the bin has been created, try again if necessary.
+    if ($try_again) {
+      return $this->setIfNotExists($key, $value);
+    }
   }
 
   /**
    * {@inheritdoc}
    */
   public function rename($key, $new_key) {
-    $this->connection->update($this->table)
-      ->fields(array('name' => $new_key))
-      ->condition('collection', $this->collection)
-      ->condition('name', $key)
-      ->execute();
+    try {
+      $this->connection->update($this->table)
+        ->fields(array('name' => $new_key))
+        ->condition('collection', $this->collection)
+        ->condition('name', $key)
+        ->execute();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
   }
 
   /**
@@ -149,20 +199,98 @@ public function rename($key, $new_key) {
   public function deleteMultiple(array $keys) {
     // Delete in chunks when a large array is passed.
     while ($keys) {
+      try {
+        $this->connection->delete($this->table)
+          ->condition('name', array_splice($keys, 0, 1000), 'IN')
+          ->condition('collection', $this->collection)
+          ->execute();
+      }
+      catch (\Exception $e) {
+        $this->catchException($e);
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteAll() {
+    try {
       $this->connection->delete($this->table)
-        ->condition('name', array_splice($keys, 0, 1000), 'IN')
         ->condition('collection', $this->collection)
         ->execute();
     }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
   }
 
   /**
-   * {@inheritdoc}
+   * Check if the table exists and create it if not.
    */
-  public function deleteAll() {
-    $this->connection->delete($this->table)
-      ->condition('collection', $this->collection)
-      ->execute();
+  protected function ensureTableExists() {
+    try {
+      $database_schema = $this->connection->schema();
+      if (!$database_schema->tableExists($this->table)) {
+        $database_schema->createTable($this->table, $this->schemaDefinition());
+        return TRUE;
+      }
+    }
+    // If the table already exists, then attempting to recreate it will throw an
+    // exception. In this case just catch the exception and do nothing.
+    catch (SchemaObjectExistsException $e) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+  /**
+   * Act on an exception when the table might not have been created.
+   *
+   * If the table does not yet exist, that's fine, but if the table exists and
+   * something else cause the exception, then propagate it.
+   *
+   * @param \Exception $e
+   *   The exception.
+   *
+   * @throws \Exception
+   */
+  protected function catchException(\Exception $e) {
+    if ($this->connection->schema()->tableExists($this->table)) {
+      throw $e;
+    }
+  }
+
+  /**
+   * Defines the schema for the key_value table.
+   */
+  public static function schemaDefinition() {
+    return [
+      'description' => 'Generic key-value storage table. See the state system for an example.',
+      'fields' => [
+        'collection' => [
+          'description' => 'A named collection of key and value pairs.',
+          'type' => 'varchar_ascii',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+        ],
+        'name' => [
+          'description' => 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.',
+          'type' => 'varchar_ascii',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+        ],
+        'value' => [
+          'description' => 'The value.',
+          'type' => 'blob',
+          'not null' => TRUE,
+          'size' => 'big',
+        ],
+      ],
+      'primary key' => ['collection', 'name'],
+    ];
   }
 
 }
diff --git a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php
index 9f3b428..64397f2 100644
--- a/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php
+++ b/core/lib/Drupal/Core/KeyValueStore/DatabaseStorageExpirable.php
@@ -34,71 +34,122 @@ public function __construct($collection, SerializationInterface $serializer, Con
    * {@inheritdoc}
    */
   public function has($key) {
-    return (bool) $this->connection->query('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :key AND expire > :now', array(
-      ':collection' => $this->collection,
-      ':key' => $key,
-      ':now' => REQUEST_TIME,
-    ))->fetchField();
+    try {
+      return (bool) $this->connection->query('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND name = :key AND expire > :now', array(
+        ':collection' => $this->collection,
+        ':key' => $key,
+        ':now' => REQUEST_TIME,
+      ))->fetchField();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+      return FALSE;
+    }
   }
 
   /**
    * {@inheritdoc}
    */
   public function getMultiple(array $keys) {
-    $values = $this->connection->query(
-      'SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE expire > :now AND name IN ( :keys[] ) AND collection = :collection',
-      array(
-        ':now' => REQUEST_TIME,
-        ':keys[]' => $keys,
-        ':collection' => $this->collection,
-      ))->fetchAllKeyed();
-    return array_map(array($this->serializer, 'decode'), $values);
+    try {
+      $values = $this->connection->query(
+        'SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE expire > :now AND name IN ( :keys[] ) AND collection = :collection',
+        array(
+          ':now' => REQUEST_TIME,
+          ':keys[]' => $keys,
+          ':collection' => $this->collection,
+        ))->fetchAllKeyed();
+      return array_map(array($this->serializer, 'decode'), $values);
+    }
+    catch (\Exception $e) {
+      // @todo: Perhaps if the database is never going to be available,
+      // key/value requests should return FALSE in order to allow exception
+      // handling to occur but for now, keep it an array, always.
+      $this->catchException($e);
+    }
+    return [];
   }
 
   /**
    * {@inheritdoc}
    */
   public function getAll() {
-    $values = $this->connection->query(
-      'SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND expire > :now',
-      array(
-        ':collection' => $this->collection,
-        ':now' => REQUEST_TIME
-      ))->fetchAllKeyed();
-    return array_map(array($this->serializer, 'decode'), $values);
+    try {
+      $values = $this->connection->query(
+        'SELECT name, value FROM {' . $this->connection->escapeTable($this->table) . '} WHERE collection = :collection AND expire > :now',
+        array(
+          ':collection' => $this->collection,
+          ':now' => REQUEST_TIME
+        ))->fetchAllKeyed();
+      return array_map(array($this->serializer, 'decode'), $values);
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+    return [];
   }
 
   /**
    * {@inheritdoc}
    */
   function setWithExpire($key, $value, $expire) {
-    $this->connection->merge($this->table)
-      ->keys(array(
-        'name' => $key,
-        'collection' => $this->collection,
-      ))
-      ->fields(array(
-        'value' => $this->serializer->encode($value),
-        'expire' => REQUEST_TIME + $expire,
-      ))
-      ->execute();
+    $try_again = FALSE;
+    try {
+      $this->connection->merge($this->table)
+        ->keys(array(
+          'name' => $key,
+          'collection' => $this->collection,
+        ))
+        ->fields(array(
+          'value' => $this->serializer->encode($value),
+          'expire' => REQUEST_TIME + $expire,
+        ))
+        ->execute();
+    }
+    catch (\Exception $e) {
+      // If there was an exception, try to create the table.
+      if (!$try_again = $this->ensureTableExists()) {
+        // If the exception happened for other reason than the missing bin
+        // table, propagate the exception.
+        throw $e;
+      }
+    }
+    // Now that the bin has been created, try again if necessary.
+    if ($try_again) {
+      $this->setWithExpire($key, $value, $expire);
+    }
   }
 
   /**
    * {@inheritdoc}
    */
   function setWithExpireIfNotExists($key, $value, $expire) {
-    $result = $this->connection->merge($this->table)
-      ->insertFields(array(
-        'collection' => $this->collection,
-        'name' => $key,
-        'value' => $this->serializer->encode($value),
-        'expire' => REQUEST_TIME + $expire,
-      ))
-      ->condition('collection', $this->collection)
-      ->condition('name', $key)
-      ->execute();
-    return $result == Merge::STATUS_INSERT;
+    $try_again = FALSE;
+    try {
+      $result = $this->connection->merge($this->table)
+        ->insertFields(array(
+          'collection' => $this->collection,
+          'name' => $key,
+          'value' => $this->serializer->encode($value),
+          'expire' => REQUEST_TIME + $expire,
+        ))
+        ->condition('collection', $this->collection)
+        ->condition('name', $key)
+        ->execute();
+      return $result == Merge::STATUS_INSERT;
+    }
+    catch (\Exception $e) {
+      // If there was an exception, try to create the table.
+      if (!$try_again = $this->ensureTableExists()) {
+        // If the exception happened for other reason than the missing bin
+        // table, propagate the exception.
+        throw $e;
+      }
+    }
+    // Now that the bin has been created, try again if necessary.
+    if ($try_again) {
+      return $this->setWithExpireIfNotExists($key, $value, $expire);
+    }
   }
 
   /**
@@ -117,4 +168,47 @@ public function deleteMultiple(array $keys) {
     parent::deleteMultiple($keys);
   }
 
+  /**
+   * Defines the schema for the key_value_expire table.
+   */
+  public static function schemaDefinition() {
+    return [
+      'description' => 'Generic key/value storage table with an expiration.',
+      'fields' => [
+        'collection' => [
+          'description' => 'A named collection of key and value pairs.',
+          'type' => 'varchar_ascii',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+        ],
+        'name' => [
+          // KEY is an SQL reserved word, so use 'name' as the key's field name.
+          'description' => 'The key of the key/value pair.',
+          'type' => 'varchar_ascii',
+          'length' => 128,
+          'not null' => TRUE,
+          'default' => '',
+        ],
+        'value' => [
+          'description' => 'The value of the key/value pair.',
+          'type' => 'blob',
+          'not null' => TRUE,
+          'size' => 'big',
+        ],
+        'expire' => [
+          'description' => 'The time since Unix epoch in seconds when this item expires. Defaults to the maximum possible time.',
+          'type' => 'int',
+          'not null' => TRUE,
+          'default' => 2147483647,
+        ],
+      ],
+      'primary key' => ['collection', 'name'],
+      'indexes' => [
+        'all' => ['name', 'collection', 'expire'],
+        'expire' => ['expire'],
+      ],
+    ];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseExpirableFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseExpirableFactory.php
index 5670fe5..1f0822d 100644
--- a/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseExpirableFactory.php
+++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueDatabaseExpirableFactory.php
@@ -58,9 +58,31 @@ public function get($collection) {
    * Deletes expired items.
    */
   public function garbageCollection() {
-    $this->connection->delete('key_value_expire')
-      ->condition('expire', REQUEST_TIME, '<')
-      ->execute();
+    try {
+      $this->connection->delete('key_value_expire')
+        ->condition('expire', REQUEST_TIME, '<')
+        ->execute();
+    }
+    catch (\Exception $e) {
+      $this->catchException($e);
+    }
+  }
+
+  /**
+   * Act on an exception when the table might not have been created.
+   *
+   * If the table does not yet exist, that's fine, but if the table exists and
+   * something else cause the exception, then propagate it.
+   *
+   * @param \Exception $e
+   *   The exception.
+   *
+   * @throws \Exception
+   */
+  protected function catchException(\Exception $e) {
+    if ($this->connection->schema()->tableExists('key_value_expire')) {
+      throw $e;
+    }
   }
 
 }
diff --git a/core/modules/dblog/tests/src/Kernel/DbLogFormInjectionTest.php b/core/modules/dblog/tests/src/Kernel/DbLogFormInjectionTest.php
index 569cdbf..a997096 100644
--- a/core/modules/dblog/tests/src/Kernel/DbLogFormInjectionTest.php
+++ b/core/modules/dblog/tests/src/Kernel/DbLogFormInjectionTest.php
@@ -79,7 +79,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
   protected function setUp() {
     parent::setUp();
     $this->installSchema('dblog', ['watchdog']);
-    $this->installSchema('system', ['key_value_expire', 'sequences']);
+    $this->installSchema('system', ['sequences']);
     $this->installEntitySchema('user');
     $this->logger = \Drupal::logger('test_logger');
     $test_user = User::create(array(
diff --git a/core/modules/editor/tests/src/Kernel/EditorImageDialogTest.php b/core/modules/editor/tests/src/Kernel/EditorImageDialogTest.php
index 02aa876..533c961 100644
--- a/core/modules/editor/tests/src/Kernel/EditorImageDialogTest.php
+++ b/core/modules/editor/tests/src/Kernel/EditorImageDialogTest.php
@@ -36,7 +36,6 @@ class EditorImageDialogTest extends EntityKernelTestBase {
   protected function setUp() {
     parent::setUp();
     $this->installEntitySchema('file');
-    $this->installSchema('system', ['key_value_expire']);
     $this->installSchema('node', array('node_access'));
     $this->installSchema('file', array('file_usage'));
     $this->installConfig(['node']);
diff --git a/core/modules/field/tests/src/Kernel/FieldKernelTestBase.php b/core/modules/field/tests/src/Kernel/FieldKernelTestBase.php
index a780895..c19ae12 100644
--- a/core/modules/field/tests/src/Kernel/FieldKernelTestBase.php
+++ b/core/modules/field/tests/src/Kernel/FieldKernelTestBase.php
@@ -47,7 +47,7 @@ protected function setUp() {
 
     $this->installEntitySchema('entity_test');
     $this->installEntitySchema('user');
-    $this->installSchema('system', ['sequences', 'key_value']);
+    $this->installSchema('system', ['sequences']);
 
     // Set default storage backend and configure the theme system.
     $this->installConfig(array('field', 'system'));
diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php
index b82d5a8..7c302f2 100644
--- a/core/modules/file/src/Entity/File.php
+++ b/core/modules/file/src/Entity/File.php
@@ -188,11 +188,7 @@ public static function preCreate(EntityStorageInterface $storage, array &$values
   public function preSave(EntityStorageInterface $storage) {
     parent::preSave($storage);
 
-    // The file itself might not exist or be available right now.
-    $uri = $this->getFileUri();
-    if ($size = @filesize($uri)) {
-      $this->setSize($size);
-    }
+    $this->setSize(filesize($this->getFileUri()));
   }
 
   /**
diff --git a/core/modules/file/src/Tests/FileManagedFileElementTest.php b/core/modules/file/src/Tests/FileManagedFileElementTest.php
index 07102a0..f5d5be3 100644
--- a/core/modules/file/src/Tests/FileManagedFileElementTest.php
+++ b/core/modules/file/src/Tests/FileManagedFileElementTest.php
@@ -171,27 +171,4 @@ public function testManagedFileRemoved() {
     $this->assertRaw('The file referenced by the Managed <em>file &amp; butter</em> field does not exist.');
   }
 
-  /**
-   * Ensure a file entity can be saved when the file does not exist on disk.
-   */
-  public function testFileRemovedFromDisk() {
-    $this->drupalGet('file/test/1/0/1');
-    $test_file = $this->getTestFile('text');
-    $file_field_name = 'files[nested_file][]';
-
-    $edit = [$file_field_name => drupal_realpath($test_file->getFileUri())];
-    $this->drupalPostForm(NULL, $edit, t('Upload'));
-    $this->drupalPostForm(NULL, array(), t('Save'));
-
-    $fid = $this->getLastFileId();
-    /** @var $file \Drupal\file\FileInterface */
-    $file = $this->container->get('entity_type.manager')->getStorage('file')->load($fid);
-    $file->setPermanent();
-    $file->save();
-    $this->assertTrue(file_unmanaged_delete($file->getFileUri()));
-    $file->save();
-    $this->assertTrue($file->isPermanent());
-    $file->delete();
-  }
-
 }
diff --git a/core/modules/language/src/ContentLanguageSettingsInterface.php b/core/modules/language/src/ContentLanguageSettingsInterface.php
index 7e1211d..3bb3d5d 100644
--- a/core/modules/language/src/ContentLanguageSettingsInterface.php
+++ b/core/modules/language/src/ContentLanguageSettingsInterface.php
@@ -39,7 +39,7 @@ public function setTargetBundle($target_bundle);
    * @param string $default_langcode
    *   The default language code.
    *
-   * @return $this
+   * @return $this;
    */
   public function setDefaultLangcode($default_langcode);
 
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 5cf42dd..dfb0272 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -36,6 +36,9 @@
  */
 class EntityResource extends ResourceBase implements DependentPluginInterface {
 
+  use EntityResourceValidationTrait;
+  use EntityResourceAccessTrait;
+
   /**
    * The entity type targeted by this resource.
    *
@@ -156,14 +159,7 @@ public function post(EntityInterface $entity = NULL) {
       throw new BadRequestHttpException('Only new entities can be created');
     }
 
-    // Only check 'edit' permissions for fields that were actually
-    // submitted by the user. Field access makes no difference between 'create'
-    // and 'update', so the 'edit' operation is used here.
-    foreach ($entity->_restSubmittedFields as $key => $field_name) {
-      if (!$entity->get($field_name)->access('edit')) {
-        throw new AccessDeniedHttpException("Access denied on creating field '$field_name'");
-      }
-    }
+    $this->checkEditFieldAccess($entity);
 
     // Validate the received data before saving.
     $this->validate($entity);
@@ -175,8 +171,7 @@ public function post(EntityInterface $entity = NULL) {
       // body. These responses are not cacheable, so we add no cacheability
       // metadata here.
       $url = $entity->urlInfo('canonical', ['absolute' => TRUE])->toString(TRUE);
-      $response = new ModifiedResourceResponse($entity, 201, ['Location' => $url->getGeneratedUrl()]);
-      return $response;
+      return new ModifiedResourceResponse($entity, 201, ['Location' => $url->getGeneratedUrl()]);
     }
     catch (EntityStorageException $e) {
       throw new HttpException(500, 'Internal Server Error', $e);
@@ -277,39 +272,6 @@ public function delete(EntityInterface $entity) {
   }
 
   /**
-   * Verifies that the whole entity does not violate any validation constraints.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   *
-   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
-   *   If validation errors are found.
-   */
-  protected function validate(EntityInterface $entity) {
-    // @todo Remove when https://www.drupal.org/node/2164373 is committed.
-    if (!$entity instanceof FieldableEntityInterface) {
-      return;
-    }
-    $violations = $entity->validate();
-
-    // Remove violations of inaccessible fields as they cannot stem from our
-    // changes.
-    $violations->filterByFieldAccess();
-
-    if (count($violations) > 0) {
-      $message = "Unprocessable Entity: validation failed.\n";
-      foreach ($violations as $violation) {
-        $message .= $violation->getPropertyPath() . ': ' . $violation->getMessage() . "\n";
-      }
-      // Instead of returning a generic 400 response we use the more specific
-      // 422 Unprocessable Entity code from RFC 4918. That way clients can
-      // distinguish between general syntax errors in bad serializations (code
-      // 400) and semantic errors in well-formed requests (code 422).
-      throw new HttpException(422, $message);
-    }
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function permissions() {
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResourceAccessTrait.php b/core/modules/rest/src/Plugin/rest/resource/EntityResourceAccessTrait.php
new file mode 100644
index 0000000..7bf8e82
--- /dev/null
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResourceAccessTrait.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Drupal\rest\Plugin\rest\resource;
+
+use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+/**
+ * @internal
+ * @todo Consider making public in https://www.drupal.org/node/2300677
+ */
+trait EntityResourceAccessTrait {
+
+  /**
+   * Performs edit access checks for fields.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity whose fields edit access should be checked for.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   *   Throws access denied when the user does not have permissions to edit a
+   *   field.
+   */
+  protected function checkEditFieldAccess(EntityInterface $entity) {
+    // Only check 'edit' permissions for fields that were actually submitted by
+    // the user. Field access makes no difference between 'create' and 'update',
+    // so the 'edit' operation is used here.
+    foreach ($entity->_restSubmittedFields as $key => $field_name) {
+      if (!$entity->get($field_name)->access('edit')) {
+        throw new AccessDeniedHttpException("Access denied on creating field '$field_name'.");
+      }
+    }
+  }
+
+}
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php b/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php
new file mode 100644
index 0000000..a2ff40a
--- /dev/null
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResourceValidationTrait.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Drupal\rest\Plugin\rest\resource;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
+
+/**
+ * @internal
+ * @todo Consider making public in https://www.drupal.org/node/2300677
+ */
+trait EntityResourceValidationTrait {
+
+  /**
+   * Verifies that the whole entity does not violate any validation constraints.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to validate.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException
+   *   If validation errors are found.
+   */
+  protected function validate(EntityInterface $entity) {
+    // @todo Remove when https://www.drupal.org/node/2164373 is committed.
+    if (!$entity instanceof FieldableEntityInterface) {
+      return;
+    }
+    $violations = $entity->validate();
+
+    // Remove violations of inaccessible fields as they cannot stem from our
+    // changes.
+    $violations->filterByFieldAccess();
+
+    if ($violations->count() > 0) {
+      $message = "Unprocessable Entity: validation failed.\n";
+      foreach ($violations as $violation) {
+        $message .= $violation->getPropertyPath() . ': ' . $violation->getMessage() . "\n";
+      }
+      throw new UnprocessableEntityHttpException($message);
+    }
+  }
+
+}
diff --git a/core/modules/rest/tests/src/Unit/EntityResourceValidationTraitTest.php b/core/modules/rest/tests/src/Unit/EntityResourceValidationTraitTest.php
new file mode 100644
index 0000000..20a6175
--- /dev/null
+++ b/core/modules/rest/tests/src/Unit/EntityResourceValidationTraitTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Drupal\Tests\rest\Unit;
+
+use Drupal\Core\Entity\EntityConstraintViolationList;
+use Drupal\node\Entity\Node;
+use Drupal\Tests\UnitTestCase;
+use Drupal\user\Entity\User;
+use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
+use Symfony\Component\Validator\ConstraintViolationInterface;
+
+/**
+ * @group rest
+ * @coversDefaultClass \Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait
+ */
+class EntityResourceValidationTraitTest extends UnitTestCase {
+
+  /**
+   * @covers ::validate
+   */
+  public function testValidate() {
+    $trait = $this->getMockForTrait('Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait');
+
+    $method = new \ReflectionMethod($trait, 'validate');
+    $method->setAccessible(TRUE);
+
+    $entity = $this->prophesize(Node::class);
+
+    $violations = $this->prophesize(EntityConstraintViolationList::class);
+    $violations->filterByFieldAccess()->willReturn([]);
+    $violations->count()->willReturn(0);
+
+    $entity->validate()->willReturn($violations->reveal());
+
+    $method->invoke($trait, $entity->reveal());
+  }
+
+  /**
+   * @covers ::validate
+   */
+  public function testFailedValidate() {
+    $violation1 = $this->prophesize(ConstraintViolationInterface::class);
+    $violation1->getPropertyPath()->willReturn('property_path');
+    $violation1->getMessage()->willReturn('message');
+
+    $violation2 = $this->prophesize(ConstraintViolationInterface::class);
+    $violation2->getPropertyPath()->willReturn('property_path');
+    $violation2->getMessage()->willReturn('message');
+
+    $entity = $this->prophesize(User::class);
+
+    $violations = $this->getMockBuilder(EntityConstraintViolationList::class)
+      ->setConstructorArgs([$entity->reveal(), [$violation1->reveal(), $violation2->reveal()]])
+      ->setMethods(['filterByFieldAccess'])
+      ->getMock();
+
+    $violations->expects($this->once())
+      ->method('filterByFieldAccess')
+      ->will($this->returnValue([]));
+
+    $entity->validate()->willReturn($violations);
+
+    $trait = $this->getMockForTrait('Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait');
+
+    $method = new \ReflectionMethod($trait, 'validate');
+    $method->setAccessible(TRUE);
+
+    $this->setExpectedException(UnprocessableEntityHttpException::class);
+
+    $method->invoke($trait, $entity->reveal());
+  }
+
+}
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index da77a67..e8f24dc 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -2627,7 +2627,7 @@ protected function assertNoResponse($code, $message = '', $group = 'Browser') {
    * @param $override_server_vars
    *   An array of server variables to override.
    *
-   * @return \Symfony\Component\HttpFoundation\Request
+   * @return $request
    *   The mocked request object.
    */
   protected function prepareRequestForGenerator($clean_urls = TRUE, $override_server_vars = array()) {
diff --git a/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php b/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php
new file mode 100644
index 0000000..32d2872
--- /dev/null
+++ b/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php
@@ -0,0 +1,148 @@
+<?php
+
+namespace Drupal\statistics;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\State\StateInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Provides the default database storage backend for statistics.
+ */
+class NodeStatisticsDatabaseStorage implements StatisticsStorageInterface {
+
+  /**
+  * The database connection used.
+  *
+  * @var \Drupal\Core\Database\Connection
+  */
+  protected $connection;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * Constructs the statistics storage.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection for the node view storage.
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state service.
+   */
+  public function __construct(Connection $connection, StateInterface $state, RequestStack $request_stack) {
+    $this->connection = $connection;
+    $this->state = $state;
+    $this->requestStack = $request_stack;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function recordView($id) {
+    return (bool) $this->connection
+      ->merge('node_counter')
+      ->key('nid', $id)
+      ->fields([
+        'daycount' => 1,
+        'totalcount' => 1,
+        'timestamp' => $this->getRequestTime(),
+      ])
+      ->expression('daycount', 'daycount + 1')
+      ->expression('totalcount', 'totalcount + 1')
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetchViews($ids) {
+    $views = $this->connection
+      ->select('node_counter', 'nc')
+      ->fields('nc', ['totalcount', 'daycount', 'timestamp'])
+      ->condition('nid', $ids, 'IN')
+      ->execute()
+      ->fetchAll();
+    foreach ($views as $id => $view) {
+      $views[$id] = new StatisticsViewsResult($view->totalcount, $view->daycount, $view->timestamp);
+    }
+    return $views;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetchView($id) {
+    $views = $this->fetchViews(array($id));
+    return reset($views);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetchAll($order = 'totalcount', $limit = 5) {
+    assert(in_array($order, ['totalcount', 'daycount', 'timestamp']), "Invalid order argument.");
+
+    return $this->connection
+      ->select('node_counter', 'nc')
+      ->fields('nc', ['nid'])
+      ->orderBy($order, 'DESC')
+      ->range(0, $limit)
+      ->execute()
+      ->fetchCol();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteViews($id) {
+    return (bool) $this->connection
+      ->delete('node_counter')
+      ->condition('nid', $id)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function resetDayCount() {
+    $statistics_timestamp = $this->state->get('statistics.day_timestamp') ?: 0;
+    if (($this->getRequestTime() - $statistics_timestamp) >= 86400) {
+      $this->state->set('statistics.day_timestamp', $this->getRequestTime());
+      $this->connection->update('node_counter')
+        ->fields(['daycount' => 0])
+        ->execute();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function maxTotalCount() {
+    $query = $this->connection->select('node_counter', 'nc');
+    $query->addExpression('MAX(totalcount)');
+    $max_total_count = (int)$query->execute()->fetchField();
+    return $max_total_count;
+  }
+
+  /**
+   * Get current request time.
+   *
+   * @return int
+   *   Unix timestamp for current server request time.
+   */
+  protected function getRequestTime() {
+    return $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
+  }
+
+}
diff --git a/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php b/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php
index 8f4384e..8bd84b2 100644
--- a/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php
+++ b/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php
@@ -4,8 +4,14 @@
 
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Entity\EntityRepositoryInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\statistics\StatisticsStorageInterface;
 
 /**
  * Provides a 'Popular content' block.
@@ -15,7 +21,72 @@
  *   admin_label = @Translation("Popular content")
  * )
  */
-class StatisticsPopularBlock extends BlockBase {
+class StatisticsPopularBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity repository service.
+   *
+   * @var \Drupal\Core\Entity\EntityRepositoryInterface
+   */
+  protected $entityRepository;
+
+  /**
+   * The storage for statistics.
+   *
+   * @var \Drupal\statistics\StatisticsStorageInterface
+   */
+  protected $statisticsStorage;
+
+  /**
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
+   * Constructs an StatisticsPopularBlock object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
+   *   The entity repository service
+   * @param \Drupal\statistics\StatisticsStorageInterface $statistics_storage
+   *   The storage for statistics.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityRepositoryInterface $entity_repository, StatisticsStorageInterface $statistics_storage, RendererInterface $renderer) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityRepository = $entity_repository;
+    $this->statisticsStorage = $statistics_storage;
+    $this->renderer = $renderer;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('entity_type.manager'),
+      $container->get('entity.repository'),
+      $container->get('statistics.storage.node'),
+      $container->get('renderer')
+    );
+  }
 
   /**
    * {@inheritdoc}
@@ -82,28 +153,64 @@ public function build() {
     $content = array();
 
     if ($this->configuration['top_day_num'] > 0) {
-      $result = statistics_title_list('daycount', $this->configuration['top_day_num']);
-      if ($result) {
-        $content['top_day'] = node_title_list($result, $this->t("Today's:"));
+      $nids = $this->statisticsStorage->fetchAll('daycount', $this->configuration['top_day_num']);
+      if ($nids) {
+        $content['top_day'] = $this->nodeTitleList($nids, $this->t("Today's:"));
         $content['top_day']['#suffix'] = '<br />';
       }
     }
 
     if ($this->configuration['top_all_num'] > 0) {
-      $result = statistics_title_list('totalcount', $this->configuration['top_all_num']);
-      if ($result) {
-        $content['top_all'] = node_title_list($result, $this->t('All time:'));
+      $nids = $this->statisticsStorage->fetchAll('totalcount', $this->configuration['top_all_num']);
+      if ($nids) {
+        $content['top_all'] = $this->nodeTitleList($nids, $this->t('All time:'));
         $content['top_all']['#suffix'] = '<br />';
       }
     }
 
     if ($this->configuration['top_last_num'] > 0) {
-      $result = statistics_title_list('timestamp', $this->configuration['top_last_num']);
-      $content['top_last'] = node_title_list($result, $this->t('Last viewed:'));
+      $nids = $this->statisticsStorage->fetchAll('timestamp', $this->configuration['top_last_num']);
+      $content['top_last'] = $this->nodeTitleList($nids, $this->t('Last viewed:'));
       $content['top_last']['#suffix'] = '<br />';
     }
 
     return $content;
   }
 
+  /**
+   * Generates the ordered array of node links for build().
+   *
+   * @param int[] $nids
+   *   An ordered array of node ids.
+   * @param string $title
+   *   The title for the list.
+   *
+   * @return array
+   *   A render array for the list.
+   */
+  protected function nodeTitleList(array $nids, $title) {
+    $nodes = $this->entityTypeManager->getStorage('node')->loadMultiple($nids);
+
+    $items = [];
+    foreach ($nids as $nid) {
+      $node = $this->entityRepository->getTranslationFromContext($nodes[$nid]);
+      $item = [
+        '#type' => 'link',
+        '#title' => $node->getTitle(),
+        '#url' => $node->urlInfo('canonical'),
+      ];
+      $this->renderer->addCacheableDependency($item, $node);
+      $items[] = $item;
+    }
+
+    return [
+      '#theme' => 'item_list__node',
+      '#items' => $items,
+      '#title' => $title,
+      '#cache' => [
+        'tags' => $this->entityTypeManager->getDefinition('node')->getListCacheTags(),
+      ],
+    ];
+  }
+
 }
diff --git a/core/modules/statistics/src/StatisticsStorageInterface.php b/core/modules/statistics/src/StatisticsStorageInterface.php
new file mode 100644
index 0000000..ccb51e4
--- /dev/null
+++ b/core/modules/statistics/src/StatisticsStorageInterface.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Drupal\statistics;
+
+/**
+ * Provides an interface defining Statistics Storage.
+ *
+ * Stores the views per day, total views and timestamp of last view
+ * for entities.
+ */
+interface StatisticsStorageInterface {
+
+  /**
+   * Count a entity view.
+   *
+   * @param int $id
+   *   The ID of the entity to count.
+   *
+   * @return bool
+   *   TRUE if the entity view has been counted.
+   */
+  public function recordView($id);
+
+  /**
+   * Returns the number of times entities have been viewed.
+   *
+   * @param array $ids
+   *   An array of IDs of entities to fetch the views for.
+   *
+   * @return array \Drupal\statistics\StatisticsViewsResult
+   */
+  public function fetchViews($ids);
+
+  /**
+   * Returns the number of times a single entity has been viewed.
+   *
+   * @param int $id
+   *   The ID of the entity to fetch the views for.
+   *
+   * @return \Drupal\statistics\StatisticsViewsResult
+   */
+  public function fetchView($id);
+
+  /**
+   * Returns the number of times a entity has been viewed.
+   *
+   * @param string $order
+   *   The counter name to order by:
+   *   - 'totalcount' The total number of views.
+   *   - 'daycount' The number of views today.
+   *   - 'timestamp' The unix timestamp of the last view.
+   *
+   * @param int $limit
+   *   The number of entity IDs to return.
+   *
+   * @return array
+   *   An ordered array of entity IDs.
+   */
+  public function fetchAll($order = 'totalcount', $limit = 5);
+
+  /**
+   * Delete counts for a specific entity.
+   *
+   * @param int $id
+   *   The ID of the entity which views to delete.
+   *
+   * @return bool
+   *   TRUE if the entity views have been deleted.
+   */
+  public function deleteViews($id);
+
+  /**
+   * Reset the day counter for all entities once every day.
+   */
+  public function resetDayCount();
+
+  /**
+   * Returns the highest 'totalcount' value.
+   *
+   * @return int
+   *   The highest 'totalcount' value.
+   */
+  public function maxTotalCount();
+
+}
diff --git a/core/modules/statistics/src/StatisticsViewsResult.php b/core/modules/statistics/src/StatisticsViewsResult.php
new file mode 100644
index 0000000..ef0db97
--- /dev/null
+++ b/core/modules/statistics/src/StatisticsViewsResult.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Drupal\statistics;
+
+/**
+ * Value object for passing statistic results.
+ */
+class StatisticsViewsResult {
+
+  /**
+   * @var int
+   */
+  protected $totalCount;
+
+  /**
+   * @var int
+   */
+  protected $dayCount;
+
+  /**
+   * @var int
+   */
+  protected $timestamp;
+
+  public function __construct($total_count, $day_count, $timestamp) {
+    $this->totalCount = $total_count;
+    $this->dayCount = $day_count;
+    $this->timestamp = $timestamp;
+  }
+
+  /**
+   * Total number of times the entity has been viewed.
+   *
+   * @return int
+   */
+  public function getTotalCount() {
+    return $this->totalCount;
+  }
+
+
+  /**
+   * Total number of times the entity has been viewed "today".
+   *
+   * @return int
+   */
+  public function getDayCount() {
+    return $this->dayCount;
+  }
+
+
+  /**
+   * Timestamp of when the entity was last viewed.
+   *
+   * @return int
+   */
+  public function getTimestamp() {
+    return $this->timestamp;
+  }
+
+}
diff --git a/core/modules/statistics/src/Tests/StatisticsReportsTest.php b/core/modules/statistics/src/Tests/StatisticsReportsTest.php
index 9c0d26c..0fe2e28 100644
--- a/core/modules/statistics/src/Tests/StatisticsReportsTest.php
+++ b/core/modules/statistics/src/Tests/StatisticsReportsTest.php
@@ -2,6 +2,9 @@
 
 namespace Drupal\statistics\Tests;
 
+use Drupal\Core\Cache\Cache;
+use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
+
 /**
  * Tests display of statistics report blocks.
  *
@@ -9,6 +12,8 @@
  */
 class StatisticsReportsTest extends StatisticsTestBase {
 
+  use AssertPageCacheContextsAndTagsTrait;
+
   /**
    * Tests the "popular content" block.
    */
@@ -30,7 +35,7 @@ function testPopularContentBlock() {
     $client->post($stats_path, array('headers' => $headers, 'body' => $post));
 
     // Configure and save the block.
-    $this->drupalPlaceBlock('statistics_popular_block', array(
+    $block = $this->drupalPlaceBlock('statistics_popular_block', array(
       'label' => 'Popular content',
       'top_day_num' => 3,
       'top_all_num' => 3,
@@ -44,9 +49,16 @@ function testPopularContentBlock() {
     $this->assertText('All time', 'Found the all time popular content.');
     $this->assertText('Last viewed', 'Found the last viewed popular content.');
 
-    // statistics.module doesn't use node entities, prevent the node language
-    // from being added to the options.
-    $this->assertRaw(\Drupal::l($node->label(), $node->urlInfo('canonical', ['language' => NULL])), 'Found link to visited node.');
+    $tags = Cache::mergeTags($node->getCacheTags(), $block->getCacheTags());
+    $tags = Cache::mergeTags($tags, $this->blockingUser->getCacheTags());
+    $tags = Cache::mergeTags($tags, ['block_view', 'config:block_list', 'node_list', 'rendered', 'user_view']);
+    $this->assertCacheTags($tags);
+    $contexts = Cache::mergeContexts($node->getCacheContexts(), $block->getCacheContexts());
+    $contexts = Cache::mergeContexts($contexts, ['url.query_args:_wrapper_format']);
+    $this->assertCacheContexts($contexts);
+
+    // Check if the node link is displayed.
+    $this->assertRaw(\Drupal::l($node->label(), $node->urlInfo('canonical')), 'Found link to visited node.');
   }
 
 }
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index 5079e43..5419645 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -52,9 +52,9 @@ function statistics_node_links_alter(array &$links, NodeInterface $entity, array
   if ($context['view_mode'] != 'rss') {
     $links['#cache']['contexts'][] = 'user.permissions';
     if (\Drupal::currentUser()->hasPermission('view post access counter')) {
-      $statistics = statistics_get($entity->id());
+      $statistics = \Drupal::service('statistics.storage.node')->fetchView($entity->id());
       if ($statistics) {
-        $statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics['totalcount'], '1 view', '@count views');
+        $statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics->getTotalCount(), '1 view', '@count views');
         $links['statistics'] = array(
           '#theme' => 'links__node__statistics',
           '#links' => $statistics_links,
@@ -70,18 +70,10 @@ function statistics_node_links_alter(array &$links, NodeInterface $entity, array
  * Implements hook_cron().
  */
 function statistics_cron() {
-  $statistics_timestamp = \Drupal::state()->get('statistics.day_timestamp') ?: 0;
-
-  if ((REQUEST_TIME - $statistics_timestamp) >= 86400) {
-    // Reset day counts.
-    db_update('node_counter')
-      ->fields(array('daycount' => 0))
-      ->execute();
-    \Drupal::state()->set('statistics.day_timestamp', REQUEST_TIME);
-  }
-
-  // Calculate the maximum of node views, for node search ranking.
-  \Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, db_query('SELECT MAX(totalcount) FROM {node_counter}')->fetchField()));
+  $storage = \Drupal::service('statistics.storage.node');
+  $storage->resetDayCount();
+  $max_total_count = $storage->maxTotalCount();
+  \Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, $max_total_count));
 }
 
 /**
@@ -123,26 +115,21 @@ function statistics_title_list($dbfield, $dbrows) {
   return FALSE;
 }
 
-
 /**
  * Retrieves a node's "view statistics".
  *
- * @param int $nid
- *   The node ID.
- *
- * @return array
- *   An associative array containing:
- *   - totalcount: Integer for the total number of times the node has been
- *     viewed.
- *   - daycount: Integer for the total number of times the node has been viewed
- *     "today". For the daycount to be reset, cron must be enabled.
- *   - timestamp: Integer for the timestamp of when the node was last viewed.
+ * @deprecated in Drupal 8.2.x, will be removed before Drupal 9.0.0.
+ *   Use \Drupal::service('statistics.storage.node')->fetchView($id).
  */
-function statistics_get($nid) {
-
-  if ($nid > 0) {
-    // Retrieve an array with both totalcount and daycount.
-    return db_query('SELECT totalcount, daycount, timestamp FROM {node_counter} WHERE nid = :nid', array(':nid' => $nid), array('target' => 'replica'))->fetchAssoc();
+function statistics_get($id) {
+  if ($id > 0) {
+    /** @var \Drupal\statistics\StatisticsViewsResult $statistics */
+    $statistics = \Drupal::service('statistics.storage.node')->fetchView($id);
+    return [
+      'totalcount' => $statistics->getTotalCount(),
+      'daycount' => $statistics->getDayCount(),
+      'timestamp' => $statistics->getTimestamp(),
+    ];
   }
 }
 
@@ -151,9 +138,8 @@ function statistics_get($nid) {
  */
 function statistics_node_predelete(EntityInterface $node) {
   // Clean up statistics table when node is deleted.
-  db_delete('node_counter')
-    ->condition('nid', $node->id())
-    ->execute();
+  $id = $node->id();
+  return \Drupal::service('statistics.storage.node')->deleteViews($id);
 }
 
 /**
diff --git a/core/modules/statistics/statistics.php b/core/modules/statistics/statistics.php
index a79af5f..a43509e 100644
--- a/core/modules/statistics/statistics.php
+++ b/core/modules/statistics/statistics.php
@@ -14,8 +14,9 @@
 
 $kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod');
 $kernel->boot();
+$container = $kernel->getContainer();
 
-$views = $kernel->getContainer()
+$views = $container
   ->get('config.factory')
   ->get('statistics.settings')
   ->get('count_content_views');
@@ -23,15 +24,7 @@
 if ($views) {
   $nid = filter_input(INPUT_POST, 'nid', FILTER_VALIDATE_INT);
   if ($nid) {
-    \Drupal::database()->merge('node_counter')
-      ->key('nid', $nid)
-      ->fields(array(
-        'daycount' => 1,
-        'totalcount' => 1,
-        'timestamp' => REQUEST_TIME,
-      ))
-      ->expression('daycount', 'daycount + 1')
-      ->expression('totalcount', 'totalcount + 1')
-      ->execute();
+    $container->get('request_stack')->push(Request::createFromGlobals());
+    $container->get('statistics.storage.node')->recordView($nid);
   }
 }
diff --git a/core/modules/statistics/statistics.services.yml b/core/modules/statistics/statistics.services.yml
new file mode 100644
index 0000000..cf15573
--- /dev/null
+++ b/core/modules/statistics/statistics.services.yml
@@ -0,0 +1,6 @@
+services:
+  statistics.storage.node:
+    class: Drupal\statistics\NodeStatisticsDatabaseStorage
+    arguments: ['@database', '@state', '@request_stack']
+    tags:
+      - { name: backend_overridable }
diff --git a/core/modules/system/src/Tests/Path/UrlAliasFixtures.php b/core/modules/system/src/Tests/Path/UrlAliasFixtures.php
index a527fc5..1f71690 100644
--- a/core/modules/system/src/Tests/Path/UrlAliasFixtures.php
+++ b/core/modules/system/src/Tests/Path/UrlAliasFixtures.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\system\Tests\Path;
 
+use Drupal\Core\KeyValueStore\DatabaseStorage;
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Path\AliasStorage;
 
@@ -89,7 +90,7 @@ public function tableDefinition() {
     $schema = system_schema();
 
     $tables['url_alias'] = AliasStorage::schemaDefinition();
-    $tables['key_value'] = $schema['key_value'];
+    $tables['key_value'] = DatabaseStorage::schemaDefinition();
 
     return $tables;
   }
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index ed181f2..4253c9f 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -846,71 +846,6 @@ function system_install() {
  * Implements hook_schema().
  */
 function system_schema() {
-  $schema['key_value'] = array(
-    'description' => 'Generic key-value storage table. See the state system for an example.',
-    'fields' => array(
-      'collection' => array(
-        'description' => 'A named collection of key and value pairs.',
-        'type' => 'varchar_ascii',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        'description' => 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.',
-        'type' => 'varchar_ascii',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'value' => array(
-        'description' => 'The value.',
-        'type' => 'blob',
-        'not null' => TRUE,
-        'size' => 'big',
-      ),
-    ),
-    'primary key' => array('collection', 'name'),
-  );
-
-  $schema['key_value_expire'] = array(
-    'description' => 'Generic key/value storage table with an expiration.',
-    'fields' => array(
-      'collection' => array(
-        'description' => 'A named collection of key and value pairs.',
-        'type' => 'varchar_ascii',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'name' => array(
-        // KEY is an SQL reserved word, so use 'name' as the key's field name.
-        'description' => 'The key of the key/value pair.',
-        'type' => 'varchar_ascii',
-        'length' => 128,
-        'not null' => TRUE,
-        'default' => '',
-      ),
-      'value' => array(
-        'description' => 'The value of the key/value pair.',
-        'type' => 'blob',
-        'not null' => TRUE,
-        'size' => 'big',
-      ),
-      'expire' => array(
-        'description' => 'The time since Unix epoch in seconds when this item expires. Defaults to the maximum possible time.',
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 2147483647,
-      ),
-    ),
-    'primary key' => array('collection', 'name'),
-    'indexes' => array(
-      'all' => array('name', 'collection', 'expire'),
-      'expire' => array('expire'),
-    ),
-  );
-
   $schema['sequences'] = array(
     'description' => 'Stores IDs.',
     'fields' => array(
diff --git a/core/modules/system/tests/src/Kernel/System/CronQueueTest.php b/core/modules/system/tests/src/Kernel/System/CronQueueTest.php
index c51ee5a..286849e 100644
--- a/core/modules/system/tests/src/Kernel/System/CronQueueTest.php
+++ b/core/modules/system/tests/src/Kernel/System/CronQueueTest.php
@@ -38,11 +38,6 @@ class CronQueueTest extends KernelTestBase {
    */
   protected function setUp() {
     parent::setUp();
-
-    // These additional tables are necessary because $this->cron->run() calls
-    // system_cron().
-    $this->installSchema('system', ['key_value_expire']);
-
     $this->connection = Database::getConnection();
     $this->cron = \Drupal::service('cron');
   }
diff --git a/core/modules/taxonomy/src/Tests/VocabularyUiTest.php b/core/modules/taxonomy/src/Tests/VocabularyUiTest.php
index 3066ef0..76fcac2 100644
--- a/core/modules/taxonomy/src/Tests/VocabularyUiTest.php
+++ b/core/modules/taxonomy/src/Tests/VocabularyUiTest.php
@@ -47,14 +47,17 @@ function testVocabularyInterface() {
 
     // Edit the vocabulary.
     $this->drupalGet('admin/structure/taxonomy');
-    $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
+    $this->assertText($edit['name'], 'Vocabulary name found in the vocabulary overview listing.');
+    $this->assertText($edit['description'], 'Vocabulary description found in the vocabulary overview listing.');
     $this->assertLinkByHref(Url::fromRoute('entity.taxonomy_term.add_form', ['taxonomy_vocabulary' => $edit['vid']])->toString());
     $this->clickLink(t('Edit vocabulary'));
     $edit = array();
     $edit['name'] = $this->randomMachineName();
+    $edit['description'] = $this->randomMachineName();
     $this->drupalPostForm(NULL, $edit, t('Save'));
     $this->drupalGet('admin/structure/taxonomy');
-    $this->assertText($edit['name'], 'Vocabulary found in the vocabulary overview listing.');
+    $this->assertText($edit['name'], 'Vocabulary name found in the vocabulary overview listing.');
+    $this->assertText($edit['description'], 'Vocabulary description found in the vocabulary overview listing.');
 
     // Try to submit a vocabulary with a duplicate machine name.
     $edit['vid'] = $vid;
diff --git a/core/modules/taxonomy/src/VocabularyListBuilder.php b/core/modules/taxonomy/src/VocabularyListBuilder.php
index b5597bb..9c24311 100644
--- a/core/modules/taxonomy/src/VocabularyListBuilder.php
+++ b/core/modules/taxonomy/src/VocabularyListBuilder.php
@@ -56,6 +56,7 @@ public function getDefaultOperations(EntityInterface $entity) {
    */
   public function buildHeader() {
     $header['label'] = t('Vocabulary name');
+    $header['description'] = t('Description');
     return $header + parent::buildHeader();
   }
 
@@ -64,6 +65,7 @@ public function buildHeader() {
    */
   public function buildRow(EntityInterface $entity) {
     $row['label'] = $entity->label();
+    $row['description']['data'] = ['#markup' => $entity->getDescription()];
     return $row + parent::buildRow($entity);
   }
 
diff --git a/core/modules/user/src/Plugin/rest/resource/UserRegistrationResource.php b/core/modules/user/src/Plugin/rest/resource/UserRegistrationResource.php
new file mode 100644
index 0000000..6a243c3
--- /dev/null
+++ b/core/modules/user/src/Plugin/rest/resource/UserRegistrationResource.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Drupal\user\Plugin\rest\resource;
+
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\rest\ModifiedResourceResponse;
+use Drupal\rest\Plugin\ResourceBase;
+use Drupal\rest\Plugin\rest\resource\EntityResourceAccessTrait;
+use Drupal\rest\Plugin\rest\resource\EntityResourceValidationTrait;
+use Drupal\user\UserInterface;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
+
+/**
+ * Represents user registration as a resource.
+ *
+ * @RestResource(
+ *   id = "user_registration",
+ *   label = @Translation("User registration"),
+ *   serialization_class = "Drupal\user\Entity\User",
+ *   uri_paths = {
+ *     "https://www.drupal.org/link-relations/create" = "/user/register",
+ *   },
+ * )
+ */
+class UserRegistrationResource extends ResourceBase {
+
+  use EntityResourceValidationTrait;
+  use EntityResourceAccessTrait;
+
+  /**
+   * User settings config instance.
+   *
+   * @var \Drupal\Core\Config\ImmutableConfig
+   */
+  protected $userSettings;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Constructs a new UserRegistrationResource instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param array $serializer_formats
+   *   The available serialization formats.
+   * @param \Psr\Log\LoggerInterface $logger
+   *   A logger instance.
+   * @param \Drupal\Core\Config\ImmutableConfig $user_settings
+   *   A user settings config instance.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current user.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, ImmutableConfig $user_settings, AccountInterface $current_user) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
+    $this->userSettings = $user_settings;
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->getParameter('serializer.formats'),
+      $container->get('logger.factory')->get('rest'),
+      $container->get('config.factory')->get('user.settings'),
+      $container->get('current_user')
+    );
+  }
+
+  /**
+   * Responds to user registration POST request.
+   *
+   * @param \Drupal\user\UserInterface $account
+   *   The user account entity.
+   *
+   * @return \Drupal\rest\ModifiedResourceResponse
+   *   The HTTP response object.
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+   * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
+   */
+  public function post(UserInterface $account = NULL) {
+    $this->ensureAccountCanRegister($account);
+
+    // Only activate new users if visitors are allowed to register and no email
+    // verification required.
+    if ($this->userSettings->get('register') == USER_REGISTER_VISITORS && !$this->userSettings->get('verify_mail')) {
+      $account->activate();
+    }
+    else {
+      $account->block();
+    }
+
+    $this->checkEditFieldAccess($account);
+
+    // Make sure that the user entity is valid (email and name are valid).
+    $this->validate($account);
+
+    // Create the account.
+    $account->save();
+
+    $this->sendEmailNotifications($account);
+
+    return new ModifiedResourceResponse($account, 200);
+  }
+
+  /**
+   * Ensure the account can be registered in this request.
+   *
+   * @param \Drupal\user\UserInterface $account
+   *   The user account to register.
+   */
+  protected function ensureAccountCanRegister(UserInterface $account = NULL) {
+    if ($account === NULL) {
+      throw new BadRequestHttpException('No user account data for registration received.');
+    }
+
+    // POSTed user accounts must not have an ID set, because we always want to
+    // create new entities here.
+    if (!$account->isNew()) {
+      throw new BadRequestHttpException('An ID has been set and only new user accounts can be registered.');
+    }
+
+    // Only allow anonymous users to register, authenticated users with the
+    // necessary permissions can POST a new user to the "user" REST resource.
+    // @see \Drupal\rest\Plugin\rest\resource\EntityResource
+    if (!$this->currentUser->isAnonymous()) {
+      throw new AccessDeniedHttpException('Only anonymous users can register a user.');
+    }
+
+    // Verify that the current user can register a user account.
+    if ($this->userSettings->get('register') == USER_REGISTER_ADMINISTRATORS_ONLY) {
+      throw new AccessDeniedHttpException('You cannot register a new user account.');
+    }
+
+    if (!$this->userSettings->get('verify_mail')) {
+      if (empty($account->getPassword())) {
+        // If no e-mail verification then the user must provide a password.
+        throw new UnprocessableEntityHttpException('No password provided.');
+      }
+    }
+    else {
+      if (!empty($account->getPassword())) {
+        // If e-mail verification required then a password cannot provided.
+        // The password will be set when the user logs in.
+        throw new UnprocessableEntityHttpException('A Password cannot be specified. It will be generated on login.');
+      }
+    }
+  }
+
+  /**
+   * Sends email notifications if necessary for user that was registered.
+   *
+   * @param \Drupal\user\UserInterface $account
+   *   The user account.
+   */
+  protected function sendEmailNotifications(UserInterface $account) {
+    $approval_settings = $this->userSettings->get('register');
+    // No e-mail verification is required. Activating the user.
+    if ($approval_settings == USER_REGISTER_VISITORS) {
+      if ($this->userSettings->get('verify_mail')) {
+        // No administrator approval required.
+        _user_mail_notify('register_no_approval_required', $account);
+      }
+    }
+    // Administrator approval required.
+    elseif ($approval_settings == USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) {
+      _user_mail_notify('register_pending_approval', $account);
+    }
+  }
+
+}
diff --git a/core/modules/user/src/Tests/RestRegisterUserTest.php b/core/modules/user/src/Tests/RestRegisterUserTest.php
new file mode 100644
index 0000000..c10d028
--- /dev/null
+++ b/core/modules/user/src/Tests/RestRegisterUserTest.php
@@ -0,0 +1,187 @@
+<?php
+
+namespace Drupal\user\Tests;
+
+use Drupal\rest\Tests\RESTTestBase;
+use Drupal\user\Entity\Role;
+use Drupal\user\RoleInterface;
+
+/**
+ * Tests user registration via REST resource.
+ *
+ * @group user
+ */
+class RestRegisterUserTest extends RESTTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['hal'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+
+    $this->enableService('user_registration', 'POST', 'hal_json');
+
+    Role::load(RoleInterface::ANONYMOUS_ID)
+      ->grantPermission('restful post user_registration')
+      ->save();
+
+    Role::load(RoleInterface::AUTHENTICATED_ID)
+      ->grantPermission('restful post user_registration')
+      ->save();
+  }
+
+  /**
+   * Tests that only anonymous users can register users.
+   */
+  public function testRegisterUser() {
+    // Verify that an authenticated user cannot register a new user, despite
+    // being granted permission to do so because only anonymous users can
+    // register themselves, authenticated users with the necessary permissions
+    // can POST a new user to the "user" REST resource.
+    $user = $this->createUser();
+    $this->drupalLogin($user);
+    $this->registerRequest('palmer.eldritch');
+    $this->assertResponse('403', 'Only anonymous users can register users.');
+    $this->drupalLogout();
+
+    $user_settings = $this->config('user.settings');
+
+    // Test out different setting User Registration and Email Verification.
+    // Allow visitors to register with no email verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS);
+    $user_settings->set('verify_mail', 0);
+    $user_settings->save();
+    $user = $this->registerUser('Palmer.Eldritch');
+    $this->assertFalse($user->isBlocked());
+    $this->assertFalse(empty($user->getPassword()));
+    $email_count = count($this->drupalGetMails());
+    $this->assertEqual(0, $email_count);
+
+    // Attempt to register without sending a password.
+    $this->registerRequest('Rick.Deckard', FALSE);
+    $this->assertResponse('422', 'No password provided');
+
+    // Allow visitors to register with email verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS);
+    $user_settings->set('verify_mail', 1);
+    $user_settings->save();
+    $user = $this->registerUser('Jason.Taverner', FALSE);
+    $this->assertTrue(empty($user->getPassword()));
+    $this->assertTrue($user->isBlocked());
+    $this->assertMailString('body', 'You may now log in by clicking this link', 1);
+
+    // Attempt to register with a password when e-mail verification is on.
+    $this->registerRequest('Estraven', TRUE);
+    $this->assertResponse('422', 'A Password cannot be specified. It will be generated on login.');
+
+    // Allow visitors to register with Admin approval and e-mail verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
+    $user_settings->set('verify_mail', 1);
+    $user_settings->save();
+    $user = $this->registerUser('Bob.Arctor', FALSE);
+    $this->assertTrue(empty($user->getPassword()));
+    $this->assertTrue($user->isBlocked());
+    $this->assertMailString('body', 'Your application for an account is', 2);
+    $this->assertMailString('body', 'Bob.Arctor has applied for an account', 2);
+
+    // Attempt to register with a password when e-mail verification is on.
+    $this->registerRequest('Ursula', TRUE);
+    $this->assertResponse('422', 'A Password cannot be specified. It will be generated on login.');
+
+    // Allow visitors to register with Admin approval and no email verification.
+    $user_settings->set('register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL);
+    $user_settings->set('verify_mail', 0);
+    $user_settings->save();
+    $user = $this->registerUser('Argaven');
+    $this->assertFalse(empty($user->getPassword()));
+    $this->assertTrue($user->isBlocked());
+    $this->assertMailString('body', 'Your application for an account is', 2);
+    $this->assertMailString('body', 'Argaven has applied for an account', 2);
+
+    // Attempt to register without sending a password.
+    $this->registerRequest('Tibe', FALSE);
+    $this->assertResponse('422', 'No password provided');
+  }
+
+  /**
+   * Creates serialize user values.
+   *
+   * @param string $name
+   *   The name of the user. Use only valid values for emails.
+   *
+   * @param bool $include_password
+   *   Whether to include a password in the user values.
+   *
+   * @return string Serialized user values.
+   *   Serialized user values.
+   */
+  protected function createSerializedUser($name, $include_password = TRUE) {
+    global $base_url;
+    // New user info to be serialized.
+    $data = [
+      "_links" =>
+        [
+          "type" => ["href" => $base_url . "/rest/type/user/user"],
+        ],
+      "langcode" => [
+        [
+          "value" => "en",
+        ],
+      ],
+      "name" => [
+        [
+          "value" => $name,
+        ],
+      ],
+      "mail" => [
+        [
+          "value" => "$name@example.com",
+        ],
+      ],
+    ];
+    if ($include_password) {
+      $data['pass']['value'] = 'SuperSecretPassword';
+    }
+
+    // Create a HAL+JSON version for the user entity we want to create.
+    $serialized = $this->container->get('serializer')
+      ->serialize($data, 'hal_json');
+    return $serialized;
+  }
+
+  /**
+   * Registers a user via REST resource.
+   *
+   * @param $name
+   *   User name.
+   *
+   * @param bool $include_password
+   *
+   * @return bool|\Drupal\user\Entity\User
+   */
+  protected function registerUser($name, $include_password = TRUE) {
+    // Verify that an anonymous user can register.
+    $this->registerRequest($name, $include_password);
+    $this->assertResponse('200', 'HTTP response code is correct.');
+    $user = user_load_by_name($name);
+    $this->assertFalse(empty($user), 'User was create as expected');
+    return $user;
+  }
+
+  /**
+   * Make a REST user registration request.
+   *
+   * @param $name
+   * @param $include_password
+   */
+  protected function registerRequest($name, $include_password = TRUE) {
+    $serialized = $this->createSerializedUser($name, $include_password);
+    $this->httpRequest('/user/register', 'POST', $serialized, 'application/hal+json');
+  }
+
+}
diff --git a/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php b/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php
index 254524a..bab1199 100644
--- a/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php
+++ b/core/modules/user/tests/src/Kernel/TempStoreDatabaseTest.php
@@ -54,10 +54,6 @@ class TempStoreDatabaseTest extends KernelTestBase {
   protected function setUp() {
     parent::setUp();
 
-    // Install system tables to test the key/value storage without installing a
-    // full Drupal environment.
-    $this->installSchema('system', array('key_value_expire'));
-
     // Create several objects for testing.
     for ($i = 0; $i <= 3; $i++) {
       $this->objects[$i] = $this->randomObject();
diff --git a/core/modules/user/tests/src/Unit/UserRegistrationResourceTest.php b/core/modules/user/tests/src/Unit/UserRegistrationResourceTest.php
new file mode 100644
index 0000000..142685c
--- /dev/null
+++ b/core/modules/user/tests/src/Unit/UserRegistrationResourceTest.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace Drupal\Tests\user\Unit;
+
+use Drupal\Core\Config\ImmutableConfig;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Tests\UnitTestCase;
+use Drupal\user\Entity\User;
+use Drupal\user\Plugin\rest\resource\UserRegistrationResource;
+use Psr\Log\LoggerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+
+/**
+ * Only administrators can create user accounts.
+ */
+if (!defined('USER_REGISTER_ADMINISTRATORS_ONLY')) {
+  define('USER_REGISTER_ADMINISTRATORS_ONLY', 'admin_only');
+}
+
+/**
+ * Visitors can create their own accounts.
+ */
+if (!defined('USER_REGISTER_VISITORS')) {
+  define('USER_REGISTER_VISITORS', 'visitors');
+}
+
+/**
+ * Visitors can create accounts, but they don't become active without
+ * administrative approval.
+ */
+if (!defined('USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL')) {
+  define('USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL', 'visitors_admin_approval');
+}
+
+/**
+ * Tests User Registration REST resource.
+ *
+ * @coversDefaultClass \Drupal\user\Plugin\rest\resource\UserRegistrationResource
+ * @group user
+ */
+class UserRegistrationResourceTest extends UnitTestCase {
+
+  const ERROR_MESSAGE = "Unprocessable Entity: validation failed.\nproperty_path: message\nproperty_path_2: message_2\n";
+
+  /**
+   * Class to be tested.
+   *
+   * @var \Drupal\user\Plugin\rest\resource\UserRegistrationResource
+   */
+  protected $testClass;
+
+  /**
+   * A reflection of self::$testClass.
+   *
+   * @var \ReflectionClass
+   */
+  protected $reflection;
+
+  /**
+   * A user settings config instance.
+   *
+   * @var \Drupal\Core\Config\ImmutableConfig|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $userSettings;
+
+  /**
+   * Logger service.
+   *
+   * @var \Psr\Log\LoggerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $logger;
+
+  /**
+   * The current user.
+   *
+   * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $currentUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->logger = $this->prophesize(LoggerInterface::class)->reveal();
+
+    $this->userSettings = $this->prophesize(ImmutableConfig::class);
+
+    $this->currentUser = $this->prophesize(AccountInterface::class);
+
+    $this->testClass = new UserRegistrationResource([], 'plugin_id', '', [], $this->logger, $this->userSettings->reveal(), $this->currentUser->reveal());
+    $this->reflection = new \ReflectionClass($this->testClass);
+  }
+
+  /**
+   * Tests that an exception is thrown when no data provided for the account.
+   */
+  public function testEmptyPost() {
+    $this->setExpectedException(BadRequestHttpException::class);
+    $this->testClass->post(NULL);
+  }
+
+  /**
+   * Tests that only new user accounts can be registered.
+   */
+  public function testExistedEntityPost() {
+    $entity = $this->prophesize(User::class);
+    $entity->isNew()->willReturn(FALSE);
+    $this->setExpectedException(BadRequestHttpException::class);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+  /**
+   * Tests that admin permissions are required to register a user account.
+   */
+  public function testRegistrationAdminOnlyPost() {
+
+    $this->userSettings->get('register')->willReturn(USER_REGISTER_ADMINISTRATORS_ONLY);
+
+    $this->currentUser->isAnonymous()->willReturn(TRUE);
+
+    $this->testClass = new UserRegistrationResource([], 'plugin_id', '', [], $this->logger, $this->userSettings->reveal(), $this->currentUser->reveal());
+
+    $entity = $this->prophesize(User::class);
+    $entity->isNew()->willReturn(TRUE);
+
+    $this->setExpectedException(AccessDeniedHttpException::class);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+  /**
+   * Tests that only anonymous users can register users.
+   */
+  public function testRegistrationAnonymousOnlyPost() {
+    $this->currentUser->isAnonymous()->willReturn(FALSE);
+
+    $this->testClass = new UserRegistrationResource([], 'plugin_id', '', [], $this->logger, $this->userSettings->reveal(), $this->currentUser->reveal());
+
+    $entity = $this->prophesize(User::class);
+    $entity->isNew()->willReturn(TRUE);
+
+    $this->setExpectedException(AccessDeniedHttpException::class);
+
+    $this->testClass->post($entity->reveal());
+  }
+
+}
diff --git a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
index d4e1b23..4cc49d2 100644
--- a/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
+++ b/core/modules/views/src/Plugin/views/query/QueryPluginBase.php
@@ -160,7 +160,7 @@ public function getLimit() {
    * @param $where
    *   'where' or 'having'.
    *
-   * @return
+   * @return $group
    *   The group ID generated.
    */
   public function setWhereGroup($type = 'AND', $group = NULL, $where = 'where') {
diff --git a/core/modules/views/src/Plugin/views/query/Sql.php b/core/modules/views/src/Plugin/views/query/Sql.php
index cfe593c..c860d32 100644
--- a/core/modules/views/src/Plugin/views/query/Sql.php
+++ b/core/modules/views/src/Plugin/views/query/Sql.php
@@ -373,7 +373,7 @@ public function addRelationship($alias, JoinPluginBase $join, $base, $link_point
    * @param $alias
    *   A specific alias to use, rather than the default alias.
    *
-   * @return string
+   * @return $alias
    *   The alias of the table; this alias can be used to access information
    *   about the table and should always be used to refer to the table when
    *   adding parts to the query. Or FALSE if the table was not able to be
@@ -412,7 +412,7 @@ public function addTable($table, $relationship = NULL, JoinPluginBase $join = NU
    * @param $alias
    *   A specific alias to use, rather than the default alias.
    *
-   * @return string
+   * @return $alias
    *   The alias of the table; this alias can be used to access information
    *   about the table and should always be used to refer to the table when
    *   adding parts to the query. Or FALSE if the table was not able to be
@@ -759,7 +759,7 @@ public function getTableInfo($table) {
    *   - aggregate: Set to TRUE to indicate that this value should be
    *     aggregated in a GROUP BY.
    *
-   * @return string
+   * @return $name
    *   The name that this field can be referred to as. Usually this is the alias.
    */
   public function addField($table, $field, $alias = '', $params = array()) {
diff --git a/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php b/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php
index f149b7f..d4657e3 100644
--- a/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php
+++ b/core/modules/views/tests/src/Kernel/ViewsKernelTestBase.php
@@ -41,7 +41,7 @@
   protected function setUp($import_test_views = TRUE) {
     parent::setUp();
 
-    $this->installSchema('system', ['router', 'sequences', 'key_value_expire']);
+    $this->installSchema('system', ['router', 'sequences']);
     $this->setUpFixtures();
 
     if ($import_test_views) {
diff --git a/core/phpcs.xml.dist b/core/phpcs.xml.dist
index 29468f7..426c67d 100644
--- a/core/phpcs.xml.dist
+++ b/core/phpcs.xml.dist
@@ -45,6 +45,7 @@
   <rule ref="Drupal.Commenting.FileComment"/>
   <rule ref="Drupal.Commenting.FunctionComment">
     <exclude name="Drupal.Commenting.FunctionComment.IncorrectTypeHint"/>
+    <exclude name="Drupal.Commenting.FunctionComment.$InReturnType"/>
     <exclude name="Drupal.Commenting.FunctionComment.InvalidNoReturn"/>
     <exclude name="Drupal.Commenting.FunctionComment.InvalidReturnNotVoid"/>
     <exclude name="Drupal.Commenting.FunctionComment.InvalidTypeHint"/>
diff --git a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php
index 8129410..9886955 100644
--- a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php
@@ -83,10 +83,7 @@ protected function setUp() {
     $this->skipTests = Database::getConnection()->databaseType() !== 'mysql';
 
     // Create some schemas so our export contains tables.
-    $this->installSchema('system', [
-      'key_value_expire',
-      'sessions',
-    ]);
+    $this->installSchema('system', ['sessions']);
     $this->installSchema('dblog', ['watchdog']);
     $this->installEntitySchema('block_content');
     $this->installEntitySchema('user');
@@ -126,7 +123,6 @@ protected function setUp() {
       'cache_discovery',
       'cache_entity',
       'file_managed',
-      'key_value_expire',
       'menu_link_content',
       'menu_link_content_data',
       'sequences',
diff --git a/core/tests/Drupal/KernelTests/Core/Element/PathElementFormTest.php b/core/tests/Drupal/KernelTests/Core/Element/PathElementFormTest.php
index ca4e501..4186a9c 100644
--- a/core/tests/Drupal/KernelTests/Core/Element/PathElementFormTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Element/PathElementFormTest.php
@@ -37,7 +37,7 @@ class PathElementFormTest extends KernelTestBase implements FormInterface {
    */
   protected function setUp() {
     parent::setUp();
-    $this->installSchema('system', ['sequences', 'key_value_expire']);
+    $this->installSchema('system', ['sequences']);
     $this->installEntitySchema('user');
     \Drupal::service('router.builder')->rebuild();
     /** @var \Drupal\user\RoleInterface $role */
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php
index ec81f7b..9249f5a 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/Element/EntityAutocompleteElementFormTest.php
@@ -45,7 +45,6 @@ class EntityAutocompleteElementFormTest extends EntityKernelTestBase implements
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('system', ['key_value_expire']);
     \Drupal::service('router.builder')->rebuild();
 
     $this->testUser = User::create(array(
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php
index ae4925c..6aea1b3 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityAutocompleteTest.php
@@ -33,14 +33,6 @@ class EntityAutocompleteTest extends EntityKernelTestBase {
   protected $bundle = 'entity_test';
 
   /**
-   * {@inheritdoc}
-   */
-  protected function setUp() {
-    parent::setUp();
-    $this->installSchema('system', ['key_value']);
-  }
-
-  /**
    * Tests autocompletion edge cases with slashes in the names.
    */
   function testEntityReferenceAutocompletion() {
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php
index 50d173b..81c33d6 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldWidgetConstraintValidatorTest.php
@@ -23,7 +23,6 @@ class FieldWidgetConstraintValidatorTest extends KernelTestBase {
   protected function setUp() {
     parent::setUp();
 
-    $this->installSchema('system', ['key_value']);
     $this->container->get('router.builder')->rebuild();
 
     $this->installEntitySchema('user');
diff --git a/core/tests/Drupal/KernelTests/Core/Form/ExternalFormUrlTest.php b/core/tests/Drupal/KernelTests/Core/Form/ExternalFormUrlTest.php
index f3fbc4f..1bc8c48 100644
--- a/core/tests/Drupal/KernelTests/Core/Form/ExternalFormUrlTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Form/ExternalFormUrlTest.php
@@ -53,7 +53,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {}
    */
   protected function setUp() {
     parent::setUp();
-    $this->installSchema('system', ['key_value_expire', 'sequences']);
+    $this->installSchema('system', ['sequences']);
     $this->installEntitySchema('user');
 
     $test_user = User::create([
diff --git a/core/tests/Drupal/KernelTests/Core/Form/FormCacheTest.php b/core/tests/Drupal/KernelTests/Core/Form/FormCacheTest.php
index fa86bee..890133a 100644
--- a/core/tests/Drupal/KernelTests/Core/Form/FormCacheTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Form/FormCacheTest.php
@@ -39,7 +39,6 @@ class FormCacheTest extends KernelTestBase {
 
   protected function setUp() {
     parent::setUp();
-    $this->installSchema('system', array('key_value_expire'));
 
     $this->formBuildId = $this->randomMachineName();
     $this->form = array(
diff --git a/core/tests/Drupal/KernelTests/Core/Form/FormDefaultHandlersTest.php b/core/tests/Drupal/KernelTests/Core/Form/FormDefaultHandlersTest.php
index 7f046e9..895ceba 100644
--- a/core/tests/Drupal/KernelTests/Core/Form/FormDefaultHandlersTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Form/FormDefaultHandlersTest.php
@@ -24,14 +24,6 @@ class FormDefaultHandlersTest extends KernelTestBase implements FormInterface {
   /**
    * {@inheritdoc}
    */
-  protected function setUp() {
-    parent::setUp();
-    $this->installSchema('system', ['key_value_expire']);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getFormId() {
     return 'test_form_handlers';
   }
diff --git a/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageExpirableTest.php b/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageExpirableTest.php
index 1631501..446e714 100644
--- a/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageExpirableTest.php
+++ b/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageExpirableTest.php
@@ -22,7 +22,6 @@ class DatabaseStorageExpirableTest extends StorageTestBase {
   protected function setUp() {
     parent::setUp();
     $this->factory = 'keyvalue.expirable';
-    $this->installSchema('system', array('key_value_expire'));
   }
 
   /**
diff --git a/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageTest.php b/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageTest.php
index 676ab10..adacfce 100644
--- a/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageTest.php
+++ b/core/tests/Drupal/KernelTests/Core/KeyValueStore/DatabaseStorageTest.php
@@ -19,11 +19,6 @@ class DatabaseStorageTest extends StorageTestBase {
    */
   public static $modules = array('system');
 
-  protected function setUp() {
-    parent::setUp();
-    $this->installSchema('system', array('key_value'));
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/tests/Drupal/KernelTests/Core/KeyValueStore/GarbageCollectionTest.php b/core/tests/Drupal/KernelTests/Core/KeyValueStore/GarbageCollectionTest.php
index 4dc9b47..ec16864 100644
--- a/core/tests/Drupal/KernelTests/Core/KeyValueStore/GarbageCollectionTest.php
+++ b/core/tests/Drupal/KernelTests/Core/KeyValueStore/GarbageCollectionTest.php
@@ -21,13 +21,6 @@ class GarbageCollectionTest extends KernelTestBase {
    */
   public static $modules = array('system');
 
-  protected function setUp() {
-    parent::setUp();
-
-    // These additional tables are necessary due to the call to system_cron().
-    $this->installSchema('system', array('key_value_expire'));
-  }
-
   /**
    * Tests garbage collection.
    */
diff --git a/core/tests/Drupal/KernelTests/Core/Queue/QueueSerializationTest.php b/core/tests/Drupal/KernelTests/Core/Queue/QueueSerializationTest.php
index 3ea4c66..d1d2641 100644
--- a/core/tests/Drupal/KernelTests/Core/Queue/QueueSerializationTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Queue/QueueSerializationTest.php
@@ -77,7 +77,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
    */
   protected function setUp() {
     parent::setUp();
-    $this->installSchema('system', ['key_value_expire', 'sequences']);
+    $this->installSchema('system', ['sequences']);
     $this->installEntitySchema('user');
     $this->queue = \Drupal::service('queue.database')->get('aggregator_refresh');
     $test_user = User::create(array(
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
index 2c4a01b..f86e667 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -525,6 +525,7 @@ public function testSaveMismatch() {
     $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
       ->disableOriginalConstructor()
       ->getMock();
+
     $config_object->expects($this->atLeastOnce())
       ->method('isNew')
       ->will($this->returnValue(TRUE));
@@ -539,14 +540,16 @@ public function testSaveMismatch() {
       ->with('the_config_prefix.foo')
       ->will($this->returnValue($config_object));
 
+    $uuid = '7C1821EF-A96F-4BF0-B654-F683532EECB3';
     $this->entityQuery->expects($this->once())
       ->method('condition')
+      ->with('uuid', $uuid)
       ->will($this->returnSelf());
     $this->entityQuery->expects($this->once())
       ->method('execute')
       ->will($this->returnValue(array('baz')));
 
-    $entity = $this->getMockEntity(array('id' => 'foo'));
+    $entity = $this->getMockEntity(array('id' => 'foo', 'uuid' => $uuid));
     $this->entityStorage->save($entity);
   }
 
@@ -591,7 +594,8 @@ public function testSaveNoMismatch() {
       ->method('execute')
       ->will($this->returnValue(array('baz')));
 
-    $entity = $this->getMockEntity(array('id' => 'foo'));
+    $uuid = '7C1821EF-A96F-4BF0-B654-F683532EECB3';
+    $entity = $this->getMockEntity(array('id' => 'foo', 'uuid' => $uuid));
     $entity->setOriginalId('baz');
     $entity->enforceIsNew();
     $this->entityStorage->save($entity);
