Problem/Motivation

SearchApiTagCache::getRowId is a very expensive function, that can slow down views a lot.

It's doesn't have its own implementation, it uses the parent method CachePluginBase::getRowId.

In CachePluginBase::getRowId all entity objects in 'index', '_entity', '_relationship_entities' are removed, because serializing objects is expensive.

For search_api the objects are not stored in those keys. Search api has its own keys. '_item', '_object', '_relationship_objects' and 'entity:node/_object'.

That means serializing a search api row result includes all the entity objects.

Proposed resolution

Remove the _item', '_object', '_relationship_objects', 'entity:node/_object' keys before serializing the row.

Comments

chr.fritsch created an issue. See original summary.

chr.fritsch’s picture

Status: Active » Needs review
StatusFileSize
new1.86 KB

Here is a patch.

It improved the views loading time from 1.2 sec to 0.7 sec for me.

legolasbo’s picture

Status: Needs review » Reviewed & tested by the community

Your suggested change makes sense and looks good to me. I only have one nitpick, but that can be fixed on commit.

+++ b/src/Plugin/views/cache/SearchApiTagCache.php
@@ -81,4 +82,32 @@ class SearchApiTagCache extends Tag {
+    $row_data = array_diff_key((array) $row, array_flip(['_item', '_object', '_relationship_objects', $row->search_api_datasource . '/_object'])) + $this->getRowCacheTags($row);

This line is getting pretty long. Maybe extract the array_flip(...) bit into a variable to improve readability?

drunken monkey’s picture

Status: Reviewed & tested by the community » Needs work

Thanks a lot for the patch, good suggestion!
However, it seems excluding $row->search_api_datasource . '/_object' specifically isn’t enough, as other such properties can contain entities as well (e.g., in my case, $row->{'entity:node/type:_object'}). So probably we need a more advanced solution here, going throw the whole (array) $row array and unsetting all search items, entities and (probably also?) entity wrappers. Or maybe we can replace them with a unique string, like entity::node::123, just to be on the safe side? If we’re already adding a helper method to go through all properties (recursively, I guess), this shouldn‘t be much extra work. (I’m not at all certain our cache tags are adequate, that’s the problem here, so better be on the safe side regarding the properties, I’d say.)

chr.fritsch’s picture

Status: Needs work » Needs review
StatusFileSize
new1.21 KB

I simplified the code to just return the search_api_id as cache row id.

I also found out, that the row cache tags are not set properly.

Status: Needs review » Needs work

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

chr.fritsch’s picture

Status: Needs work » Needs review
StatusFileSize
new2.16 KB
new1.73 KB

A new patch to fix the failing tests

drunken monkey’s picture

Component: General code » Views integration

Hm, OK, I guess that could work. Not sure, though, don’t understand caching (especially for Views) well enough. Would be great to have others confirm this makes sense and works for them (or looks to them like it should work).

In any case, though, $row->_relationship_objects already contains $row->_object, so we can simplify a bit. (Otherwise, we’d have to verify that this is an entity, too.)

In any case, this change looks like it would break things for indexes that contain non-entities – but since that’s a rather minor use case, and the problems should not be large (just potentially showing stale data until item is re-indexed), I think we can live with that compared to the large performance gain for the main use case.

drunken monkey’s picture

StatusFileSize
new1.75 KB
new2.79 KB

Also, is there a simple way to get this covered by tests, do you think?

chr.fritsch’s picture

I think getRowCacheTags is kind of covered by ViewsDisplayCachingTest. For getRowId we could look at RowRenderCacheTest::testNoCaching and try to build a similar test.

drunken monkey’s picture

Status: Needs review » Fixed

Alright, that’s probably not worth it.
So, let’s just commit this.
Thanks again, everyone!

Status: Fixed » Closed (fixed)

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