Back in Drupal 7, when using the "Index items immediately" option, changed items wouldn't actually be indexed immediately (i.e., inside the update hook) but at the end of the page request. This had a bunch of advantages, mainly (perceived) performance, but also indexing problems not messing up the entity save.
For some reason, this is not the case anymore in Drupal 8, and if we ever made a conscious decision for that, I don't remember it anymore. I also can't really think why it would be the better option here, as the only advantage I can see is simpler code (which is a good thing, but shouldn't be the deciding factor in the face of other arguments).

Via #2919884: Only set search_api.index.*.has_reindexed state if necessary, we also found out that, when both the "Content access" processor and the "Index items immediately" option are enabled, saving a node will index it twice in that page request. Just indexing at the end of the page request would resolve that, too. (And would also index it in one batch along with all its comments, if a comment datasource is also present.)

So, does anyone know why we changed this behavior compared to Drupal 7?
Otherwise, I'd (obviously) propose adding it back.

(If this doesn't get implemented, though, we should in any case fix the double-indexing problem, perhaps in a new issue.)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drunken monkey created an issue. See original summary.

drunken monkey’s picture

Issue summary: View changes
drunken monkey’s picture

drunken monkey’s picture

Priority: Normal » Major
drunken monkey’s picture

Status: Active » Needs review
FileSize
3.32 KB

This should do it, and seems to only need a single change to make tests run.
Is anything else needed? Is there a good way to test this?

Status: Needs review » Needs work

The last submitted patch, 5: 2922525-5--index_delayed_at_shutdown.patch, failed testing. View results

drunken monkey’s picture

OK, maybe two changes …

borisson_’s picture

I like this, it's a great idea, both for perceived performance and for other tricky problems. However, I think this implementation is a little bit different than how I'd like to see it.

I would love to see this get moved to an evensubcriber that subscribes to a PostResponseEvent. If I understand this correctly - it's a little bit earlier in the process, but that doesn't really matter. Because an event subscriber is a lot easier to test, we could write a unit and/or kernel test for this behavior - making it more robust.

Not sure if that is enough to set it back to NW though.

drunken monkey’s picture

Thanks a lot for your feedback.
First off, it's great to hear you're behind this general idea, too!

Secondly, thanks a lot for the hint about the post-request event! That's indeed a very good idea, and totally worth re-doing this. (It wasn't much work to begin with.) I fear I'm sometimes still too used to the Drupal 7 way of things.
Please see the attached revision and tell me what you think.

(Interdiff included for completeness' sake, but pretty much useless. (See size.))

drunken monkey’s picture

Actually, we can very easily also test the correct behavior here (i.e., no immediate indexing occurs before the end of the page request).

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

Great work! Let's get this in.

  • drunken monkey committed ce68da1 on 8.x-1.x
    Issue #2922525 by drunken monkey, borisson_: Changed "Index items...
drunken monkey’s picture

Status: Reviewed & tested by the community » Fixed

Alright, then, thanks a lot for the feedback!
Committed.

Berdir’s picture

FYI, this broke paragraph tests, because we install a demo module with search integration and default content in the test process. Then we access the site and check the search in the test. But the test "page request" is still ongoing and it hasn't been indexed yet.

Not complaining, just leaving this here in case someone else did something similar.

Berdir’s picture

+++ b/src/Utility/PostRequestIndexing.php
@@ -0,0 +1,108 @@
+   */
+  public static function getSubscribedEvents() {
+    $events[KernelEvents::TERMINATE][] = ['onKernelTerminate'];
+
+    return $events;

There is actually a better way to do this.

This event is *always* called on every request (except anonymous page cache hits), so we have to instantiate it just to find out that there is nothing to do.

There is a feature specifically to allow to call a method on services *if* they have been instantiated.

See \Drupal\Core\EventSubscriber\KernelDestructionSubscriber. You add the needs_destruction tag to the service definition and implement \Drupal\Core\DestructableInterface

borisson_’s picture

Status: Fixed » Active

Setting this back to active based on @Berdir's feedback, we should implement the DestructableInterface.

dpagini’s picture

I don't know if it helps, but I stumbled across this issue for similar reasons. Tests in my project started failing when items were not in the index. I have "index items immediately" set. It sounds like the items are not indexed because a page request is not creating them, the tests are, is that accurate?

trwill’s picture

Isn't the problem with this approach though, that now the index process becomes request driven, rather than the hook for node_access_records_alter? Is it possible that nodes could be created without a request?

drunken monkey’s picture

FYI, this broke paragraph tests, because we install a demo module with search integration and default content in the test process. Then we access the site and check the search in the test. But the test "page request" is still ongoing and it hasn't been indexed yet.

Not complaining, just leaving this here in case someone else did something similar.

Yeah, that was more or less unavoidable, of course. But we provide PostRequestIndexingTrait for that, using that you should easily be able to fix all tests.

There is actually a better way to do this.

Thanks a lot for that, seems like a great improvement!
See the attached patch.

Isn't the problem with this approach though, that now the index process becomes request driven, rather than the hook for node_access_records_alter? Is it possible that nodes could be created without a request?

Hardly – and even if it is possible and someone does it, not immediately triggering indexing isn't such a big problem then either, I'd say.

dpagini’s picture

I applied this patch and my tests are still failing. I will investigate further this week, but just wanted to give some early feedback. I can't totally tell from the comments if this is still expected...? Are you saying my tests that are creating content need to use this new trait in order to run correctly?

I just sort of think that if I choose "Index items immediately" option, it's a little misleading. It should read "Index items immediately (that are created via admin screens)."

Berdir’s picture

Yeah, that was more or less unavoidable, of course. But we provide PostRequestIndexingTrait for that, using that you should easily be able to fix all tests.

No, the trait didn't work for me, I had to run it in the install hook of my demo module. That's because I created my content in a hook_install() and the test also installed another module after that, which builds a new container and then the new service didn't have the content-to-be-indexed anymore.

A safer version would be to just index *all* content in that test.

@dpagini: It is not supposed to fix your case, and yes, you will have to adjust your test. Tests are in general not covered by the backwards-compatibility policy, also not by Drupal core. It's fairly common that tests need to be updated in contrib/custom modules after updates of other modules.

dpagini’s picture

I don't necessarily mind changing the test if I have to, but this does really change the behavior that was in place prior to this original commit. I just want to make sure the tests don't hint at a larger issue... Before this commit went in, creating a node programmatically (via a cron job maybe as an example?), would have been indexed. After this update, those programmatic nodes would no longer be indexed, right? I haven't dug in yet, but at what point DO those types of nodes get indexed? Do they ever? Or is that what the trait is intended for? Should that be indicated somewhere for other users maybe?

claudiu.cristea’s picture

I have Behat tests failing after this went in, for the same reason it was described in #14. What I did is I've implemented an @AfterStep hook. Unfortunately I couldn't use PostRequestIndexingTrait because in my FeatureContext class I cannot use the trait. I'm receiving

PHP Fatal error: Trait 'Drupal\Tests\search_api\Kernel\PostRequestIndexingTrait' not found

This is, probably, because of the special autoloading that unit tests have.

Here's my context hook:

  /**
   * @AfterStep
   */
  public function indexEntities() {
    \Drupal::service('search_api.post_request_indexing')->destruct();
  }
zero2one’s picture

I have the same Behat problem: all tests about Search API driven overview pages fail.

I got the following error while using the fix by claudiu.cristea
Fatal error: Call to undefined method Drupal\search_api\Utility\PostRequestIndexing::destruct() (Behat\Testwork\Call\Exception\FatalThrowableError)

This works for me:

public function indexEntities() {
  \Drupal::service('search_api.post_request_indexing')->onKernelTerminate();
}
claudiu.cristea’s picture

@zero2one, you should use the patch from #19.

Berdir’s picture

> I just want to make sure the tests don't hint at a larger issue...

I think it is fine.

The thing with tests is that they are a continuous process that remains active for the whole test run. So the destruct/indexing never happens ar at least not early enough for the test steps. Indexing on cron for example should still work because running cron (even in drush) should terminate the kernel which would then in turn cause those methods to be called.

And the worst case is just that the content will be picked up the next time cron runs and it will be indexed then as if the immediate-index checkbox isn't enabled. I think :)

drunken monkey’s picture

I think it is fine.

Yes, exactly. This is just a test problem, the real-life functionality is still exactly the same. (Just better, hopefully.)

Since this seems to affect a lot more people than I thought it would, I now also posted a change record for this. Should have done that right away, it seems – sorry about that!
(Also a bit unlucky that I already created a release with this right away – hope us changing the service now won't trip up too much people again.)

drunken monkey’s picture

Anyways, does someone want to review the follow-up patch?

borisson_’s picture

Status: Needs review » Reviewed & tested by the community

Looks solid, let's get this in!

  • drunken monkey committed 4a8ed4f on 8.x-1.x
    Follow-up to #2922525 by drunken monkey, Berdir, borisson_: Further...
drunken monkey’s picture

Status: Reviewed & tested by the community » Fixed

Thanks! Committed.
Thanks again, everyone – especially Berdir, of course!

Status: Fixed » Closed (fixed)

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

mpp’s picture