hasura / hasura/graphql-engine

[Relay] non-optimized row_number adding performance problems to simple queries

Open
#8,170 0 comments 1 reaction 0 assignees View on GitHub
k/bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.