diff --git a/modules/search_api_db/tests/src/Kernel/AutocompleteTest.php b/modules/search_api_db/tests/src/Kernel/AutocompleteTest.php
index a58e619c..201c80cd 100644
--- a/modules/search_api_db/tests/src/Kernel/AutocompleteTest.php
+++ b/modules/search_api_db/tests/src/Kernel/AutocompleteTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\search_api_db\Kernel;
 
 use Drupal\KernelTests\KernelTestBase;
+use Drupal\search_api\Utility\Utility;
 use Drupal\search_api_autocomplete\Entity\Search;
 use Drupal\search_api_db\Plugin\search_api\backend\Database;
 use Drupal\Tests\search_api\Functional\ExampleContentTrait;
@@ -70,7 +71,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/src/Entity/Index.php b/src/Entity/Index.php
index e957074b..c1b80a6a 100644
--- a/src/Entity/Index.php
+++ b/src/Entity/Index.php
@@ -1326,7 +1326,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
         // batch for tracking items. Also, do not use a batch if running in the
         // CLI.
         $use_batch = \Drupal::state()->get('search_api_use_tracking_batch', TRUE);
-        if (!$use_batch || php_sapi_name() == 'cli') {
+        if (!$use_batch || Utility::isRunningInCli()) {
           $index_task_manager->addItemsAll($this);
         }
         else {
diff --git a/src/Plugin/search_api/datasource/ContentEntity.php b/src/Plugin/search_api/datasource/ContentEntity.php
index eb5bf56e..324ca27a 100644
--- a/src/Plugin/search_api/datasource/ContentEntity.php
+++ b/src/Plugin/search_api/datasource/ContentEntity.php
@@ -747,6 +747,13 @@ public function getPartialItemIds($page = NULL, array $bundles = NULL, array $la
       }
     }
 
+    if (Utility::isRunningInCli()) {
+      // When running in the CLI, this might be executed for all entities from
+      // within a single process. To avoid running out of memory, reset the
+      // static cache after each batch.
+      $this->getEntityStorage()->resetCache($entity_ids);
+    }
+
     return $item_ids;
   }
 
diff --git a/src/Utility/Utility.php b/src/Utility/Utility.php
index 4f81184e..856e4b6d 100644
--- a/src/Utility/Utility.php
+++ b/src/Utility/Utility.php
@@ -197,4 +197,14 @@ public static function getConfigOverrides(EntityInterface $entity) {
     return $overrides;
   }
 
+  /**
+   * Determines whether this PHP process is running on the command line.
+   *
+   * @return bool
+   *   TRUE if this PHP process is running via CLI, FALSE otherwise.
+   */
+  public static function isRunningInCli() {
+    return php_sapi_name() === 'cli';
+  }
+
 }
diff --git a/tests/src/Functional/SearchApiBrowserTestBase.php b/tests/src/Functional/SearchApiBrowserTestBase.php
index 4be8ecdd..20bd38d5 100644
--- a/tests/src/Functional/SearchApiBrowserTestBase.php
+++ b/tests/src/Functional/SearchApiBrowserTestBase.php
@@ -6,6 +6,7 @@
 use Drupal\node\Entity\NodeType;
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
+use Drupal\search_api\Utility\Utility;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -116,7 +117,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
   }
diff --git a/tests/src/Functional/ViewsTest.php b/tests/src/Functional/ViewsTest.php
index e631091f..d102f376 100644
--- a/tests/src/Functional/ViewsTest.php
+++ b/tests/src/Functional/ViewsTest.php
@@ -54,7 +54,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
   }
diff --git a/tests/src/Kernel/BackendTestBase.php b/tests/src/Kernel/BackendTestBase.php
index 67953e08..5ab5e570 100644
--- a/tests/src/Kernel/BackendTestBase.php
+++ b/tests/src/Kernel/BackendTestBase.php
@@ -12,6 +12,7 @@
 use Drupal\search_api\IndexInterface;
 use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\Query\ResultSetInterface;
+use Drupal\search_api\Utility\Utility;
 use Drupal\Tests\search_api\Functional\ExampleContentTrait;
 
 /**
@@ -71,7 +72,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/tests/src/Kernel/ConfigEntity/ConfigOverrideKernelTest.php b/tests/src/Kernel/ConfigEntity/ConfigOverrideKernelTest.php
index 27d360b8..4e4bd2d0 100644
--- a/tests/src/Kernel/ConfigEntity/ConfigOverrideKernelTest.php
+++ b/tests/src/Kernel/ConfigEntity/ConfigOverrideKernelTest.php
@@ -7,6 +7,7 @@
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
 use Drupal\search_api\Processor\ProcessorInterface;
+use Drupal\search_api\Utility\Utility;
 use Drupal\search_api_test\PluginTestTrait;
 
 /**
@@ -58,7 +59,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/tests/src/Kernel/Datasource/EntityStringIdTest.php b/tests/src/Kernel/Datasource/EntityStringIdTest.php
index 772fdf87..6d782875 100644
--- a/tests/src/Kernel/Datasource/EntityStringIdTest.php
+++ b/tests/src/Kernel/Datasource/EntityStringIdTest.php
@@ -6,6 +6,7 @@
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
+use Drupal\search_api\Utility\Utility;
 
 /**
  * Tests indexing entities that use string IDs.
@@ -72,7 +73,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/tests/src/Kernel/Datasource/LanguageKernelTest.php b/tests/src/Kernel/Datasource/LanguageKernelTest.php
index 0b6f3265..1f09742a 100644
--- a/tests/src/Kernel/Datasource/LanguageKernelTest.php
+++ b/tests/src/Kernel/Datasource/LanguageKernelTest.php
@@ -111,7 +111,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/tests/src/Kernel/Index/IndexImportTest.php b/tests/src/Kernel/Index/IndexImportTest.php
index 741ce950..5d7e8219 100644
--- a/tests/src/Kernel/Index/IndexImportTest.php
+++ b/tests/src/Kernel/Index/IndexImportTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\search_api\Kernel\Index;
 
 use Drupal\KernelTests\KernelTestBase;
+use Drupal\search_api\Utility\Utility;
 
 /**
  * Tests whether importing of index configuration works correctly.
@@ -50,7 +51,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/tests/src/Kernel/Processor/ProcessorTestBase.php b/tests/src/Kernel/Processor/ProcessorTestBase.php
index ad2707ba..1cfefcdd 100644
--- a/tests/src/Kernel/Processor/ProcessorTestBase.php
+++ b/tests/src/Kernel/Processor/ProcessorTestBase.php
@@ -82,7 +82,7 @@ public function setUp($processor = NULL) {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/tests/src/Kernel/System/CliTest.php b/tests/src/Kernel/System/CliTest.php
index 9a9f4edc..dae373d4 100644
--- a/tests/src/Kernel/System/CliTest.php
+++ b/tests/src/Kernel/System/CliTest.php
@@ -6,6 +6,7 @@
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
+use Drupal\search_api\Utility\Utility;
 
 /**
  * Tests Search API functionality when executed in the CLI.
@@ -62,7 +63,7 @@ public function setUp() {
 
     // Disable the use of batches for item tracking to simulate a CLI
     // environment.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
   }
diff --git a/tests/src/Kernel/System/CustomDataTypesTest.php b/tests/src/Kernel/System/CustomDataTypesTest.php
index ef844455..4c266e45 100644
--- a/tests/src/Kernel/System/CustomDataTypesTest.php
+++ b/tests/src/Kernel/System/CustomDataTypesTest.php
@@ -6,6 +6,7 @@
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
+use Drupal\search_api\Utility\Utility;
 
 /**
  * Tests custom data types integration.
@@ -67,7 +68,7 @@ public function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
diff --git a/tests/src/Kernel/Views/ViewsDisplayTest.php b/tests/src/Kernel/Views/ViewsDisplayTest.php
index e5169f9f..62d4edec 100644
--- a/tests/src/Kernel/Views/ViewsDisplayTest.php
+++ b/tests/src/Kernel/Views/ViewsDisplayTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\search_api\Kernel\Views;
 
 use Drupal\KernelTests\KernelTestBase;
+use Drupal\search_api\Utility\Utility;
 
 /**
  * Tests whether Views pages correctly create search display plugins.
@@ -43,7 +44,7 @@ protected function setUp() {
     // Do not use a batch for tracking the initial items after creating an
     // index when running the tests via the GUI. Otherwise, it seems Drupal's
     // Batch API gets confused and the test fails.
-    if (php_sapi_name() != 'cli') {
+    if (!Utility::isRunningInCli()) {
       \Drupal::state()->set('search_api_use_tracking_batch', FALSE);
     }
 
