-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
improve performance of relations visible scope #17755
base: release/15.3
Are you sure you want to change the base?
improve performance of relations visible scope #17755
Conversation
7dcb115
to
3adac9b
Compare
3adac9b
to
47565f2
Compare
Generated by 🚫 Danger |
The ping was sent out before I could change the branch and is a false positive. Sorry for this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think association extensions can be used to resolve the problem of requiring to use visible_relations
instead of relations.visible
. proxy_association.owner
and proxy_association.target
should give access to the work_package or scoped relation query
END id | ||
SQL | ||
) | ||
.where("relations.from_id = :id OR relations.to_id = :id", id: id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Relation.
… .where("relations.from_id = :id OR relations.to_id = :id", id: id)
can be replaced with just relations
relation_from_or_to = Relation | ||
.select( | ||
<<~SQL.squish | ||
CASE | ||
WHEN from_id = #{id} | ||
THEN to_id | ||
ELSE from_id | ||
END id | ||
SQL | ||
) | ||
.where("relations.from_id = :id OR relations.to_id = :id", id: id) | ||
.arel | ||
|
||
self_id = Arel.sql("SELECT #{id} AS id") | ||
|
||
wp_focus_scope = Arel::Nodes::UnionAll.new(relation_from_or_to, self_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about:
wp_focus_scope = Arel::Nodes::Union.new(relations.select("from_id as id").arel, relations.select("to_id as id").arel)
wp_arel = work_package_focus_scope.respond_to?(:arel) ? work_package_focus_scope.arel : work_package_focus_scope | ||
visible_work_packages = WorkPackage.visible(user) | ||
|
||
visible_work_packages = visible_work_packages.where(WorkPackage.arel_table[:id].in(wp_arel)) if wp_arel |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wp_arel = work_package_focus_scope.respond_to?(:arel) ? work_package_focus_scope.arel : work_package_focus_scope | |
visible_work_packages = WorkPackage.visible(user) | |
visible_work_packages = visible_work_packages.where(WorkPackage.arel_table[:id].in(wp_arel)) if wp_arel | |
visible_work_packages = WorkPackage.visible(user) | |
if work_package_focus_scope | |
wp_arel = work_package_focus_scope.respond_to?(:arel) ? work_package_focus_scope.arel : work_package_focus_scope | |
visible_work_packages = visible_work_packages.where(WorkPackage.arel_table[:id].in(wp_arel)) | |
end |
Ticket
https://community.openproject.org/wp/61058
What are you trying to accomplish?
When calling
some_work_package.relations.visible
or its aliassome_work_package.visible_relations
, the following SQL is executed for a non admin user.This sometimes performs poorly in cases where there are a lot of work packages. The exact trigger for when Postgres starts to perform poorly is unknown. But
EXPLAIN
shows why the performance is poor:The planner is way off estimating the number of work packages that would be returned as visible ones and chooses a full table scan. And it does so twice within the same query as the visible scope is applied two times.
Some API responses like
GET api/v3/work_packages/:id
andPATCH api/v3/work_packages/:id
the visible relations are queried twice when rendering the response as the relations are embedded in the work package resource. This can lead to poor performance on those requests.Without checking for confirmation, the code indicates that the reworked relations tab fetches the visible scope multiple times as well.
What approach did you choose and why?
The problem arrises when
some_work_package.relations.visible
is concatenated. While it leads to the desired results, it does not make use of the fact that the number of relations directly connected to a work package will likely be small. For most work packages the number will be below 10 and even more connected work packages will not have thousands of relations. Using this, the already existingvisible_relations
can be employed to combine the visible scope with a subselect so that it focuses on work packages that are related to the work package the relations are searched for. Additionally, instead of providing the visible scope as subselects twice, the visible scope is provided once as a CTE which thefrom_id
/to_id
condition both reference.This results in the following SQL:
The
EXPLAIN
outputs show that the planned rows are closer to the actual ones and that indices are used now:It is unfortunate, that this requires to always use
visible_relations
when doingrelations.visible
is the code most developers would be using naturally. I did not find a better way here.The performance is also stable when querying like it is done in the relations tab by now e.g.: