Problem/Motivation

When enabling a search index via CLI using drush using search api command or a features-import, the allowed memory size is being reached because all content entities are being loaded (and cached) at once.

Here is the general error and attached is the stack trace

PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in ../core/lib/Drupal/Core/Entity/ContentEntityBase.php on line 207

Proposed resolution

Breakup the indexing of content entities into smaller chunks and reset the content entity storage cache after each chunk is indexed.

Remaining tasks

  • Determine how many entities should be indexed per chunk.
  • Maybe make chunk size configurable.

User interface changes

N/A

API changes

N/A

Data model changes

N/A

Comments

jrockowitz created an issue. See original summary.

jrockowitz’s picture

StatusFileSize
new2.23 KB
drunken monkey’s picture

Title: Breakup indexing of content entities into smaller chunks to prevent memory limit issue » Breakup tracking of content entities into smaller chunks to prevent memory limit issue
Component: General code » Framework
Category: Task » Bug report
Status: Needs review » Postponed (maintainer needs more info)

This should already work, we have batching already implemented for this case, just on a higher layer. (That's the $page being passed to that method.)
(Also, this is not indexing, but tracking.)
Could you find out the exact backtrace of the call to that method, if it really does get $page = NULL passed? If it does get a page number, the only thing that could go wrong is you having the tracking batch size (search_api.settings:tracking_page_size config value) set too high for your site.

jrockowitz’s picture

My specific issue is that the indexing is happening via CLI which triggers every entity to be loaded and no batch processing is being triggered.

Below is the code where I saw CLI processing triggering every entity being indexed at once.

if (!$use_batch || php_sapi_name() == 'cli') {
  $index_task_manager->addItemsAll($this);
}

FROM: http://cgit.drupalcode.org/search_api/tree/src/Entity/Index.php#n1329

I think I might be the first person to have this problem because I am enabling the Search API on existing website with 40,000+ nodes. Most websites probably have already enabled Search API and then they start adding their nodes.

drunken monkey’s picture

Status: Postponed (maintainer needs more info) » Needs review
StatusFileSize
new11.51 KB

Again, it's "tracking", not "indexing".
In any case, though, your problem description makes sense. Indeed, if running in the CLI, after saving an index the module will try to get the complete set of items already tracked right away. This was implemented in #2624424: Indexes enabled with drush aren't tracked, since it seems Drush doesn't automatically execute batches we set. (If we could manually make Drush execute the batch, that would probably be the best solution. But I don't think we can/should call drush_backend_batch_process() right in the middle of the post-save phase. Maybe with a shutdown hook – might that work?)

However, while it does currently try to load all entities in one request/process, it will already do so in batches, not load them all at once. That means that most of your patch (the whole "chunks" code) is unnecessary, we simply need to clear the static cache after loading the entities:

diff --git a/src/Plugin/search_api/datasource/ContentEntity.php b/src/Plugin/search_api/datasource/ContentEntity.php
index eb5bf56e..695c691b 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 (php_sapi_name() === 'cli') {
+      // 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;
   }
 

However, the attached patch is much more massive, since I also took the opportunity of refactoring the php_sapi_name() === 'cli' check (which is used quite a bit throughout our code) to a helper method on the Utility class. But the effective portion of the patch is just the above.
Please try out whether this also resolves your problem!

And, to anyone that wants to review: Do you think the new method makes sense like this? It would actually have been great to somehow make it dependency-injectable (is that a word), i.e., an instance method on some service, so it can be mocked in tests (e.g., to really test CLI compliance in the CliTest, not some state setting). However, I don't really think that would make any sense from any other point of view, so it seemed like a bit much just for the sake of slighlty better tests. This way, it's really just as simple to use as the previous code, just a bit more … "clean"?
Anyways, I'd be glad for others' opinions on this!

borisson_’s picture

And, to anyone that wants to review: Do you think the new method makes sense like this? It would actually have been great to somehow make it dependency-injectable (is that a word), i.e., an instance method on some service, so it can be mocked in tests (e.g., to really test CLI compliance in the CliTest, not some state setting). However, I don't really think that would make any sense from any other point of view, so it seemed like a bit much just for the sake of slighlty better tests. This way, it's really just as simple to use as the previous code, just a bit more … "clean"?

I think this is a good idea. While it's not easy to mock this from a test it does make sense to extract this code, it makes it easier in the other code to get the actual meaning.

In any case, this patch looks solid from just a review but I haven't manually tested it yet.

jrockowitz’s picture

Status: Needs review » Reviewed & tested by the community

I have manually tested the patch via CLI and setting up the initial tracking of 30,000+ nodes worked fine.

The patch makes complete sense to me.

Marking this as RTBC.

Thanks

  • drunken monkey committed b9a0f3d on 8.x-1.x
    Issue #2907518 by drunken monkey, jrockowitz, borisson_: Fixed index...
drunken monkey’s picture

Status: Reviewed & tested by the community » Fixed

Thanks a lot for testing, good to hear it works!
Also thanks for your review, Joris, of course!
Committed.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.