Skip to content

Conversation

@ryanolee
Copy link

@ryanolee ryanolee commented Jun 18, 2025

Question Answer
JIRA issue IBX-10174
Type bug
Target Ibexa version v3/V4
BC breaks no
Required by ezsystems/ezplatform-admin-ui#2124

What

This PR adds a new "$limit" option to the LocationService::getSubtreeSize query in order to reduce the effects of expensive count queries within the CMS.

In #360 the LocationService::getSubtreeSize was introduced to address prior issues with the prior LocationService::count being two slow. This PR aims to follow on from that by addressing further issues with LocationService::getSubtreeSize resulting in disproportionately slow queries in it's own right.

This has been done by adding a function to limit the overall selected subTree size by changing the public API from

interface LocationService {
    # ...

    public function getSubtreeSize(Location $location): int;

    # ...
}

To

interface LocationService {
    # ...

    public function getSubtreeSize(Location $location, ?int $limit  = null): int;

    # ...
}

Why

In some circumstances (Where a content object may have a large number of child items) the impact of a single call to the getSubtreeSize function can account for over 30 percent of the total page load time

See the following flame chart for loading the "view" page in the CMS for a content object with a large number of sub-items:
image

With this patch included this becomes
image

The time the function takes to resolve falls from 407MS -> 1MS for what is functionally the same query.

How

This PR introduces a $limit parameter that targets the use case presented by IsWithinCopySubtreeLimit (The only usecase presented by the current eZ Codebase). The query is aiming to ascertain if there are more than a number of given number of sub items in a content object tree.

The current implementation is prone to over scanning the table due to there being no limit properly set on the table in relation to the primary usecase for the function.

The query it currently generates is:

SELECT COUNT(node_id) FROM ezcontentobject_tree t WHERE t.path_string LIKE '/1/2/80/136/%'; 

Which performs an entire table scan against existing indexes:

-> Aggregate: count(t.node_id)  (cost=78629.40 rows=1) (actual time=550.231..550.232 rows=1 loops=1)    -> Filter: (t.path_string like '/1/2/80/136/%')  (cost=58454.40 rows=201750) (actual time=0.028..547.540 rows=85943 loops=1)        -> Covering index range scan on t using ezcontentobject_tree_path over ('/1/2/80/136/                                                                                                                                                                              ' <= path_string <= '/1/2/80/136/????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????')  (cost=58454.40 rows=201750) (actual time=0.024..538.213 rows=85943 loops=1) |

In the new approach the query is terminated early resulting in much faster query times

 SELECT COUNT(*) FROM (SELECT t.node_id FROM ezcontentobject_tree t WHERE t.path_string LIKE '/1/2/80/136/%' LIMIT 100) AS x;

which gives

-> Aggregate: count(0)  (cost=58463.76..58463.76 rows=1) (actual time=0.508..0.508 rows=1 loops=1)    -> Table scan on x  (cost=0.04..3.75 rows=100) (actual time=0.002..0.004 rows=100 loops=1)        -> Materialize  (cost=58450.05..58453.76 rows=100) (actual time=0.496..0.502 rows=100 loops=1)            -> Limit: 100 row(s)  (cost=58440.01 rows=100) (actual time=0.045..0.469 rows=100 loops=1)                -> Filter: (t.path_string like '/1/2/80/136/%')  (cost=58440.01 rows=201750) (actual time=0.044..0.463 rows=100 loops=1)                    -> Covering index range scan on t using ezcontentobject_tree_path over ('/1/2/80/136/                                                                                                                                                                  ' <= path_string <= '/1/2/80/136/????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????')  (cost=58440.01 rows=201750) (actual time=0.039..0.446 rows=100 loops=1) |

Special care has been taken so that no existing code paths should be effected by this change given the new query is only used if a limitation is supplied (where by default it is not)

Checklist:

  • Provided PR description.
  • Tested the solution manually.
  • Provided automated test coverage.
  • Checked that target branch is set correctly (master for features, the oldest supported for bugs).
  • Ran PHP CS Fixer for new PHP code (use $ composer fix-cs).
  • Asked for a review (ping @ezsystems/engineering-team).

@sonarqubecloud
Copy link

@ryanolee
Copy link
Author

Superseded by #413

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants