diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
index bb17fe6..86202f6 100644
--- a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
+++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -70,6 +71,16 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
     $storage = $this->entityTypeManager->getStorage($entity_type_id);
+
+    // Entity type definition updates can change the schema by adding or
+    // removing entity tables (for example when switching an entity type from
+    // non-revisionable to revisionable), so CRUD operations on a field storage
+    // definition need to use the last installed entity type schema.
+    if ($storage instanceof SqlContentEntityStorage
+       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
+      $storage->setEntityType($last_installed_entity_type);
+    }
+
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionCreate($storage_definition);
     }
@@ -89,6 +100,16 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
     $storage = $this->entityTypeManager->getStorage($entity_type_id);
+
+    // Entity type definition updates can change the schema by adding or
+    // removing entity tables (for example when switching an entity type from
+    // non-revisionable to revisionable), so CRUD operations on a field storage
+    // definition need to use the last installed entity type schema.
+    if ($storage instanceof SqlContentEntityStorage
+       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
+      $storage->setEntityType($last_installed_entity_type);
+    }
+
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionUpdate($storage_definition, $original);
     }
@@ -108,6 +129,16 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
     // @todo Forward this to all interested handlers, not only storage, once
     //   iterating handlers is possible: https://www.drupal.org/node/2332857.
     $storage = $this->entityTypeManager->getStorage($entity_type_id);
+
+    // Entity type definition updates can change the schema by adding or
+    // removing entity tables (for example when switching an entity type from
+    // non-revisionable to revisionable), so CRUD operations on a field storage
+    // definition need to use the last installed entity type schema.
+    if ($storage instanceof SqlContentEntityStorage
+       && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) {
+      $storage->setEntityType($last_installed_entity_type);
+    }
+
     if ($storage instanceof FieldStorageDefinitionListenerInterface) {
       $storage->onFieldStorageDefinitionDelete($storage_definition);
     }
diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install
index 47241c2..c244fe4 100644
--- a/core/modules/comment/comment.install
+++ b/core/modules/comment/comment.install
@@ -184,3 +184,14 @@ function comment_update_8301() {
   $entity_type->set('entity_keys', $keys);
   $definition_update_manager->updateEntityType($entity_type);
 }
+
+/**
+ * Update the status field.
+ */
+function comment_update_8400() {
+  // The status field was promoted to an entity key in comment_update_8301(),
+  // which makes it NOT NULL in the default SQL storage, which means its storage
+  // definition needs to be updated as well.
+  $entity_definition_update_manager = \Drupal::service('entity.definition_update_manager');
+  $entity_definition_update_manager->updateFieldStorageDefinition($entity_definition_update_manager->getFieldStorageDefinition('status', 'comment'));
+}
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateBundleTest.php b/core/modules/migrate/tests/src/Kernel/MigrateBundleTest.php
index 7463295..780cce8 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateBundleTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateBundleTest.php
@@ -18,13 +18,14 @@ class MigrateBundleTest extends MigrateTestBase {
    *
    * @var array
    */
-  public static $modules = ['taxonomy', 'text'];
+  public static $modules = ['taxonomy', 'text', 'user'];
 
   /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
+    $this->installEntitySchema('user');
     $this->installEntitySchema('taxonomy_vocabulary');
     $this->installEntitySchema('taxonomy_term');
     $this->installConfig(['taxonomy']);
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateRollbackEntityConfigTest.php b/core/modules/migrate/tests/src/Kernel/MigrateRollbackEntityConfigTest.php
index 76945a7..a639959 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateRollbackEntityConfigTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateRollbackEntityConfigTest.php
@@ -17,13 +17,14 @@ class MigrateRollbackEntityConfigTest extends MigrateTestBase {
    *
    * @var array
    */
-  public static $modules = ['field', 'taxonomy', 'text', 'language', 'config_translation'];
+  public static $modules = ['field', 'taxonomy', 'text', 'language', 'config_translation', 'user'];
 
   /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
+    $this->installEntitySchema('user');
     $this->installEntitySchema('taxonomy_vocabulary');
     $this->installEntitySchema('taxonomy_term');
     $this->installConfig(['taxonomy']);
diff --git a/core/modules/migrate/tests/src/Kernel/MigrateRollbackTest.php b/core/modules/migrate/tests/src/Kernel/MigrateRollbackTest.php
index 4b78e65..427f5bc 100644
--- a/core/modules/migrate/tests/src/Kernel/MigrateRollbackTest.php
+++ b/core/modules/migrate/tests/src/Kernel/MigrateRollbackTest.php
@@ -20,13 +20,14 @@ class MigrateRollbackTest extends MigrateTestBase {
    *
    * @var array
    */
-  public static $modules = ['field', 'taxonomy', 'text'];
+  public static $modules = ['field', 'taxonomy', 'text', 'user'];
 
   /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
+    $this->installEntitySchema('user');
     $this->installEntitySchema('taxonomy_vocabulary');
     $this->installEntitySchema('taxonomy_term');
     $this->installConfig(['taxonomy']);
diff --git a/core/modules/system/src/Tests/Update/EntityUpdateToRevisionableAndPublishableTest.php b/core/modules/system/src/Tests/Update/EntityUpdateToRevisionableAndPublishableTest.php
new file mode 100644
index 0000000..8f480d1
--- /dev/null
+++ b/core/modules/system/src/Tests/Update/EntityUpdateToRevisionableAndPublishableTest.php
@@ -0,0 +1,216 @@
+<?php
+
+namespace Drupal\system\Tests\Update;
+
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\system\Tests\Entity\EntityDefinitionTestTrait;
+
+/**
+ * Tests the upgrade path for making an entity revisionable and publishable.
+ *
+ * @see https://www.drupal.org/node/2841291
+ *
+ * @group Update
+ */
+class EntityUpdateToRevisionableAndPublishableTest extends UpdatePathTestBase {
+
+  use EntityDefinitionTestTrait;
+  use DbUpdatesTrait;
+
+  /**
+   * The entity manager service.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The entity definition update manager.
+   *
+   * @var \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
+   */
+  protected $entityDefinitionUpdateManager;
+
+  /**
+   * The last installed schema repository service.
+   *
+   * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
+   */
+  protected $lastInstalledSchemaRepository;
+
+  /**
+   * The key-value collection for tracking installed storage schema.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  protected $installedStorageSchema;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->entityManager = \Drupal::entityManager();
+    $this->entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
+    $this->lastInstalledSchemaRepository = \Drupal::service('entity.last_installed_schema.repository');
+    $this->installedStorageSchema = \Drupal::keyValue('entity.storage_schema.sql');
+    $this->state = \Drupal::state();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setDatabaseDumpFiles() {
+    $this->databaseDumpFiles = [
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.0.0-rc1-filled.standard.entity_test_update_mul.php.gz',
+      __DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.entity-test-schema-converter-enabled.php',
+    ];
+  }
+
+  /**
+   * Tests the conversion of an entity type to revisionable and publishable.
+   */
+  public function testConvertToRevisionableAndPublishable() {
+    // Check that entity type is not revisionable nor publishable prior to
+    // running the update process.
+    $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+    $this->assertFalse($entity_test_update->isRevisionable());
+    $this->assertFalse($entity_test_update->getKey('published'));
+
+    // Make the entity type revisionable, translatable and publishable.
+    $this->updateEntityTypeDefinition();
+
+    $this->enableUpdates('entity_test_update', 'entity_rev_pub_updates', 8400);
+    $this->runUpdates();
+
+    /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_test_update */
+    $entity_test_update = $this->lastInstalledSchemaRepository->getLastInstalledDefinition('entity_test_update');
+    $this->assertTrue($entity_test_update->isRevisionable());
+    $this->assertEqual('status', $entity_test_update->getKey('published'));
+
+    /** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */
+    $storage = \Drupal::entityTypeManager()->getStorage('entity_test_update');
+    $this->assertEqual(count($storage->loadMultiple()), 102, 'All test entities were found.');
+
+    // The conversion to revisionable is already tested by
+    // \Drupal\system\Tests\Entity\Update\SqlContentEntityStorageSchemaConverterTest::testMakeRevisionable()
+    // so we only need to check that some special cases are handled.
+    // All the checks implemented here are taking into consideration the special
+    // conditions in which the test database was created.
+    // @see _entity_test_update_create_test_entities()
+
+    // The test entity with ID 50 was created before Content Translation was
+    // enabled, which means it didn't have a 'content_translation_status' field.
+    // content_translation_update_8400() added values for that field which
+    // should now be reflected in the entity's 'status' field.
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
+    $revision = $storage->loadRevision(50);
+    $this->assertEqual(1, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(1, $translation->status->value);
+
+    // The test entity with ID 100 was created with Content Translation enabled
+    // and it should have the same values as entity 50.
+    $revision = $storage->loadRevision(100);
+    $this->assertEqual(1, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(1, $translation->status->value);
+
+    // The test entity 101 had 'content_translation_status' set to 0 for the
+    // English (source) language.
+    $revision = $storage->loadRevision(101);
+    $this->assertEqual(0, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(1, $translation->status->value);
+
+    // The test entity 102 had 'content_translation_status' set to 0 for the
+    // Romanian language.
+    $revision = $storage->loadRevision(102);
+    $this->assertEqual(1, $revision->status->value);
+
+    $translation = $revision->getTranslation('ro');
+    $this->assertEqual(0, $translation->status->value);
+  }
+
+  /**
+   * Updates the 'entity_test_update' entity type to revisionable,
+   * translatable, publishable and adds revision metadata keys.
+   */
+  protected function updateEntityTypeDefinition() {
+    $entity_type = clone $this->entityManager->getDefinition('entity_test_update');
+
+    $keys = $entity_type->getKeys();
+    $keys['revision'] = 'revision_id';
+    $keys['published'] = 'status';
+    $entity_type->set('entity_keys', $keys);
+
+    $revision_metadata_keys = [
+      'revision_user' => 'revision_user',
+      'revision_created' => 'revision_created',
+      'revision_log_message' => 'revision_log_message'
+    ];
+    $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
+
+
+    $entity_type->set('translatable', TRUE);
+    $entity_type->set('data_table', 'entity_test_update_data');
+    $entity_type->set('revision_table', 'entity_test_update_revision');
+    $entity_type->set('revision_data_table', 'entity_test_update_revision_data');
+
+    $this->state->set('entity_test_update.entity_type', $entity_type);
+
+    // Also add the status and revision metadata base fields to the entity type.
+    $status = BaseFieldDefinition::create('boolean')
+      ->setLabel(t('Publishing status'))
+      ->setDescription(t('A boolean indicating the published state.'))
+      ->setRevisionable(TRUE)
+      ->setTranslatable(TRUE)
+      ->setRequired(TRUE)
+      ->setDefaultValue(TRUE);
+
+    $revision_created = BaseFieldDefinition::create('created')
+      ->setLabel(t('Revision create time'))
+      ->setDescription(t('The time that the current revision was created.'))
+      ->setRevisionable(TRUE);
+
+    $revision_user = BaseFieldDefinition::create('entity_reference')
+      ->setLabel(t('Revision user'))
+      ->setDescription(t('The user ID of the author of the current revision.'))
+      ->setSetting('target_type', 'user')
+      ->setRevisionable(TRUE);
+
+    $revision_log_message = BaseFieldDefinition::create('string_long')
+      ->setLabel(t('Revision log message'))
+      ->setDescription(t('Briefly describe the changes you have made.'))
+      ->setRevisionable(TRUE)
+      ->setDefaultValue('')
+      ->setDisplayOptions('form', [
+        'type' => 'string_textarea',
+        'weight' => 25,
+        'settings' => [
+          'rows' => 4,
+        ],
+      ]);
+
+    $this->state->set('entity_test_update.additional_base_field_definitions', [
+      'status' => $status,
+      'revision_created' => $revision_created,
+      'revision_user' => $revision_user,
+      'revision_log_message' => $revision_log_message,
+    ]);
+
+    $this->entityManager->clearCachedDefinitions();
+  }
+
+}
diff --git a/core/modules/system/tests/modules/entity_test_update/entity_test_update.install b/core/modules/system/tests/modules/entity_test_update/entity_test_update.install
new file mode 100644
index 0000000..1423308
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test_update/entity_test_update.install
@@ -0,0 +1,10 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the Entity Test Update module.
+ */
+
+use Drupal\system\Tests\Update\DbUpdatesTrait;
+
+DbUpdatesTrait::includeUpdates('entity_test_update', 'entity_rev_pub_updates');
diff --git a/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc b/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc
new file mode 100644
index 0000000..988505d
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test_update/update/entity_rev_pub_updates_8400.inc
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Defines the 8400 db update for the "entity_rev_pub_updates" group.
+ */
+
+use Drupal\Core\Field\BaseFieldDefinition;
+
+/**
+ * Implements hook_update_dependencies().
+ */
+function entity_test_update_update_dependencies() {
+  // The update function that adds the status field must run after
+  // content_translation_update_8400() which fixes NULL values for the
+  // 'content_translation_status' field.
+  $dependencies['entity_test_update'][8400] = [
+    'content_translation' => 8400,
+  ];
+
+  return $dependencies;
+}
+
+/**
+ * Add the 'published' and revisionable metadata fields to entity_test_update.
+ */
+function entity_test_update_update_8400() {
+  $definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+
+  // Add the published entity key and revisionable metadata fields to the
+  // entity_test_update entity type.
+  $entity_type = $definition_update_manager->getEntityType('entity_test_update');
+
+  $entity_keys = $entity_type->getKeys();
+  $entity_keys['published'] = 'status';
+  $entity_type->set('entity_keys', $entity_keys);
+
+  $revision_metadata_keys = [
+    'revision_user' => 'revision_user',
+    'revision_created' => 'revision_created',
+    'revision_log_message' => 'revision_log_message'
+  ];
+  $entity_type->set('revision_metadata_keys', $revision_metadata_keys);
+
+  $definition_update_manager->updateEntityType($entity_type);
+
+  // Add the status field.
+  $status = BaseFieldDefinition::create('boolean')
+    ->setLabel(t('Publishing status'))
+    ->setDescription(t('A boolean indicating the published state.'))
+    ->setRevisionable(TRUE)
+    ->setTranslatable(TRUE)
+    ->setDefaultValue(TRUE);
+
+  $has_content_translation_status_field = \Drupal::moduleHandler()->moduleExists('content_translation') && $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'entity_test_update');
+  if ($has_content_translation_status_field) {
+    $status->setInitialValueFromField('content_translation_status');
+  }
+  else {
+    $status->setInitialValue(TRUE);
+  }
+  $definition_update_manager->installFieldStorageDefinition('status', 'entity_test_update', 'entity_test_update', $status);
+
+  // Add the revision metadata fields.
+  $revision_created = BaseFieldDefinition::create('created')
+    ->setLabel(t('Revision create time'))
+    ->setDescription(t('The time that the current revision was created.'))
+    ->setRevisionable(TRUE);
+  $definition_update_manager->installFieldStorageDefinition('revision_created', 'entity_test_update', 'entity_test_update', $revision_created);
+
+  $revision_user = BaseFieldDefinition::create('entity_reference')
+    ->setLabel(t('Revision user'))
+    ->setDescription(t('The user ID of the author of the current revision.'))
+    ->setSetting('target_type', 'user')
+    ->setRevisionable(TRUE);
+  $definition_update_manager->installFieldStorageDefinition('revision_user', 'entity_test_update', 'entity_test_update', $revision_user);
+
+  $revision_log_message = BaseFieldDefinition::create('string_long')
+    ->setLabel(t('Revision log message'))
+    ->setDescription(t('Briefly describe the changes you have made.'))
+    ->setRevisionable(TRUE)
+    ->setDefaultValue('')
+    ->setDisplayOptions('form', [
+      'type' => 'string_textarea',
+      'weight' => 25,
+      'settings' => [
+        'rows' => 4,
+      ],
+    ]);
+  $definition_update_manager->installFieldStorageDefinition('revision_log_message', 'entity_test_update', 'entity_test_update', $revision_log_message);
+
+  // Uninstall the 'content_translation_status' field if needed.
+  $database = \Drupal::database();
+  if ($has_content_translation_status_field) {
+    // First we have to remove the field data.
+    $database->update($entity_type->getDataTable())
+      ->fields(['content_translation_status' => NULL])
+      ->execute();
+
+    // A site may have disabled revisionability for this entity type.
+    if ($entity_type->isRevisionable()) {
+      $database->update($entity_type->getRevisionDataTable())
+        ->fields(['content_translation_status' => NULL])
+        ->execute();
+    }
+
+    $content_translation_status = $definition_update_manager->getFieldStorageDefinition('content_translation_status', 'entity_test_update');
+    $definition_update_manager->uninstallFieldStorageDefinition($content_translation_status);
+  }
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
index 92733bc..a066616 100644
--- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php
@@ -777,6 +777,11 @@ public function testBaseFieldEntityKeyUpdateWithExistingData() {
     // of a NOT NULL constraint.
     $this->makeBaseFieldEntityKey();
 
+    // Field storage CRUD operations use the last installed entity type
+    // definition so we need to update it before doing any other field storage
+    // updates.
+    $this->entityDefinitionUpdateManager->updateEntityType($this->state->get('entity_test_update.entity_type'));
+
     // Try to apply the update and verify they fail since we have a NULL value.
     $message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
     try {
