Problem/Motivation
This is an error that seems - at first sight - to be related to views but the error got introduced after applying a patch from #2869121 (also tested and reproduced this issue with latest stable version of search_api). This may also be related to a views configuration but we have an exposed "Search: Fulltext search" filter that is shown on all pages and no results are returned per default i.e. the query gets aborted. When I'm logged in as an Administrator and fire a request to the home page the code throws the following fatal error: Fatal error: Call to a member function usePager() on null in docroot/core/modules/views/src/ViewExecutable.php on line 1407. It seems that that it tries to build up a view with no results. Clearing the cache and refreshing the page solves this but it happens again afterwards.
This is not a suggestion nor a proposed resolution, but I just wanted to post a (probably very bad) workaround that resolved this issue where I only want to point out that the issue (to my opinion) is related to search_api and not to views.
In modules/contrib/search_api/src/Plugin/views/filter/SearchApiFulltext.php on line 273, I add a string that will never be found allowing to build a query object.
// Catch empty strings entered by the user, but not "0".
if ($this->value === '') {
$this->value = 'thisisastringnoonewilleverfinddrupal8';
}
Then in the same file on line 238 I've commented out the following line (the complete if statement could be removed of course if no code is executed within in)
if (!empty($this->options['expose']['required']) && $this->getQuery()) {
//$this->getQuery()->abort();
}
Proposed resolution
- Add tests
Remaining tasks
Test.
User interface changes
None.
API changes
None.
Data model changes
None.
Comments
Comment #1
Anonymous (not verified) commentedwouter.adem created an issue. See original summary.
Comment #2
Anonymous (not verified) commentedComment #3
Anonymous (not verified) commentedComment #4
drunken monkeyThanks a lot for reporting this! It seems indeed that aborted queries cause massive problems with Views caching. (See also #2871030: Cached Search API results in Views are NULL, with a similar problem.)
Posting such workarounds is also always a good idea, since (even if they aren't proper solutions) they clearly show that we can indeed influence the problem at hand. (Otherwise, with just that error message, it would seem more likely that it's just a Views bug.)
I now see that, in the query plugins
build()method, we currently don't initialize the pager properly if the query was aborted. Which seems to make sense from our perspective, but since Views doesn't understand our concept of "aborted queries", it can't handle this in some cases (i.e., for cached results). Moving the pager initialization to the top of that method, to run even for aborted queries, should fix this problem.I've also included a test (modification) that should verify this problem and the solution. The test might also require #2871030-2: Cached Search API results in Views are NULL to pass, though, so we might need to fuse the two issues and patches.
Comment #5
borisson_The test only patch doesn't fail, was it supposed to?
I did find one nit to pick.
Can we add in documentation why this is at the top? So adding
This is near the top of the method because views expects to be able to call the pager, even if we don't return any results.Comment #6
drunken monkeyAs you've probably suspected: yes, it was. Damn.
I had feared the test wouldn't pass with just this fix, but instead we get one that even passes without it … Hm, guess I should try and get functional tests running again locally.
Or, could maybe someone else try to debug? Or at least verify that reproducing the bug works this way, just by …
Suggested comment is added in the attached patch. I felt this was taken care of already by the comment immediately afterwards, but I guess this really makes it clearer, which can't hurt.
Comment #7
drunken monkeyNote (mostly to self): Once we get a working test, we should also verify it correctly tests the fix in #2871030: Cached Search API results in Views are NULL.
Comment #8
drunken monkeyOK, I think I've done it! Was quite some work, but eventually it turned out that, for some reason, both page cache modules were enabled and, of course, massively interfered. I now just clear those caches before each of the page requests to really test the Views cache plugin – and now it works, and even correctly catches #2871030: Cached Search API results in Views are NULL.
Patch attached, please test/review!
(Also, I now can run functional tests locally again – a mixed blessing, as I now don't have any excuse not to debug those. Blergh.)
Comment #11
borisson_Looks good, one nit to pick though.
I don't see why you'd need to do the if here, we control those modules ourselves.
Comment #12
drunken monkeyThat's because I'm not actually sure where the module is enabled/installed. It's not in our code, as far as I can see. And if I don't know where it comes from, my thinking was that I can't guarantee it will stay there. (My bet is that this was enabled at some point in Core, by default, for all browser tests, so the page cache functionality would be automatically tested. But I couldn't even verify that.)
On the other hand, I guess it's very likely that it will stay there, now that Core is stable, and if it's removed, we can still add the
if, or remove the line. So, if you think we should remove it, we can also go with that – your choice.Comment #13
wim leersThis triggered some intensive facepalming.
This tests is pretty pointless, because it's explicitly emptying the render cache and Dynamic Page Cache (although NOT the Page Cache)… which basically means that you don't know whether this module behaves correctly with render cache and Dynamic Page Cache enabled (which is the case for pretty much every D8 site).
Rather than doing this, you should be A) asserting the
X-Drupal-CacheandX-Drupal-Dynamic-Cacheresponse headers, B) fixing Search API's code so that it also works correctly when those modules are installed.Indeed.
Ehm…
class ViewsTest extends SearchApiBrowserTestBase,abstract class SearchApiBrowserTestBase extends BrowserTestBase; the latter has:And this is
testing.info.yml:So there's your reason, there's no magic involved here at all.
Comment #14
borisson_Awesome, thanks for the information Wim! Really helpful.
@drunken monkey: because we explicitly want to test the views cache plugin I think we should probably keep the cache clears but we should remove the if around the dynamic page cache clear.
We should probably also check for the
X-Drupal-CacheandX-Drupal-Dynamic-Cacheheaders after doing the loop and checking that those are outputted as expected.Comment #15
drunken monkeyThe point was that it's working with the two page cache modules, which is pointless for a regression test. For that, we had to make the code fail without the fix, so needed to disable those modules. Makes sense, right? As you see, we already have the modules enabled for all other tests.
I never said it was magic, just that I couldn't be bothered to go through all the code to find out where it's enabled. And if you read my paragraph above, my guess was actually spot-on. So the question, as I said before, is just whether we want to rely on Drupal's testing framework keeping those modules enabled by default. If you're both in favor, sure, let's do that. I guess it really is unlikely they'll go back on that.
Please try to be a bit less condescending here. I did my best in the other issue, too. (At least after your reprimands.)
But yeah, thanks for the information!
OK, sure. Though it doesn't seem like that's the thing we want to assert there, it does make sure we're actually testing the correct thing.
So, like this, I guess?
Comment #17
borisson_Yeah, this looks good.
Comment #19
drunken monkeyGood to hear, thanks for reviewing!
Committed.
Thanks again, everyone!