Repository navigation
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #2455.
Problem
Backpex.Adapters.Ectojoined every group of relationship aggregates as an uncorrelated subquery (LEFT JOIN (SELECT root.pk, agg(...) FROM root JOIN related GROUP BY root.pk),DISTINCT ONfor:first). It aggregated the related items of all items on every read, also when the aggregate was only selected for the 15 items of a page or for the single item of the show view.Change
For a query with a limit that selects aggregates, the adapter now finds the primary keys of the page first and restricts every aggregate subquery to them:
backpex_pageapplies the filter, the search, the sorts, the offset and the limit. It joins only the aggregates these read, as before over all items (sorting or filtering by an aggregate needs the values of all items).:valueshare it), named from the fixed pool of atoms, now withWHERE pk IN (SELECT key FROM backpex_page).ecto_query/1), the aggregates of metrics (aggregate/1), preload queries ofBackpex.Query.related/1, aggregates inexistsor in another aggregate, and base queries that may return an item more than once or are limited themselves (an:options_querywith a join, select, distinct, group or limit).Where.join_aggregates/3takesof: keys.I chose the common table expression over
LEFT JOIN LATERALand correlated subqueries in the select list: it does not depend on the planner postponing the subqueries behind sort, limit and offset, needs no named parent binding, and keeps one subquery per group.Measurements
PostgreSQL 18.7, test app schema, 100,000 users, 2,000,000 posts (skewed, about 20 per user), page of 15,
EXPLAIN (ANALYZE, BUFFERS)execution time of the second run. "FK index" is an index onposts.user_id.username,aggregate(:count, :posts)selectedusernameprefixaggregate(:first, :posts, value: title, sort: [likes: :desc])selectedUserStatsLive(four groups)Shared buffers of the first row: 291,197 before, 184 after (FK index).
Not improved, and worth knowing:
Tests
test/adapters/ecto_test.exs: the shape of the query with a limit (keys without aggregates, restricted subqueries, sort without limit in the outer query), with a sort and a filter by aggregates, and the cases that keep the grouped join (no limit, aggregate only filtered by, options query with a join, metric).integration_test/options/computed_test.exs: pages read the same aggregates as the query without a limit, for three sorts and three filters.MIX_ENV=test mix cipasses.Surfaces
Views: index and show/edit (
Resource.get/4haslimit: 1) go through the new query; metrics and options keep the old one. Adapter boundary: the change is insideBackpex.Adapters.Ecto. User docs: cost notes inguides/fields/computed-fields.mdandguides/upgrading/v0.25.md. No texts, markup or fixtures changed.