hasura / hasura/graphql-engine
[Relay] non-optimized row_number adding performance problems to simple queries
- Dominant language
- TypeScript
- Stars
- 32.1k
- Forks
- 3k
- PR merge metrics
- PR metrics pending
Description
### Version Information
Server Version: v2.0.10 and tested on 2.2.0
### Environment
docker running on ubuntu
### What is the expected behaviour?
Queries for `1-1` relations should be performant, especially on simple queries.
### Keywords
performance, queries, optimization, analyze.
### What is the current behaviour?
On the example of :
```
query MyQuery {
leads_connection(first: 40) {
edges {
node {
property {
id
}
}
}
}
}
```
Using the relay endpoint, it takes a lot of time even though it is as simple as it could be, and apparently, the main cause is in the piece of generated SQL:
```
SELECT
*,
(row_number() over (partition by 1)) AS "__row_number"
```
As the whole sql generated is:
```
WITH "__base_select" AS (
SELECT
*,
(row_number() over (partition by 1)) AS "__row_number"
FROM
(
SELECT
"_0_root.base"."id" AS "root.pg.id",
(json_build_object('id', "_0_root.base"."id")) :: text AS "__cursor",
row_to_json(
(
SELECT
"_4_e"
FROM
(
SELECT
"_3_root.or.property"."property" AS "property"
) AS "_4_e"
)
) AS "root.edges.node"
FROM
(
SELECT
*
FROM
"public"."leads"
WHERE
(
(
("public"."leads"."tenant_id") = (('8ce0ed5e-1748-11ea-ab1d-63204f713040') :: uuid)
)
OR (
(("public"."leads"."tenant_id") IS NULL)
AND (
(('8ce0ed5e-1748-11ea-ab1d-63204f713040') :: uuid) IS NULL
)
)
)
) AS "_0_root.base"
LEFT OUTER JOIN LATERAL (
SELECT
row_to_json(
(
SELECT
"_2_e"
FROM
(
SELECT
regexp_replace(
encode(
convert_to(
(
json_build_array(
1,
'public',
'properties',
"_1_root.or.property.base"."id"
)
) :: text,
'UTF8'
),
'base64'
),
E '\\n',
'',
'g'
) AS "id"
) AS "_2_e"
)
) AS "property"
FROM
(
SELECT
*
FROM
"public"."properties"
WHERE
(
(("_0_root.base"."property_id") = ("id"))
AND (
(
("public"."properties"."tenant_id") = (('8ce0ed5e-1748-11ea-ab1d-63204f713040') :: uuid)
)
OR (
(("public"."properties"."tenant_id") IS NULL)
AND (
(('8ce0ed5e-1748-11ea-ab1d-63204f713040') :: uuid) IS NULL
)
)
)
)
) AS "_1_root.or.property.base"
) AS "_3_root.or.property" ON ('true')
) AS "_5_root"
),
"__slice_select" AS (
SELECT
*
FROM
"__base_select"
LIMIT
40
), "__final_select" AS (
SELECT
*
FROM
"__slice_select"
),
"__cursors_select" AS (
SELECT
(array_agg("__cursor")) [1] AS "__start_cursor",
(array_agg("__cursor")) [array_length(array_agg("__cursor" ), 1 )] AS "__end_cursor",
(array_agg("__row_number")) [1] AS "__start_row_number",
(array_agg("__row_number")) [array_length(array_agg("__row_number" ), 1 )] AS "__end_row_number"
FROM
"__final_select"
),
"__page_info" AS (
SELECT
EXISTS (
SELECT
1
FROM
"__base_select"
WHERE
(
("__row_number") < (
(
SELECT
"__start_row_number"
FROM
"__cursors_select"
)
)
)
) AS "__has_previous_page",
EXISTS (
SELECT
1
FROM
"__base_select"
WHERE
(
("__row_number") > (
(
SELECT
"__end_row_number"
FROM
"__cursors_select"
)
)
)
) AS "__has_next_page"
)
SELECT
json_build_object(
'edges',
coalesce(
json_agg(json_build_object('node', "root.edges.node")),
'[]'
)
) AS "root"
FROM
"__final_select"
```
### How to reproduce the issue?
1. Query anything that has 1-1 relationship using relay endpoint
2. See hasura struggling to retrieve 40 or less rows
3. Analyze using the analyze button on console.
Contributor guide
Research direction
Start at the Relay endpoint's SQL generation path and reproduce the shown `leads_connection(first: 40)` query against PostgreSQL. Use the generated SQL and the console's analyze output to trace the cost of the `row_number()` expression. Done means the simple 1-1 Relay query retrieves 40 or fewer rows without the reported performance regression.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- graphql, postgresql
- Domain
- api, database, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 32/100