diff --git a/core/modules/comment/src/CommentStorage.php b/core/modules/comment/src/CommentStorage.php
index 2d5541e..2604b13 100644
--- a/core/modules/comment/src/CommentStorage.php
+++ b/core/modules/comment/src/CommentStorage.php
@@ -221,6 +221,18 @@ public function getChildCids(array $comments) {
 
   /**
    * {@inheritdoc}
+   */
+  public function getCommentingUserIds(EntityInterface $entity) {
+    return $this->database->query('SELECT DISTINCT c.uid FROM {comment_field_data} c WHERE entity_id = :entity_id
+                                    AND entity_type=:entity_type
+                                    AND default_langcode = 1', array(
+      ':entity_id' => $entity->id(),
+      ':entity_type' => $entity->getEntityTypeId()
+    ))->fetchCol();
+  }
+
+  /**
+   * {@inheritdoc}
    *
    * To display threaded comments in the correct order we keep a 'thread' field
    * and order by that value. This field keeps this data in
diff --git a/core/modules/comment/src/CommentStorageInterface.php b/core/modules/comment/src/CommentStorageInterface.php
index 9087e5c..0fdf993 100644
--- a/core/modules/comment/src/CommentStorageInterface.php
+++ b/core/modules/comment/src/CommentStorageInterface.php
@@ -78,7 +78,7 @@ public function getDisplayOrdinal(CommentInterface $comment, $comment_mode, $div
   /**
    * Gets the comment ids of the passed comment entities' children.
    *
-   * @param array $comments
+   * @param \Drupal\comment\CommentInterface[] $comments
    *   An array of comment entities keyed by their ids.
    * @return array
    *   The entity ids of the passed comment entities' children as an array.
@@ -86,6 +86,17 @@ public function getDisplayOrdinal(CommentInterface $comment, $comment_mode, $div
   public function getChildCids(array $comments);
 
   /**
+   * Gets the ids of users that commented on a given parent entity.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   User object.
+   *
+   * @return int[]
+   *   Array of user ids.
+   */
+  public function getCommentingUserIds(EntityInterface $entity);
+
+  /**
    * Retrieves comments for a thread, sorted in an order suitable for display.
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
diff --git a/core/modules/comment/src/Tests/CommentLanguageTest.php b/core/modules/comment/src/Tests/CommentLanguageTest.php
index a12194a..ed63d4d 100644
--- a/core/modules/comment/src/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/src/Tests/CommentLanguageTest.php
@@ -112,17 +112,14 @@ function testCommentLanguage() {
         $this->drupalPostForm(NULL, $edit, t('Save'));
 
         // Check that comment language matches the current content language.
-        $cid = db_select('comment_field_data', 'c')
-          ->fields('c', array('cid'))
+        $cids = \Drupal::entityQuery('comment')
           ->condition('entity_id', $node->id())
           ->condition('entity_type', 'node')
           ->condition('field_name', 'comment')
-          ->condition('default_langcode', 1)
-          ->orderBy('cid', 'DESC')
+          ->sort('cid', 'DESC')
           ->range(0, 1)
-          ->execute()
-          ->fetchField();
-        $comment = Comment::load($cid);
+          ->execute();
+        $comment = Comment::load(reset($cids));
         $args = array('%node_language' => $node_langcode, '%comment_language' => $comment->langcode->value, '%langcode' => $langcode);
         $this->assertEqual($comment->langcode->value, $langcode, format_string('The comment posted with content language %langcode and belonging to the node with language %node_language has language %comment_language', $args));
         $this->assertEqual($comment->comment_body->value, $comment_values[$node_langcode][$langcode], 'Comment body correctly stored.');
diff --git a/core/modules/comment/src/Tests/CommentLinksTest.php b/core/modules/comment/src/Tests/CommentLinksTest.php
index fe4b5aa..56f2b9f 100644
--- a/core/modules/comment/src/Tests/CommentLinksTest.php
+++ b/core/modules/comment/src/Tests/CommentLinksTest.php
@@ -153,7 +153,7 @@ function setEnvironment(array $info) {
         $this->comment = $comment;
       }
       else {
-        $cids = db_query("SELECT cid FROM {comment}")->fetchCol();
+        $cids = \Drupal::entityQuery('comment')->execute();
         entity_delete_multiple('comment', $cids);
         unset($this->comment);
       }
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index 6f1f819..d3d1612 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -8,6 +8,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\comment\CommentInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\node\Entity\Node;
 use Drupal\node\NodeInterface;
 use Drupal\Core\Session\AccountInterface;
 
@@ -37,36 +38,41 @@ function tracker_help($route_name, RouteMatchInterface $route_match) {
  * 'tracker.index_nid' is set to ((the last node ID that was indexed) - 1) and
  * used to select the nodes to be processed. If there are no remaining nodes to
  * process, 'tracker.index_nid' will be 0.
+ * This process does not run regularly on live sites, rather it updates tracking
+ * info once on an existing site just after the tracker module was installed.
  */
 function tracker_cron() {
   $state = \Drupal::state();
   $max_nid = $state->get('tracker.index_nid') ?: 0;
   if ($max_nid > 0) {
-    $batch_size = \Drupal::config('tracker.settings')->get('cron_index_limit');
     $last_nid = FALSE;
-    // @todo This should be actually filtering on the desired language and just
-    //   fall back to the default language.
-    $result = db_query_range('SELECT nid, uid, status FROM {node_field_data} WHERE nid <= :max_nid AND default_langcode = 1 ORDER BY nid DESC', 0, $batch_size, array(':max_nid' => $max_nid), array('target' => 'replica'));
-
     $count = 0;
 
-    foreach ($result as $row) {
+    $nids = \Drupal::entityQuery('node')
+      ->condition('nid', $max_nid, '<=')
+      ->sort('nid', 'DESC')
+      ->range(0, \Drupal::config('tracker.settings')->get('cron_index_limit'))
+      ->execute();
+
+    $nodes = Node::loadMultiple($nids);
+    foreach ($nodes as $nid => $node) {
+
       // Calculate the changed timestamp for this node.
-      $changed = _tracker_calculate_changed($row->nid);
+      $changed = _tracker_calculate_changed($node);
 
       // Remove existing data for this node.
       db_delete('tracker_node')
-        ->condition('nid', $row->nid)
+        ->condition('nid', $nid)
         ->execute();
       db_delete('tracker_user')
-        ->condition('nid', $row->nid)
+        ->condition('nid', $nid)
         ->execute();
 
       // Insert the node-level data.
       db_insert('tracker_node')
         ->fields(array(
-          'nid' => $row->nid,
-          'published' => $row->status,
+          'nid' => $nid,
+          'published' => $node->isPublished(),
           'changed' => $changed,
         ))
         ->execute();
@@ -74,35 +80,35 @@ function tracker_cron() {
       // Insert the user-level data for the node's author.
       db_insert('tracker_user')
         ->fields(array(
-          'nid' => $row->nid,
-          'published' => $row->status,
+          'nid' => $nid,
+          'published' => $node->isPublished(),
           'changed' => $changed,
-          'uid' => $row->uid,
+          'uid' => $node->getOwnerId(),
         ))
         ->execute();
 
-      $query = db_select('comment_field_data', 'c', array('target' => 'replica'));
-      // Force PostgreSQL to do an implicit cast by adding 0.
-      $query->addExpression('0 + :changed', 'changed', array(':changed' => $changed));
-      $query->addField('c', 'status', 'published');
-      $query->addField('c', 'entity_id', 'nid');
-      $query
-        ->distinct()
-        ->fields('c', array('uid'))
-        ->condition('c.entity_id', $row->nid)
-        ->condition('c.entity_type', 'node')
-        ->condition('c.uid', $row->uid, '<>')
-        ->condition('c.status', CommentInterface::PUBLISHED)
-        ->condition('c.default_langcode', 1);
-
-      // Insert the user-level data for the commenters (except if a commenter
-      // is the node's author).
-      db_insert('tracker_user')
-        ->from($query)
-        ->execute();
+      if ($node->isPublished()) {
+        // Insert the user-level data for the commenters (except if a commenter
+        // is the node's author).
+        $uids = \Drupal::entityManager()->getStorage('comment')->getCommentingUserIds($node);
+        if ($uids) {
+          $query = db_insert('tracker_user');
+          foreach ($uids as $uid) {
+            if ($uid != $node->getOwnerId()) {
+              $query->fields(array(
+                'uid' => $uid,
+                'nid' => $nid,
+                'published' => CommentInterface::PUBLISHED,
+                'changed' => $changed,
+              ));
+            }
+          }
+          $query->execute();
+        }
+      }
 
       // Note that we have indexed at least one node.
-      $last_nid = $row->nid;
+      $last_nid = $nid;
 
       $count++;
     }
@@ -269,23 +275,21 @@ function _tracker_add($nid, $uid, $changed) {
 /**
  * Picks the most recent timestamp between node changed and the last comment.
  *
- * @param $nid
- *   A node ID.
+ * @param \Drupal\node\NodeInterface $node
+ *   The node entity.
+ *
+ * @return int
+ *   The node changed timestamp, or most recent comment timestamp, whichever is
+ *   the greatest.
  *
- * @return
- *  The node changed timestamp, or most recent comment timestamp, whichever is
- *  the greatest.
+ * @todo Check if we should introduce 'language context' here, because the
+ *   callers may need different timestamps depending on the users' language?
  */
-function _tracker_calculate_changed($nid) {
-  // @todo This should be actually filtering on the desired language and just
-  //   fall back to the default language.
-  $changed = db_query('SELECT changed FROM {node_field_data} WHERE nid = :nid AND default_langcode = 1 ORDER BY changed DESC', array(':nid' => $nid), array('target' => 'replica'))->fetchField();
-  $latest_comment = db_query_range("SELECT cid, changed FROM {comment_field_data} WHERE entity_type = 'node' AND entity_id = :nid AND status = :status AND default_langcode = 1 ORDER BY changed DESC", 0, 1, array(
-    ':nid' => $nid,
-    ':status' => CommentInterface::PUBLISHED,
-  ), array('target' => 'replica'))->fetchObject();
-  if ($latest_comment && $latest_comment->changed > $changed) {
-    $changed = $latest_comment->changed;
+function _tracker_calculate_changed($node) {
+  $changed = $node->getChangedTime();
+  $latest_comment = \Drupal::service('comment.statistics')->read(array($node), 'node', FALSE);
+  if ($latest_comment && $latest_comment->last_comment_timestamp > $changed) {
+    $changed = $latest_comment->last_comment_timestamp;
   }
   return $changed;
 }
@@ -301,23 +305,24 @@ function _tracker_calculate_changed($nid) {
  *   The last changed timestamp of the node.
  */
 function _tracker_remove($nid, $uid = NULL, $changed = NULL) {
-  // @todo This should be actually filtering on the desired language and just
-  //   fall back to the default language.
-  $node = db_query('SELECT nid, status, uid, changed FROM {node_field_data} WHERE nid = :nid AND default_langcode = 1 ORDER BY changed DESC, status DESC', array(':nid' => $nid))->fetchObject();
+  $node = Node::load($nid);
 
   // The user only keeps their subscription if the node exists.
   if ($node) {
     // And they are the author of the node.
-    $keep_subscription = ($node->uid == $uid);
+    $keep_subscription = ($node->getOwnerId() == $uid);
 
     // Or if they have commented on the node.
     if (!$keep_subscription) {
       // Check if the user has commented at least once on the given nid.
-      $keep_subscription = db_query_range("SELECT COUNT(*) FROM {comment_field_data} WHERE entity_type = 'node' AND entity_id = :nid AND uid = :uid AND status = :status AND default_langcode = 1", 0, 1, array(
-        ':nid' => $nid,
-        ':uid' => $uid,
-        ':status' => CommentInterface::PUBLISHED,
-      ))->fetchField();
+      $keep_subscription = \Drupal::entityQuery('comment')
+        ->condition('entity_type', 'node')
+        ->condition('entity_id', $nid)
+        ->condition('uid', $uid)
+        ->condition('status', CommentInterface::PUBLISHED)
+        ->range(0, 1)
+        ->count()
+        ->execute();
     }
 
     // If we haven't found a reason to keep the user's subscription, delete it.
@@ -338,21 +343,21 @@ function _tracker_remove($nid, $uid = NULL, $changed = NULL) {
       // established the node's changed timestamp.
 
       // We just have to recalculate things from scratch.
-      $changed = _tracker_calculate_changed($nid);
+      $changed = _tracker_calculate_changed($node);
 
       // And then we push the out the new changed timestamp to our denormalized
       // tables.
       db_update('tracker_node')
         ->fields(array(
           'changed' => $changed,
-          'published' => $node->status,
+          'published' => $node->isPublished(),
         ))
         ->condition('nid', $nid)
         ->execute();
       db_update('tracker_node')
         ->fields(array(
           'changed' => $changed,
-          'published' => $node->status,
+          'published' => $node->isPublished(),
         ))
         ->condition('nid', $nid)
         ->execute();
