Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix entity selector gen + massive perf improvements #16249

Merged

Conversation

cconard96
Copy link
Contributor

@cconard96 cconard96 commented Dec 27, 2023

Q A
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #16341

Issue:
When an entity is created/updated/deleted, the 'entity_selector' cache entry is deleted, but a recent patch changed the entity selector to use a cache entry with a calculated key based on the user's current profile entities.

$ckey = 'entity_selector'
    . sha1(json_encode($_SESSION['glpiactiveprofile']['entities']))
    . sha1($base_path) // cached value contains links based on `$base_path`, so cache key should change when `$base_path` changes
;

So, the entity selector is not correct after an entity is added/changed if the cache entry was already set .

Solution:
Don't cache entity selector data in a key based on the active profile's entities. Instead, rework the getTreeForItem function to utilize the existing getSonsOf function which makes use of the sons cache in GLPI_CACHE and at the DB level if present. Then after we have every child's ID, we can get all of the names and parent IDs in a single DB request.

Result:
For me on my GLPI with 38 entities, the time required to generate the selector went from 7ms to 4ms with this PR removing the use of the combined selector cache entry and replacing it with the use of the sons_cache entries. Indeed this is still longer than it would have taken to get the full selector data from the cache, but that doesn't seem possible without using a single cache entry again. Not sure about the effect on performance with a massive number of entities, but I suppose performance could be improved more if the entity parents/names are cached in some way.

Also, caching data based on the $base_path doesn't seem to be needed as the base path isn't saved in the data. Instead it is added after the entity selector data is already fetched.

@tsmr
Copy link
Collaborator

tsmr commented Dec 28, 2023

I confirm. There is a problem in 10.0.11. After create a new entity, this entity is not find by search engine

@cconard96
Copy link
Contributor Author

I confirm. There is a problem in 10.0.11. After create a new entity, this entity is not find by search engine

If you have time, can you test this PR to validate the fix as well as check the impact on performance?

@tsmr
Copy link
Collaborator

tsmr commented Dec 28, 2023

I have applied your fix on a client instance, it's ok (but i haven't check performance)

@orthagh orthagh mentioned this pull request Jan 4, 2024
Copy link
Contributor

@orthagh orthagh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think prefer this solution over #16306.
If everyone agree to choose it, and to avoid any further issue, a new test adding/removing entities and compare somehow (I'm not sure how to do this in the current state) to the rendered tree. The entity selector is a crucial part and we should avoid regressions here in priority.

Copy link
Member

@cedric-anne cedric-anne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since #15792, the entity tree is loaded only on demand, so, as long as generating the HTML is not anymore done ion every page display, it seems acceptable to remove the cache from here.

I just wonder how long it would take to generate a tree with thousands of entities.

src/DbUtils.php Outdated Show resolved Hide resolved
src/Entity.php Outdated Show resolved Hide resolved
@orthagh
Copy link
Contributor

orthagh commented Jan 8, 2024

I just wonder how long it would take to generate a tree with thousands of entities.

I will do some tests with a 45k entities instance this afternoon

@cconard96
Copy link
Contributor Author

cconard96 commented Jan 8, 2024

After testing with 40,000-45,000 entities we saw the selector generation take 30-60s after the sons cache was warmed up. This test had the entities directly under the root entity rather than in more of a tree hierarchy. I added more profiling and found that nearly all of the time taken was from the constructTreeFromList recursive calls.

The first optimization I tried can still be seen in the commit and it simply separated the single for-loop into two so only the unused data would be passed to the subsequent calls. For me, that optimization reduced the time taken from approximately a minute down to just over 1 second. However, this optimization only works well if the bulk of the items are in the first few levels. After I moved the 40k new entities under a child entity, then moved 2 groups of 10k down another level, the AJAX call jumped to 38 seconds.

The second optimization I tried pre-grouped the items by the parent ID before the first call to constructTreeFromList. This reduced the total AJAX call time from 38 seconds to 5 seconds. This second optimization seems like the best solution regardless of the complexity of the entity tree.

Although I only see constructTreeFromList used by getTreeForItem, I kept support for non-grouped items in case some plugin uses it.

@cedric-anne
Copy link
Member

cedric-anne commented Jan 9, 2024

I did not find any occurence to getTreeForItem() or constructTreeFromList() in any plugin. Their only usage seems to be for the entity selector.

As the logic does not seems to have to be reusable, it could probably be optimized by combinating all of this into a single loop, instead of having:

  • a loop on an iterator to generate an array,
  • a loop on the array to generate a tree,
  • a loop on the tree to format the output,
  • a loop on the tree to add flags on expanded/selected element.

All of this could probably be done in Entity::getEntitySelectorTree(), and existing methods could be preserved and deprecated in GLPI 10.1, in case they are used by a plugin.

@cconard96
Copy link
Contributor Author

The full AJAX request time is now down to 650ms for me following my last commit. The entity selector generation itself is 450ms. This is still with the 40,000 entities.

Copy link
Member

@cedric-anne cedric-anne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes made in the DbUtils class can probably be reverted now.

Also, adding a test on Entity::getEntityTree() to validate its output would help to prevent regressions.

@cconard96 cconard96 changed the title Fix entity selector gen + improve getTreeForItem perf Fix entity selector gen + massive perf improvements Jan 9, 2024
@cconard96
Copy link
Contributor Author

cconard96 commented Jan 9, 2024

Changes made in the DbUtils class can probably be reverted now.

Done

Also, adding a test on Entity::getEntityTree() to validate its output would help to prevent regressions.

That function is private. There is a test on the implementation of it instead Entity::getEntitySelectorTree.

@cedric-anne cedric-anne force-pushed the fix/entity_selector_cache branch from 150264d to 4c11f06 Compare January 11, 2024 10:45
@cedric-anne cedric-anne linked an issue Jan 11, 2024 that may be closed by this pull request
2 tasks
@cedric-anne cedric-anne merged commit aa750fb into glpi-project:10.0/bugfixes Jan 11, 2024
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

New created entities does not appear in dropdown entities selector
4 participants