Not sure if this is a bug or my fault, but my search_api_task table has over 6 million rows in it that all have a type of "deleteItems". It looks like this:
mysql> select * from search_api_task limit 10;
+--------+-----------+-------------+--------------------------+------------------------+
| id | server_id | type | index_id | data |
+--------+-----------+-------------+--------------------------+------------------------+
| 532555 | solr | deleteItems | files_for_download_index | a:1:{i:0;s:5:"11705";} |
| 532556 | solr | deleteItems | products_index | a:1:{i:0;s:5:"11673";} |
| 532557 | solr | deleteItems | files_for_download_index | a:1:{i:0;s:5:"11673";} |
| 532558 | solr | deleteItems | products_index | a:1:{i:0;s:5:"11706";} |
| 532559 | solr | deleteItems | files_for_download_index | a:1:{i:0;s:5:"11706";} |
| 532560 | solr | deleteItems | products_index | a:1:{i:0;s:5:"11707";} |
| 532561 | solr | deleteItems | files_for_download_index | a:1:{i:0;s:5:"11707";} |
| 532562 | solr | deleteItems | products_index | a:1:{i:0;s:5:"11708";} |
| 532563 | solr | deleteItems | files_for_download_index | a:1:{i:0;s:5:"11708";} |
| 532564 | solr | deleteItems | products_index | a:1:{i:0;s:5:"11709";} |
+--------+-----------+-------------+--------------------------+------------------------+
They are all solr, deleteItems and are split roughly 50/50 between my 2 indexes. Every time the cron runs, the following query hangs, and although it doesn't crash mysql, I end up killing it after 45+ minutes:
SELECT t.* FROM search_api_task t INNER JOIN search_api_server s ON t.server_id = s.machine_name ....
We are in the final stages of development, and have done a lot of deleting of test data and importing of new data, on a moderately sized site (~10,000 nodes).
Any help appreciated.
Comments
Comment #1
drunken monkeyRemove all indexes from the server, then re-add them. That should do it.
Otherwise, just deleting them from the database should probably be fine, too.
PS: It seems you (like many others – it's really easy to misinterpret) are confused by the "Issue tags" field. As the guidelines state, they aren't meant for free text tags related to the issue, but only for specific categorization purposes, usually by module maintainers.
So, if you aren't sure your current usage is correct, please just leave the field empty.
Comment #3
cweagansPretty sure this is a bug. I ran into this too, and have 14,938,677 rows in my
search_api_tasktable. This has the very unfortunate side effect of really messing up node save performance. Any idea why this might be happening?Comment #4
cweagans(Also, removing all indexes from the server and re-adding it is not really an option on a production site that takes forever to reindex)
Comment #5
drunken monkeyDepends on what kinds of operations those are. But, in general, some operation seems to be consistently failing, filling up this table. Do you maybe have an unreachable Solr server set up, or something? Try to look into your logs to see what the error might be.
In any case, depending on the kind of tasks, you can execute them manually (e.g., write a script that deletes the items in question from the server) and then truncate the
search_api_tasktable.Comment #6
kenorb commentedI've similar problem (sorry if it's not the same). The indexing is stuck on
Initializingwhen run from UI. I've increased limit of max_execution_time to 120, but it didn't help. It still processing a lot of data, making hundreds of requests, despite I've requested only 10 items to index.I've used DTrace to see what the script is doing and half a minute of tracing generated millions of entries.
It seems like Search API keep trying to
deleteItems()and keepSearchApiSolrService connect().Here is my trace file: https://gist.github.com/kenorb/110d9e3f4b266f82c166
I'm using Solr 5.5.0, Drupal 7.39
My Solr doesn't have any indexed docs, so I'm not sure what Search API is trying to delete. It may attempt to remove some nodes which were on other instance and since I'm using dump from other server on my local on empty Solr instance, SAPI is confused.
Here is a dump after 120 second timeout which may be useful:
Comment #7
kenorb commentedBacktrace and local vars:

Most calls are from these functions below (~100.000 calls in 120 seconds), sorted by number of calls:
Comment #8
kenorb commentedI've found out that in search_api_task table I've a lot of tasks to delete the items, not sure how these records get there:
5mln items to remove?;/
Workaround:
drush sqlq "SELECT COUNT(*) FROM search_api_task"The batch worked! But how this happened, I don't know.
Btw. Shouldn't search_api_task table be processed on cron run, not on site indexing?
Comment #9
kenorb commentedI've implemented this workaround:
so it'll clear the SAPI tasks during the deployment, but still no idea if that will happen again.
Comment #10
drunken monkeyThe problem is that the "tasks" stored in that table should have already been executed, and we can't really know whether their order is relevant. So, doing any other operation before the pending tasks have been finished might result in erroneous behavior. (Think of trying to index before
addIndex()was called.)So, unfortunately I'm quite sure that the code is correct there, even though it can lead to problems, as we see here.
As to why there are so many tasks, I can't really say. But, confusingly, in your case these aren't tasks for deleting specific items, but all items from the server. Why it tried to do that 5 M times is beyond me.
Maybe try to add a debugging line to the code where it creates the task and log a backtrace, or something?
Also, see my other comments in this issue: disabled servers are evil and should be avoided.
Comment #11
gregsullivan commentedJust to add my case to this, for me it was this exactly:
Solr stopped working properly after a Java update, and I ended up with 2.5 million entries in my search_api_task table. This ended up prevented reindexing via the admin, drush, and cron.php, as the server would run out of memory trying to process the tasks.
It took awhile to debug, and I didn't find this thread until after I'd tracked down the issue; while that's on me, I do wonder if some sort of status message on the View Server page indicating that there are a very large number of entries in the search_api_task table could help. Because PHP was either failing with a fatal error or having its tasks killed for pushing up against the limit of the VPS's resources, I wasn't finding useful information in logs.
Thanks for all your work on this! My experience with Solr via the Search API has otherwise been a positive one.
Comment #12
GMahe commentedSame as gregsullivan the SOLR server wasn't reachable and end up inserting millions of rows in the table "search_api_task" and ends up using all of my cpu on the mysql daemon.
That ends up slowing down the login process at a point we had to wait more that a minute to be able to log in...
Even after reconfiguring the solr to be reachable it was still slow, not as bad though.
I had to truncate the search_api_task table and it worked much faster
Comment #13
drunken monkeyI think we've solved this a lot better now in Drupal 8, but some improvements would surely also be possible in Drupal 7.
However, since I'm currently fully occupied (in my contrib time) with the D8 port, I probably won't be able to work on this in the foreseeable future. But I'd be happy to give pointers, if someone else wants to write a patch.
Comment #14
janusman commented@drunken_monkey: Pointers please! :) Perhaps you could summarize how this was improved in D8 to guide us coding any D7 improvements.
Comment #15
mrdalesmith commentedBeen hit by this too: Solr unavailable for a couple of days on a site indexing 146297 taxonomy terms led to 19393509 items in search_api_task. Causes fatal memory time outs on every page, as the index was set to update immediately.
Truncating table and reindexing has allowed the site to survive, but would be good to work out a way to prevent this kind of behaviour.
Comment #16
drunken monkeyIn Drupal 8, we never just execute all the tasks (at least outside of tests), but instead just to a maximum of 100 tasks – see
\Drupal\search_api\Task\ServerTaskManager::execute(). If there are more than 100 tasks, this will always report failure to execute the tasks, but will still execute and remove 100 of them – so, steadily, all tasks will be gone.There is also the possibility on the server's "View" tab to execute all pending tasks for the server at once, in a batch (
\Drupal\search_api\Form\ServerStatusForm::buildForm()and\Drupal\search_api\Task\TaskManager::setTasksBatch()). This also gives users a chance to do something about large numbers of tasks.However, for over a million tasks, this might still not be enough. So, maybe, additionally we should provide a button (both in D7 and D8) to just delete all tasks? With a strict warning that this might mess up the search?
I hope this helps!
One last note: If you're looking at the D8 code, you have to be aware that we extended the tasks system a bit there, so that there aren't just server tasks, but also index tasks and tasks for individual datasources (and anything else that might want to create tasks). So the corresponding D7 code would probably be a bit simpler.
Comment #17
mrdalesmith commentedIs there any kind of checking going on about duplication in the tasks? The table was too big for me to query or export so I couldn't tell, but my instinct tells me there weren't 19m unique activities: most seemed to be deletions, and I suspect at least some might have been duplicated when the processing found that Solr was unavailable.
Comment #18
drunken monkeyIn theory, we only create one task for such an event (e.g., item deletion), and delete all tasks which we attempted to execute. So even if the deletion also fails during task execution, and thus generates another task, the old task should be removed, and no duplication should occur.
However, once there are too many tasks and the function executing tasks runs out of time or memory, new tasks for ones whose execution failed will still be created, but the old ones not removed (since that would only happen at the end of the function – which will never be reached, since PHP runs out of time/memory before that) – so, yes, then you'd have duplicates. Furthermore, even if tasks are successfully executed, they wouldn't be removed from the pending tasks anymore (but no new ones are created for them in this case).
So, thinking about that, I guess we'll have to categorize this as a bug after all.
@ janusman: Any progress with a patch?
I'm still too caught up in D8 for the moment, but if you bump this issue after the Search API D8 version becomes stable, I can also work on the patch. (Will still take a few months until then, though.)
Comment #19
julianmancera commentedHi all,
I'm also having this issue, do you have any pointers for the back port?
Thank you
Julian
Comment #20
drunken monkeyThe attached patch should at least implement the 100 tasks limit, which will help to avoid crashing anything. Indexing will still not work while there are tasks remaining, but at least they'll get fewer over time, not more.
The code for having an "Execute tasks" local action is mostly in
\Drupal\search_api\Task\TaskManager::setTasksBatch()(and the methods mentioned there).For D7, the simplest
processbatch callback would probably be just callingsearch_api_server_tasks_check()untill it returnsTRUE. (Would not have a progress indicator unless you manually check the task count between each call, but should otherwise work well enough.)Then you'll just need to add a local action through
search_api_menu()– either one directly on the overview, or for each server individually (could also be a button, in the latter case, to keep with the UI pattern we already use) – that comes with an access check that only shows it if there are actual tasks to be executed.Bonus would be also porting the "pending tasks" check in
search_api_requirements(), to notify admins that there are tasks they could execute.Comment #21
OliverColeman commented#20 Worked for me. Was experiencing out of memory issue (even with a 1GB limit).
Comment #23
drunken monkeyGreat to hear, thanks a lot for reporting back!
Committed the patch. Back to "Active" for the batch.
Comment #24
swim commented#20 worked for us as well. We were seeing a large number of remaining entries in search_api_task after indexing. Thanks Drunken Monkey =).
Comment #25
swim commentedInitial attempt at backporting the d8 functionality of exposing a method to execute pending tasks for a given server. Additionally, providing warnings on the status report page if pending tasks exist. Kept with the current UI pattern over a local action / task (server page).
Comment #26
drunken monkeyThanks a lot for taking this on, looks great already!
This revision also adds an option for executing pending tasks for all servers, makes the batch a bit more robust and prevents tasks from being executed on a disabled server, plus a few style changes.
Please test/review!
If you want to add a Drush command, too, go ahead! Does sound like a good idea.
Otherwise, though, we can just remove that
@todoand create a follow-up issue.In theory, I don't see any problem with running this via Drush.
Comment #27
swim commentedDrush command for executing pending tasks with an optional server argument added. Let me know if you're after an interdiff.
Comment #28
drunken monkeyGreat job, thanks!
Yes, an interdiff would have been nice, since you not only changed the Drush file, but since lots of people are lazy that way, I already have a script setup which just takes me five extra seconds. So no big problem.
Just three remarks regarding the code:
It's
bool, notBooelan, for type hints.I think if an invalid server was passed we should just abort, not execute for all servers (as this seems to be doing).
We can just use
function_exists()here and avoid the extra param.Revised patch attached, please tell me if it still works for you!
Comment #29
drunken monkeyOops …
Comment #30
drunken monkeyCould maybe someone review and confirm this works for them? Then we could commit this.
Comment #31
swim commentedAhh I'm all over the place atm; sorry I didn't get a chance to test until today. Works wonderfully for us! Thanks heaps Drunken Monkey =).
Comment #33
drunken monkeyAlright, thanks a lot for testing!
Committed.