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

qe: partially refactor relation aggregation selection #4658

Merged
merged 31 commits into from
Jan 23, 2024

Conversation

aqrln
Copy link
Member

@aqrln aqrln commented Jan 19, 2024

Refactor relation aggregation selections by making them a kind of SelectedField. More generally, this introduces virtual fields (i.e. fields that don't exist in the model and represent a computation instead) which relation aggregations (i.e. _count) are one and so far the only kind of.

This eliminates code duplication and special cases for relation aggregations in many places:

  • No need to pass relation aggregation fields separately all the way down to connector, selected_fields are the source of truth
  • No need to manually concatenate names and type identifiers of field selections and aggregation selections in multiple places
  • No need for separate data structures to hold the results of aggregations
  • No need to extract relation aggregation results and remove them from the result set in the interpreter, we can now store the results exactly as returned by the database and process them in the serializer

One particular special case that is avoidable with the new design but still exists is that we still store the list of virtual selection fields separately in the RecordSelection struct. The new virtual_fields field plays the role of the old aggregation_rows one, except the old one stored extracted aggregation data together with definitions of the fields while the new one stores the pure definitions. It shouldn't be necessary, and both fields: Vec<String> and virtual_fields: Vec<VirtualSelection> can be replaced with a single fields: Vec<SelectedField>. This also implies splitting selected_fields in query graph nodes into full_selection and user_selection/serialized_selection and eliminating separate selection_order field. These changes were de-scoped from this PR to avoid changing the write operations that depend on RecordSelection and to timebox the refactoring, and will happen separately in a future PR.

Collection of both virtuals and non-virtuals in collect_selected_fields in the query graph builder, as well as processing the scalars together with virtuals in the serializer, both happen in a single pass already in anticipation of future changes.

WASM benchmarks are about 5% faster on my machine with these changes.

This is preparatory work necessary for making relation aggregation selections work with JOINs (https://github.com/prisma/team-orm/issues/700).

Closes: https://github.com/prisma/team-orm/issues/815

Copy link
Contributor

github-actions bot commented Jan 19, 2024

WASM Size

Engine This PR Base branch Diff
WASM 2.329MiB 2.334MiB -4.970KiB
WASM (gzip) 906.375KiB 907.092KiB -735.000B

Copy link

codspeed-hq bot commented Jan 19, 2024

CodSpeed Performance Report

Merging #4658 will not alter performance

Comparing refactor-relation-count-2 (ef0e83b) with main (e086e3a)

Summary

✅ 11 untouched benchmarks

Copy link
Contributor

github-actions bot commented Jan 19, 2024

🚀 WASM query-engine performance will improve by 5.33%

Full benchmark report
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/bench?schema=imdb_bench&sslmode=disable" \
node --experimental-wasm-modules query-engine/driver-adapters/executor/dist/bench.mjs
cpu: AMD EPYC 7763 64-Core Processor
runtime: node v18.19.0 (x64-linux)

benchmark                   time (avg)             (min … max)       p75       p99      p995
-------------------------------------------------------------- -----------------------------
• movies.findMany() (all - 25000)
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline  303.04 ms/iter (299.25 ms … 326.15 ms) 302.09 ms 326.15 ms 326.15 ms
Web Assembly: Latest    390.59 ms/iter (386.05 ms … 406.47 ms) 392.29 ms 406.47 ms 406.47 ms
Web Assembly: Current   363.93 ms/iter  (360.56 ms … 370.4 ms) 364.88 ms  370.4 ms  370.4 ms
Node API: Current       227.11 ms/iter (218.26 ms … 237.38 ms) 231.95 ms 237.38 ms 237.38 ms

summary for movies.findMany() (all - 25000)
  Web Assembly: Current
   1.6x slower than Node API: Current
   1.2x slower than Web Assembly: Baseline
   1.07x faster than Web Assembly: Latest

• movies.findMany({ take: 2000 })
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline   12.39 ms/iter   (11.83 ms … 17.36 ms)  12.32 ms  17.36 ms  17.36 ms
Web Assembly: Latest     15.83 ms/iter    (15.4 ms … 19.58 ms)   15.8 ms  19.58 ms  19.58 ms
Web Assembly: Current    14.98 ms/iter   (14.71 ms … 17.85 ms)  14.85 ms  17.85 ms  17.85 ms
Node API: Current         8.89 ms/iter    (8.59 ms … 11.93 ms)   8.95 ms  11.93 ms  11.93 ms

summary for movies.findMany({ take: 2000 })
  Web Assembly: Current
   1.69x slower than Node API: Current
   1.21x slower than Web Assembly: Baseline
   1.06x faster than Web Assembly: Latest

• movies.findMany({ where: {...}, take: 2000 })
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline    2.17 ms/iter     (1.86 ms … 3.75 ms)   2.15 ms   3.61 ms   3.62 ms
Web Assembly: Latest      2.61 ms/iter     (2.41 ms … 4.31 ms)   2.56 ms   4.28 ms   4.29 ms
Web Assembly: Current     2.46 ms/iter     (2.26 ms … 4.09 ms)   2.39 ms   4.01 ms   4.08 ms
Node API: Current         1.47 ms/iter      (1.38 ms … 2.2 ms)    1.5 ms   1.67 ms   1.82 ms

summary for movies.findMany({ where: {...}, take: 2000 })
  Web Assembly: Current
   1.67x slower than Node API: Current
   1.13x slower than Web Assembly: Baseline
   1.06x faster than Web Assembly: Latest

• movies.findMany({ include: { cast: true } take: 2000 }) (m2m)
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline   12.13 ms/iter    (11.9 ms … 12.71 ms)  12.21 ms  12.71 ms  12.71 ms
Web Assembly: Latest      15.6 ms/iter   (15.27 ms … 16.31 ms)  15.67 ms  16.31 ms  16.31 ms
Web Assembly: Current    14.83 ms/iter   (14.67 ms … 17.13 ms)   14.8 ms  17.13 ms  17.13 ms
Node API: Current         8.99 ms/iter    (8.47 ms … 13.42 ms)   8.87 ms  13.42 ms  13.42 ms

summary for movies.findMany({ include: { cast: true } take: 2000 }) (m2m)
  Web Assembly: Current
   1.65x slower than Node API: Current
   1.22x slower than Web Assembly: Baseline
   1.05x faster than Web Assembly: Latest

• movies.findMany({ where: {...}, include: { cast: true } take: 2000 }) (m2m)
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline    1.92 ms/iter     (1.82 ms … 3.21 ms)   1.92 ms   2.85 ms   3.15 ms
Web Assembly: Latest      2.49 ms/iter        (2.39 ms … 4 ms)    2.5 ms   3.08 ms   3.24 ms
Web Assembly: Current     2.34 ms/iter        (2.25 ms … 5 ms)   2.33 ms   2.81 ms   2.81 ms
Node API: Current          1.5 ms/iter      (1.4 ms … 1.77 ms)   1.52 ms   1.68 ms    1.7 ms

summary for movies.findMany({ where: {...}, include: { cast: true } take: 2000 }) (m2m)
  Web Assembly: Current
   1.57x slower than Node API: Current
   1.22x slower than Web Assembly: Baseline
   1.06x faster than Web Assembly: Latest

• movies.findMany({ take: 2000, include: { cast: { include: { person: true } } } })
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline   12.12 ms/iter   (11.84 ms … 12.87 ms)  12.18 ms  12.87 ms  12.87 ms
Web Assembly: Latest     15.62 ms/iter    (15.5 ms … 15.77 ms)  15.68 ms  15.77 ms  15.77 ms
Web Assembly: Current    14.91 ms/iter    (14.4 ms … 19.84 ms)   14.9 ms  19.84 ms  19.84 ms
Node API: Current         8.84 ms/iter    (8.57 ms … 11.73 ms)   8.85 ms  11.73 ms  11.73 ms

summary for movies.findMany({ take: 2000, include: { cast: { include: { person: true } } } })
  Web Assembly: Current
   1.69x slower than Node API: Current
   1.23x slower than Web Assembly: Baseline
   1.05x faster than Web Assembly: Latest

• movie.findMany({ where: { ... }, take: 2000, include: { cast: { include: { person: true } } } })
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline    1.89 ms/iter      (1.8 ms … 2.75 ms)   1.89 ms   2.68 ms   2.69 ms
Web Assembly: Latest      2.46 ms/iter     (2.37 ms … 3.26 ms)   2.46 ms   3.02 ms   3.24 ms
Web Assembly: Current     2.34 ms/iter     (2.26 ms … 3.14 ms)   2.33 ms   2.91 ms   3.02 ms
Node API: Current         1.49 ms/iter     (1.39 ms … 1.91 ms)   1.52 ms   1.72 ms   1.85 ms

summary for movie.findMany({ where: { ... }, take: 2000, include: { cast: { include: { person: true } } } })
  Web Assembly: Current
   1.57x slower than Node API: Current
   1.23x slower than Web Assembly: Baseline
   1.05x faster than Web Assembly: Latest

• movie.findMany({ where: { reviews: { author: { ... } }, take: 100 }) (to-many -> to-one)
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline  936.19 µs/iter   (855.63 µs … 2.38 ms) 923.68 µs   1.52 ms   1.55 ms
Web Assembly: Latest      1.22 ms/iter        (1.13 ms … 2 ms)   1.21 ms   1.91 ms   1.98 ms
Web Assembly: Current      1.2 ms/iter     (1.11 ms … 2.68 ms)   1.19 ms   1.89 ms   1.93 ms
Node API: Current       846.31 µs/iter    (736.3 µs … 1.48 ms) 856.27 µs   1.19 ms   1.24 ms

summary for movie.findMany({ where: { reviews: { author: { ... } }, take: 100 }) (to-many -> to-one)
  Web Assembly: Current
   1.42x slower than Node API: Current
   1.29x slower than Web Assembly: Baseline
   1.02x faster than Web Assembly: Latest

• movie.findMany({ where: { cast: { person: { ... } }, take: 100 }) (m2m -> to-one)
-------------------------------------------------------------- -----------------------------
Web Assembly: Baseline  922.21 µs/iter   (854.41 µs … 4.35 ms) 926.36 µs   1.27 ms    1.4 ms
Web Assembly: Latest      1.22 ms/iter     (1.14 ms … 2.11 ms)   1.21 ms   1.81 ms   2.06 ms
Web Assembly: Current     1.15 ms/iter      (1.1 ms … 1.76 ms)   1.15 ms   1.49 ms   1.71 ms
Node API: Current       800.13 µs/iter   (740.96 µs … 1.07 ms) 831.23 µs 928.77 µs 950.71 µs

summary for movie.findMany({ where: { cast: { person: { ... } }, take: 100 }) (m2m -> to-one)
  Web Assembly: Current
   1.44x slower than Node API: Current
   1.25x slower than Web Assembly: Baseline
   1.06x faster than Web Assembly: Latest

After changes in ef0e83b

@aqrln aqrln self-assigned this Jan 19, 2024
@aqrln aqrln added this to the 5.9.0 milestone Jan 19, 2024
@aqrln aqrln force-pushed the refactor-relation-count-2 branch from 58b6759 to d837906 Compare January 19, 2024 18:21
@aqrln aqrln marked this pull request as ready for review January 19, 2024 22:43
@aqrln aqrln requested a review from a team as a code owner January 19, 2024 22:43
@aqrln aqrln requested review from laplab, Druue and Weakky and removed request for a team January 19, 2024 22:43
@aqrln aqrln marked this pull request as draft January 20, 2024 00:31
@aqrln aqrln marked this pull request as ready for review January 20, 2024 01:04
@aqrln aqrln force-pushed the refactor-relation-count-2 branch 2 times, most recently from c5c9ca4 to 26c6166 Compare January 22, 2024 14:08
Copy link
Contributor

@jkomyno jkomyno left a comment

Choose a reason for hiding this comment

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

(Reviewer's note)

Most diff in this PR is about getting rid of aggr_selections (whether as a variable or as a function parameter), or replacing it with virtual_selections.

The PR, despite its size, seems cohesive and very useful. I'll leave the final comment to Flavian though, as I don't fully grasp the context of these changes.

@aqrln aqrln force-pushed the refactor-relation-count-2 branch from 74b3e09 to d96e721 Compare January 23, 2024 11:50
Copy link
Contributor

@Weakky Weakky left a comment

Choose a reason for hiding this comment

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

Long due tech debt work (that the team should've done, not that you're late), so much cleaner, nice work 👍

@aqrln aqrln merged commit c1c3774 into main Jan 23, 2024
114 checks passed
@aqrln aqrln deleted the refactor-relation-count-2 branch January 23, 2024 18:23
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.

3 participants