We currently have two separate systems for dealing with pending tasks (which either failed before or take too much time on larger sites to do in a single request) – server tasks and index tasks. Both have much the same functionality, and the former is even somewhat extensible, but they still lack flexibility and duplicate a lot of code.

We should instead have a single task system for dealing with any Search API-related tasks and rewrite those two previous systems to build on the new one as far as possible.

One application for this should then be to also add task integration for config changes in a content entity datasource (where we currently might add thousands or even millions of new items to the tracking system in a single request – or, rather, fail horribly while trying to do that).

Estimated Value and Story Points

This issue was identified as a Beta Blocker for Drupal 8. We sat down and figured out the value proposition and amount of work (story points) for this issue.

Value and Story points are in the scale of fibonacci. Our minimum is 1, our maximum is 21. The higher, the more value or work a certain issue has.

Value : 3
Story Points: 8

CommentFileSizeAuthor
#49 2574611-49--fix_postgres.patch574 bytesdrunken monkey
#48 2574611-48--pg_fix_experiment.patch3.47 KBdrunken monkey
#48 2574611-48--pg_fix_experiment--interdiff.txt617 bytesdrunken monkey
#45 2574611-45--pg_fix_experiment.patch3.44 KBdrunken monkey
#42 2574611-42--experimental_partial_revert.patch1.37 KBdrunken monkey
#41 2574611-41--dummy.patch422 bytesdrunken monkey
#40 2574611-40--revert.patch133.23 KBdrunken monkey
#36 2574611-36--task_system_overhaul.patch133.2 KBdrunken monkey
#36 2574611-36--task_system_overhaul--interdiff.txt888 bytesdrunken monkey
#34 2574611-34--task_system_overhaul.patch134.33 KBdrunken monkey
#34 2574611-34--task_system_overhaul--interdiff.txt1.37 KBdrunken monkey
#32 2574611-32--task_system_overhaul.patch133.74 KBdrunken monkey
#32 2574611-32--task_system_overhaul--interdiff.txt24.33 KBdrunken monkey
#30 2574611-30--task_system_overhaul.patch113.07 KBdrunken monkey
#30 2574611-30--task_system_overhaul--interdiff.txt9.72 KBdrunken monkey
#26 2574611-26--task_system_overhaul.patch111.58 KBdrunken monkey
#26 2574611-26--task_system_overhaul--interdiff.txt7.73 KBdrunken monkey
#23 2574611-23--task_system_overhaul.patch108.02 KBdrunken monkey
#23 2574611-23--task_system_overhaul--interdiff.txt24.82 KBdrunken monkey
#20 2574611-20--task_system_overhaul.patch96.98 KBdrunken monkey
#20 2574611-20--task_system_overhaul--interdiff.txt763 bytesdrunken monkey
#17 2574611-17--task_system_overhaul.patch96.83 KBdrunken monkey
#17 2574611-17--task_system_overhaul--interdiff.txt31.08 KBdrunken monkey
#14 2574611-14--task_system_overhaul.patch69.46 KBdrunken monkey
#14 2574611-14--task_system_overhaul--interdiff.txt2.61 KBdrunken monkey
#11 2574611-11--task_system_overhaul.patch69.41 KBdrunken monkey
#11 2574611-11--task_system_overhaul--interdiff.txt457 bytesdrunken monkey
#7 2574611-4--task_system_overhaul.patch69.41 KBdrunken monkey

Comments

drunken monkey created an issue. See original summary.

nick_vh’s picture

Issue summary: View changes
Issue tags: +beta blocker
drunken monkey’s picture

Assigned: Unassigned » drunken monkey
Issue summary: View changes

Tackling this now, but currently in the process of succumbing to another onset of megalomania. (I.e., I'm revamping the two task systems into a single, more flexible one.)

drunken monkey’s picture

Title: Use a batch when a datasource's included bundles change » Unify our two task systems
Component: Plugins » Framework
Issue summary: View changes
Status: Active » Needs review
Issue tags: +Needs tests

Something like the attached. It uses events to execute the different types of tasks, thus staying fully flexible.

Still to do:

  • Actually using this for datasource config changes.
  • Replacing the "Execute pending tasks" local action with one for all types of tasks.
  • Same for the status report.

Still setting to "Needs review" to a) see whether the test bot is happy so far (the new functionality still needs tests, though) and b) to maybe get some feedback on what I've got so far. Will probably not work on it again until Monday or Tuesday.

borisson_’s picture

Status: Needs review » Active

I'd love to provide feedback on this, but I think you forgot to attach a patch.

drunken monkey’s picture

Status: Active » Needs review

So I did.
Damn.
Updated patch coming soon anyways, but I'm still interested in the test results here.

drunken monkey’s picture

StatusFileSize
new69.41 KB

Ah, now I get it! Forgot the ".patch" extension for the patch file so it got rejected. (Blame my thousands of small scripts which usually take care of this for me.)

Status: Needs review » Needs work

The last submitted patch, 7: 2574611-4--task_system_overhaul.patch, failed testing.

The last submitted patch, 7: 2574611-4--task_system_overhaul.patch, failed testing.

borisson_’s picture

At first glance, this looks great, I'd think that maybe we'd want to create a value object for executeSingleTask's argument instead of just using an array? Not sure though.

I'll look in more detail at this when there's less failures.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new457 bytes
new69.41 KB

I'd think that maybe we'd want to create a value object for executeSingleTask's argument instead of just using an array? Not sure though.

I don't think so. First of all, we'd need to do this for all $conditions arguments for the class's methods. And second, I don't think this is done anywhere in Drupal Core. Conditions on properties are still expressed with associative arrays, as far as I can see (e.g., \Drupal\Core\Entity\EntityStorageInterface::loadByProperties()).

The failures mostly seem to be a single syntax error in services.yml – which PhpStorm doesn't spot, for some reason. But you might as well wait for a thorough review until I have a patch I'd call RTBC. As long as you agree that the basic direction makes sense, nothing major will hopefully pop up then.

Status: Needs review » Needs work

The last submitted patch, 11: 2574611-11--task_system_overhaul.patch, failed testing.

The last submitted patch, 11: 2574611-11--task_system_overhaul.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new2.61 KB
new69.46 KB

And several more small mistakes, ranging from tricky to dilettantish.

Status: Needs review » Needs work

The last submitted patch, 14: 2574611-14--task_system_overhaul.patch, failed testing.

The last submitted patch, 14: 2574611-14--task_system_overhaul.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new31.08 KB
new96.83 KB

This adds the original objective of integration with datasource item tracking and also passes all PhpUnit tests, at least locally.

To do: local action, status report and tests.

Status: Needs review » Needs work

The last submitted patch, 17: 2574611-17--task_system_overhaul.patch, failed testing.

The last submitted patch, 17: 2574611-17--task_system_overhaul.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new763 bytes
new96.98 KB

Status: Needs review » Needs work

The last submitted patch, 20: 2574611-20--task_system_overhaul.patch, failed testing.

The last submitted patch, 20: 2574611-20--task_system_overhaul.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new24.82 KB
new108.02 KB

Discovered #2727667: Subsequent submit callbacks are handled incorrectly after batch is added and #2727697: Changes to the index are discarded when it is serialized, but in the end they both don't really apply here, since it's a bad idea to create the batch as soon as $datasource->setConfiguration() is called anyways – we should change the tracking only once the index has actually been saved, not just when a property is changed (which might not be saved after all).
This was, by the way, wrong before, too. However, I have to say this solution is a lot less elegant, with relying on a hook implementation completely separated from the rest of the datasource code. But to solve that, we'd have to either add some "index save" reaction method for datasources (or all index plugins), which might be a bit overkill (or pretty nice to have in general, too – would have to be determined) or re-think the whole "write property changes only on index save" mechanism (and we're not doing that unless we really have to – was enough effort getting it to work in the first place).

Anyways, worked the whole day on this, and still not finished. But the attached patch at least changes the local task and status report functionality to encompass all kinds of tasks, and should also fix a few more bugs. Hopefully I'll be able to finish this tomorrow, by fixing the remaining test fails and adding a few tests for the new functionality.

Status: Needs review » Needs work

The last submitted patch, 23: 2574611-23--task_system_overhaul.patch, failed testing.

The last submitted patch, 23: 2574611-23--task_system_overhaul.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new7.73 KB
new111.58 KB

Had to fix #2574881: Fix tracking race conditions by making tracker ignore duplicate inserts at the same time, too.
But at least that's also a beta blocker, so we get two for one here.

Status: Needs review » Needs work

The last submitted patch, 26: 2574611-26--task_system_overhaul.patch, failed testing.

The last submitted patch, 26: 2574611-26--task_system_overhaul.patch, failed testing.

borisson_’s picture

Awesome that we're going to fix the other issue in here as well, that's great. Reviewed the interdiff in #26 and had one small remark.

  1. +++ b/src/Entity/Index.php
    @@ -939,15 +939,28 @@ public function indexSpecificItems(array $search_objects) {
    +    ++$this->batchTracking;
    

    Wait, huh? This looks weird. Can we do $this->batchTracking++ instead? I had no idea this even worked.

  2. +++ b/src/Entity/Index.php
    @@ -939,15 +939,28 @@ public function indexSpecificItems(array $search_objects) {
    +      $args['%index'] = $this->label();
    +      $message = new FormattableMarkup('Trying to stop "batch tracking" mode on index %index which was not entered first.', $args);
    +      throw new SearchApiException($message);
    

    I recently got a comment that we shouldn't introduce new objects while throwing exceptions. see #2722271: Make sure as little code as possible is triggered when throwing an exception.

  3. +++ b/src/Entity/Index.php
    @@ -939,15 +939,28 @@ public function indexSpecificItems(array $search_objects) {
    +    --$this->batchTracking;
    

    Again, same thing as .1

  4. +++ b/src/Entity/Index.php
    @@ -980,7 +993,7 @@ public function trackItemsUpdated($datasource_id, array $ids) {
    -    if ($this->hasValidTracker() && $this->status() && \Drupal::getContainer()->get('search_api.index_task_manager')->isTrackingComplete($this)) {
    +    if ($this->hasValidTracker() && $this->status()) {
    

    So much better.

  5. +++ b/src/Task/IndexTaskManager.php
    @@ -86,9 +86,9 @@ public function trackItems(TaskEvent $event) {
    -          $index->setBatchTracking();
    +          $index->startBatchTracking();
               $index->trackItemsInserted($datasource_id, $raw_ids);
    -          $index->setBatchTracking(FALSE);
    +          $index->stopBatchTracking();
    

    Again, much more expressive, love this!

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new9.72 KB
new113.07 KB

Wait, huh? This looks weird. Can we do $this->batchTracking++ instead? I had no idea this even worked.

I dislike the postfix increment operators, and precedence-wise there shouldn't be any problem since -> has always had highest operator precedence, as far as I know (even though it's unfortunately not listed in the PHP docs). I didn't even really think about it, to be honest.
However, if it looks weird to you, it probably also does for others, so I changed it. Better I find it ugly than anyone finding it confusing. ;)

I recently got a comment that we shouldn't introduce new objects while throwing exceptions. see #2722271: Make sure as little code as possible is triggered when throwing an exception.

Oh, you're right, apparently this has even been in our coding standards for several months now! Created #2728399: Do not use FormattableMarkup in exceptions to take care of this project-wide.
But fixed it in this patch, too. Thanks!

Again, much more expressive, love this!

Thanks, glad to hear it. I thought so, too, and it fixes nested calls elegantly (though they're probably an edge case anyways).

The attached patch implements those changes and should also, finally, fix all tests. (Additional tests are still needed, though.)

drunken monkey’s picture

Damn Postgres tests …
Here's to hoping #2709899: php7 + postgres throws AlreadyInstalledException randomly. will get fixed soon!

drunken monkey’s picture

Issue tags: -Needs tests
StatusFileSize
new24.33 KB
new133.74 KB

Tests added, both for the general task manager functionality and the content entity datasource-specific code.

From my point of view, this is RTBC – eager to hear other comments, though, of course.

borisson_’s picture

Only briefly looked at the interdiff, will look at the big patch again later today. Everything looks solid.

  1. +++ b/src/Plugin/search_api/tracker/Basic.php
    @@ -151,9 +151,10 @@ public function trackItemsInserted(array $ids) {
    -        $existing = $this->createSelectStatement()
    -          ->fields('sai', array('item_id'))
    -          ->condition('item_id', $ids_chunk, 'IN')
    +        $select = $this->createSelectStatement()
    +          ->fields('sai', array('item_id'));
    +        $select->condition('item_id', $ids_chunk, 'IN');
    +        $existing = $select
    

    I don't understand why this change is needed?

  2. +++ b/tests/src/Kernel/DatasourceTaskTest.php
    @@ -0,0 +1,261 @@
    +  /**
    +   * Modules to enable for this test.
    +   *
    +   * @var string[]
    +   */
    

    {@inheritdoc} should work as well

  3. +++ b/tests/src/Kernel/TaskTest.php
    @@ -0,0 +1,296 @@
    +  /**
    +   * Modules to enable for this test.
    +   *
    +   * @var string[]
    +   */
    

    {@inheritdoc}

  4. +++ b/tests/src/Kernel/TaskTest.php
    @@ -0,0 +1,296 @@
    +    $tasks[0] = $this->addTask('success', $this->server, $this->index, array('foo' => 1, 'bar'));
    +    $tasks[6] = $this->addTask('fail');
    +    $tasks[1] = $this->addTask('success', $this->server, NULL, TRUE);
    +    $tasks[4] = $this->addTask('success', NULL, NULL, 1);
    +    $tasks[2] = $this->addTask('fail', $this->server, $this->index);
    +    $tasks[5] = $this->addTask('success');
    +    $tasks[3] = $this->addTask('success', NULL, $this->index);
    

    Is there a reason why these aren't in order?

drunken monkey’s picture

StatusFileSize
new1.37 KB
new134.33 KB

I don't understand why this change is needed?

So PhpStorm doesn't complain, since condition() is documented to return \Drupal\Core\Database\Query\ConditionInterface, and not $this – therefore, PhpStorm doesn't think it has an execute() method.

Is there a reason why these aren't in order?

Yes, because we want to make sure the correct ones are skipped when we apply conditions. If we just create them in the order they should be executed, we wouldn't really test the $conditions parameter at all.
But it probably deserves some explanation in a comment.

andypost’s picture

  1. +++ b/search_api.install
    @@ -61,17 +61,17 @@ function search_api_schema() {
    -      'server_id' => array(
    -        'description' => 'The ID of the server for which this task should be executed',
    +      'type' => array(
    +        'description' => 'A keyword identifying the type of task that should be executed',
             'type' => 'varchar',
             'length' => 50,
             'not null' => TRUE,
           ),
    -      'type' => array(
    -        'description' => 'A keyword identifying the type of task that should be executed',
    +      'server_id' => array(
    +        'description' => 'The ID of the server for which this task should be executed',
             'type' => 'varchar',
             'length' => 50,
    -        'not null' => TRUE,
    +        'not null' => FALSE,
    

    useless change

  2. +++ b/search_api.module
    @@ -42,7 +47,7 @@ function search_api_cron() {
    -  /** @var \Drupal\search_api\IndexInterface[] $indexes */
    +  /** @var IndexInterface[] $indexes */
    
    @@ -249,7 +257,7 @@ function search_api_entity_delete(EntityInterface $entity) {
    -  /** @var \Drupal\search_api\IndexInterface $index */
    +  /** @var IndexInterface $index */
    

    the same, should be FQDN

drunken monkey’s picture

StatusFileSize
new888 bytes
new133.2 KB

useless change

First off, no, it changes server_id to allow NULL.
Secondly, the order makes more sense this way, now that we always have a type, but the server might be missing. It's no functional difference, sure, but why should that stop us?

the same, should be FQDN

Ah, yes, thanks for spotting that!

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

  • drunken monkey committed b139353 on 8.x-1.x
    Issue #2574611 by drunken monkey: Unified the module's tasks systems.
    
drunken monkey’s picture

Status: Reviewed & tested by the community » Fixed

Great to hear, thanks for reviewing!
Committed.

drunken monkey’s picture

Status: Fixed » Needs review
StatusFileSize
new133.23 KB

Postgres is failing (apparently for real, this time) after this commit. The attached patch completely reverts the commit – let's see whether that would fix it.

drunken monkey’s picture

StatusFileSize
new422 bytes

Now a dummy patch to see the apparent test failures.

drunken monkey’s picture

StatusFileSize
new1.37 KB

Maybe this is the culprit?

andypost’s picture

Probably that's a pg related issue, suppose better to file follow-up
Also core itself as random failures on pg https://www.drupal.org/node/3060/qa

drunken monkey’s picture

Probably that's a pg related issue, suppose better to file follow-up

Of course it's a Postgres-related issue – that's why all tests pass in the other two environments. And yes, we're aware of the random fails: #2709899: php7 + postgres throws AlreadyInstalledException randomly.. But this is something different, the tests are really failing, and I'd like to fix that as soon as possible to avoid adding even more Postgres fails on top of those.

I have now managed to finally install Postgres locally and am in the process of debugging this issue.

drunken monkey’s picture

StatusFileSize
new3.44 KB

From what I can tell, there is just a glaring bug in Core's Entity Query implementation for Postgres. Switching to a normal DB query solved it for me locally (at least one test). Let's see what the test bot says.

Status: Needs review » Needs work

The last submitted patch, 45: 2574611-45--pg_fix_experiment.patch, failed testing.

The last submitted patch, 45: 2574611-45--pg_fix_experiment.patch, failed testing.

drunken monkey’s picture

Status: Needs work » Needs review
StatusFileSize
new617 bytes
new3.47 KB

Oh, er, like this, I mean.
But anyways, not a proper solution anyways.

drunken monkey’s picture

StatusFileSize
new574 bytes

This might actually be an option until the Core bug gets fixed.
(Gotta reproduce it with a Core entity type and then file it, first, though.)

drunken monkey’s picture

Couldn't reproduce it properly, unfortunately, but created the issue: #2734445: Postgres entity queries doesn't treat multiple IN conditions correctly.
And if no-one objects I'd like to just commit this workaround for now and then go on to more productive issues.

borisson_’s picture

Sounds good.

  • drunken monkey committed 1de76e6 on 8.x-1.x
    Follow-up to #2574611 by drunken monkey: Fixed Postgres regression due...
drunken monkey’s picture

Status: Needs review » Fixed

OK, good. Then: committed.
Thanks for your feedback!

Status: Fixed » Closed (fixed)

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