You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
server: remove remnants of query plan caching (fix#1795)
Query plan caching was introduced by - I believe - #1934 in order to reduce the query response latency. During the development of PDV in #4111, it was found out that the new architecture (for which query plan caching wasn't implemented) performed comparably to the pre-PDV architecture with caching. Hence, it was decided to leave query plan caching until some day in the future when it was deemed necessary.
Well, we're in the future now, and there still isn't a convincing argument for query plan caching. So the time has come to remove some references to query plan caching from the codebase. For the most part, any code being removed would probably not be very well suited to the post-PDV architecture of query execution, so arguably not much is lost.
Apart from simplifying the code, this PR will contribute towards making the GraphQL schema generation more modular, testable, and easier to profile. I'd like to eventually work towards a situation in which it's easy to generate a GraphQL schema parser *in isolation*, without being connected to a database, and then parse a GraphQL query *in isolation*, without even listening any HTTP port. It is important that both of these operations can be examined in detail, and in isolation, since they are two major performance bottlenecks, as well as phases where many important upcoming features hook into.
Implementation
The following have been removed:
- The entirety of `server/src-lib/Hasura/GraphQL/Execute/Plan.hs`
- The core phases of query parsing and execution no longer have any references to query plan caching. Note that this is not to be confused with query *response* caching, which is not affected by this PR. This includes removal of the types:
- - `Opaque`, which is replaced by a tuple. Note that the old implementation was broken and did not adequately hide the constructors.
- - `QueryReusability` (and the `markNotReusable` method). Notably, the implementation of the `ParseT` monad now consists of two, rather than three, monad transformers.
- Cache-related tests (in `server/src-test/Hasura/CacheBoundedSpec.hs`) have been removed .
- References to query plan caching in the documentation.
- The `planCacheOptions` in the `TenantConfig` type class was removed. However, during parsing, unrecognized fields in the YAML config get ignored, so this does not cause a breaking change. (Confirmed manually, as well as in consultation with @sordina.)
- The metrics no longer send cache hit/miss messages.
There are a few places in which one can still find references to query plan caching:
- We still accept the `--query-plan-cache-size` command-line option for backwards compatibility. The `HASURA_QUERY_PLAN_CACHE_SIZE` environment variable is not read.
hasura/graphql-engine-mono#1815
GitOrigin-RevId: 17d92b2
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1-1
Original file line number
Diff line number
Diff line change
@@ -6,12 +6,12 @@
6
6
- server: Fixed a bug where MSSQL and BigQuery would ignore environment variables set from the console
7
7
- server: Fixing bug in ReplaceMetadata parser - Moving from Alternative to committed-choice.
8
8
- server: Relax the unique operation name constraint when adding a query to a query collection
9
+
- server: officially deprecate query plan caching, which had already been disabled for a long time
9
10
- server/bigquery: Fix issues related to adding and querying from non-US datasets (closes [6937](https://github.com/hasura/graphql-engine/issues/6937)).
10
11
- console: add pagination on the Raw SQL results page
11
12
- console: fix issues with replacing invalid graphql identifiers in table and column names
12
13
- console: show error message on inconsistent objects table
13
14
14
-
15
15
## v2.0.3
16
16
(Add entries below in the order of server, console, cli, docs, others)
Copy file name to clipboardExpand all lines: docs/graphql/core/databases/ms-sql-server/queries/performance.rst
+5-135
Original file line number
Diff line number
Diff line change
@@ -15,8 +15,8 @@ MS SQL Server: Query performance
15
15
Introduction
16
16
------------
17
17
18
-
Sometimes queries can become slow due to large data volumes or levels of nesting.
19
-
This page explains how to identify the query performance, how the query plan caching in Hasura works, and how queries can be optimized.
18
+
Sometimes queries can become slow due to large data volumes or levels of nesting.
19
+
This page explains how to identify the query performance, and how queries can be optimized.
20
20
21
21
.. _ms_sql_server_analysing_query_performance:
22
22
@@ -53,146 +53,16 @@ The ``cost`` of a query is an arbitrary number generated by MS SQL Server and is
53
53
54
54
Read more about query performance analysis in the `MS SQL Server explain statement docs <https://docs.microsoft.com/en-us/sql/t-sql/queries/explain-transact-sql?view=azure-sqldw-latest>`__.
55
55
56
-
.. _ms_sql_server_query_plan_caching:
57
-
58
-
Query plan caching
59
-
------------------
60
-
61
-
How it works
62
-
^^^^^^^^^^^^
63
-
64
-
Hasura executes GraphQL queries as follows:
65
-
66
-
1. The incoming GraphQL query is parsed into an `abstract syntax tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`__ (AST) which is how GraphQL is represented.
67
-
2. The GraphQL AST is validated against the schema to generate an internal representation.
68
-
3. The internal representation is converted into an SQL statement (a `prepared statement <https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-queries/executing-statements/prepared-execution?view=sql-server-ver15>`__ whenever possible).
69
-
4. The (prepared) statement is executed on MS SQL Server to retrieve the result of the query.
70
-
71
-
For most use cases, Hasura constructs a "plan" for a query, so that a new instance of the same query can be executed without the overhead of steps 1 to 3.
72
-
73
-
For example, let's consider the following query:
74
-
75
-
.. code-block:: graphql
76
-
77
-
query getAuthor($id: Int!) {
78
-
authors(where: {id: {_eq: $id}}) {
79
-
name
80
-
rating
81
-
}
82
-
}
83
-
84
-
With the following variable:
85
-
86
-
.. code-block:: graphql
87
-
88
-
{
89
-
"id": 1
90
-
}
91
-
92
-
Hasura now tries to map a GraphQL query to a prepared statement where the parameters have a one-to-one correspondence to the variables defined in the GraphQL query.
93
-
The first time a query comes in, Hasura generates a plan for the query which consists of two things:
94
-
95
-
1. The prepared statement
96
-
2. Information necessary to convert variables into the prepared statement's arguments
97
-
98
-
For the above query, Hasura generates the following prepared statement (simplified):
99
-
100
-
.. code-block:: plpgsql
101
-
102
-
select name, rating from author where id = $1
103
-
104
-
With the following prepared variables:
105
-
106
-
.. code-block:: plpgsql
107
-
108
-
$1 = 1
109
-
110
-
This plan is then saved in a data structure called ``Query Plan Cache``. The next time the same query is executed,
111
-
Hasura uses the plan to convert the provided variables into the prepared statement's arguments and then executes the statement.
112
-
This will significantly cut down the execution time for a GraphQL query resulting in lower latencies and higher throughput.
113
-
114
-
Caveats
115
-
^^^^^^^
116
-
117
-
The above optimization is not possible for all types of queries. For example, consider this query:
The statement generated for ``getAuthorWithCondition`` is now dependent on the variables.
129
-
130
-
With the following variables:
131
-
132
-
.. code-block:: json
133
-
134
-
{
135
-
"condition": {"id": {"_eq": 1}}
136
-
}
137
-
138
-
the generated statement will be:
139
-
140
-
.. code-block:: plpgsql
141
-
142
-
select name, rating from author where id = $1
143
-
144
-
However, with the following variables:
145
-
146
-
.. code-block:: json
147
-
148
-
{
149
-
"condition": {"name": {"_eq": "John"}}
150
-
}
151
-
152
-
the generated statement will be:
153
-
154
-
.. code-block:: plpgsql
155
-
156
-
select name, rating from author where name = 'John'
157
-
158
-
A plan cannot be generated for such queries because the variables defined in the GraphQL query don't have a one-to-one correspondence to the parameters in the prepared statement.
159
-
160
56
Query optimization
161
57
------------------
162
58
163
-
Using GraphQL variables
164
-
^^^^^^^^^^^^^^^^^^^^^^^
165
-
166
-
In order to leverage Hasura's query plan caching (as explained in the :ref:`previous section <ms_sql_server_query_plan_caching>`) to the full extent, GraphQL queries should be defined with
167
-
variables whose types are **non-nullable scalars** whenever possible.
168
-
169
-
To make variables non-nullable, add a ``!`` at the end of the type, like here:
170
-
171
-
.. code-block:: graphql
172
-
:emphasize-lines: 1
173
-
174
-
query getAuthor($id: Int!) {
175
-
authors(where: {id: {_eq: $id}}) {
176
-
name
177
-
rating
178
-
}
179
-
}
180
-
181
-
If the ``!`` is not added and the variable is nullable, the generated query will be different depending on whether an ``id`` is passed or whether the variable is ``null``
182
-
(for the latter, there is no ``where`` statement present). Therefore, it's not possible for Hasura to create a reusable plan for a query in this case.
183
-
184
-
.. note::
185
-
186
-
Hasura is fast even for queries which cannot have a reusable plan.
187
-
This should concern you only if you face a high volume of traffic (thousands of requests per second).
188
-
189
59
.. _ms_sql_server_data_validation_mssql_indexes:
190
60
191
61
Using MS SQL indexes
192
62
^^^^^^^^^^^^^^^^^^^^
193
63
194
64
`MS SQL Server indexes <https://docs.microsoft.com/en-us/sql/relational-databases/indexes/indexes?view=sql-server-ver15>`__ are special lookup tables that MS SQL Server can use to speed up data lookup.
195
-
An index acts as a pointer to data in a table, and it works very similar to an index in the back of a book.
65
+
An index acts as a pointer to data in a table, and it works very similar to an index in the back of a book.
196
66
If you look in the index first, you'll find the data much quicker than searching the whole book (or - in this case - database).
197
67
198
68
Let's say we know that ``authors`` table is frequently queried by ``name``:
@@ -223,14 +93,14 @@ The following statement sets an index on ``name`` in the ``authors`` table.
223
93
224
94
.. tab:: CLI
225
95
226
-
:ref:`Create a migration manually <manual_migrations>` and add your create index statement to the ``up.sql`` file.
96
+
:ref:`Create a migration manually <manual_migrations>` and add your create index statement to the ``up.sql`` file.
227
97
Also, add an SQL statement to revert that statement to the ``down.sql`` file in case you need to :ref:`roll back <roll_back_migrations>` the migration.
Copy file name to clipboardExpand all lines: docs/graphql/core/databases/ms-sql-server/queries/variables-aliases-fragments-directives.rst
-4
Original file line number
Diff line number
Diff line change
@@ -50,10 +50,6 @@ In order to make a query re-usable, it can be made dynamic by using variables.
50
50
"author_id": 1
51
51
}
52
52
53
-
.. admonition:: Variables and performance
54
-
55
-
Variables have an impact on query performance. Refer to :ref:`query performance <ms_sql_server_query_performance>` to learn more about Hasura's query plan caching and about optimizing when using variables.
Copy file name to clipboardExpand all lines: docs/graphql/core/databases/postgres/queries/performance.rst
+5-135
Original file line number
Diff line number
Diff line change
@@ -15,8 +15,8 @@ Postgres: Query performance
15
15
Introduction
16
16
------------
17
17
18
-
Sometimes queries can become slow due to large data volumes or levels of nesting.
19
-
This page explains how to identify the query performance, how the query plan caching in Hasura works, and how queries can be optimized.
18
+
Sometimes queries can become slow due to large data volumes or levels of nesting.
19
+
This page explains how to identify the query performance, and how queries can be optimized.
20
20
21
21
.. _analysing_query_performance:
22
22
@@ -53,146 +53,16 @@ The ``cost`` of a query is an arbitrary number generated by Postgres and is to b
53
53
54
54
Read more about query performance analysis in the `Postgres explain statement docs <https://www.postgresql.org/docs/current/sql-explain.html>`__.
55
55
56
-
.. _query_plan_caching:
57
-
58
-
Query plan caching
59
-
------------------
60
-
61
-
How it works
62
-
^^^^^^^^^^^^
63
-
64
-
Hasura executes GraphQL queries as follows:
65
-
66
-
1. The incoming GraphQL query is parsed into an `abstract syntax tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`__ (AST) which is how GraphQL is represented.
67
-
2. The GraphQL AST is validated against the schema to generate an internal representation.
68
-
3. The internal representation is converted into an SQL statement (a `prepared statement <https://www.postgresql.org/docs/current/sql-prepare.html>`__ whenever possible).
69
-
4. The (prepared) statement is executed on Postgres to retrieve the result of the query.
70
-
71
-
For most use cases, Hasura constructs a "plan" for a query, so that a new instance of the same query can be executed without the overhead of steps 1 to 3.
72
-
73
-
For example, let's consider the following query:
74
-
75
-
.. code-block:: graphql
76
-
77
-
query getAuthor($id: Int!) {
78
-
authors(where: {id: {_eq: $id}}) {
79
-
name
80
-
rating
81
-
}
82
-
}
83
-
84
-
With the following variable:
85
-
86
-
.. code-block:: graphql
87
-
88
-
{
89
-
"id": 1
90
-
}
91
-
92
-
Hasura now tries to map a GraphQL query to a prepared statement where the parameters have a one-to-one correspondence to the variables defined in the GraphQL query.
93
-
The first time a query comes in, Hasura generates a plan for the query which consists of two things:
94
-
95
-
1. The prepared statement
96
-
2. Information necessary to convert variables into the prepared statement's arguments
97
-
98
-
For the above query, Hasura generates the following prepared statement (simplified):
99
-
100
-
.. code-block:: plpgsql
101
-
102
-
select name, rating from author where id = $1
103
-
104
-
With the following prepared variables:
105
-
106
-
.. code-block:: plpgsql
107
-
108
-
$1 = 1
109
-
110
-
This plan is then saved in a data structure called ``Query Plan Cache``. The next time the same query is executed,
111
-
Hasura uses the plan to convert the provided variables into the prepared statement's arguments and then executes the statement.
112
-
This will significantly cut down the execution time for a GraphQL query resulting in lower latencies and higher throughput.
113
-
114
-
Caveats
115
-
^^^^^^^
116
-
117
-
The above optimization is not possible for all types of queries. For example, consider this query:
The statement generated for ``getAuthorWithCondition`` is now dependent on the variables.
129
-
130
-
With the following variables:
131
-
132
-
.. code-block:: json
133
-
134
-
{
135
-
"condition": {"id": {"_eq": 1}}
136
-
}
137
-
138
-
the generated statement will be:
139
-
140
-
.. code-block:: plpgsql
141
-
142
-
select name, rating from author where id = $1
143
-
144
-
However, with the following variables:
145
-
146
-
.. code-block:: json
147
-
148
-
{
149
-
"condition": {"name": {"_eq": "John"}}
150
-
}
151
-
152
-
the generated statement will be:
153
-
154
-
.. code-block:: plpgsql
155
-
156
-
select name, rating from author where name = 'John'
157
-
158
-
A plan cannot be generated for such queries because the variables defined in the GraphQL query don't have a one-to-one correspondence to the parameters in the prepared statement.
159
-
160
56
Query optimization
161
57
------------------
162
58
163
-
Using GraphQL variables
164
-
^^^^^^^^^^^^^^^^^^^^^^^
165
-
166
-
In order to leverage Hasura's query plan caching (as explained in the :ref:`previous section <query_plan_caching>`) to the full extent, GraphQL queries should be defined with
167
-
variables whose types are **non-nullable scalars** whenever possible.
168
-
169
-
To make variables non-nullable, add a ``!`` at the end of the type, like here:
170
-
171
-
.. code-block:: graphql
172
-
:emphasize-lines: 1
173
-
174
-
query getAuthor($id: Int!) {
175
-
authors(where: {id: {_eq: $id}}) {
176
-
name
177
-
rating
178
-
}
179
-
}
180
-
181
-
If the ``!`` is not added and the variable is nullable, the generated query will be different depending on whether an ``id`` is passed or whether the variable is ``null``
182
-
(for the latter, there is no ``where`` statement present). Therefore, it's not possible for Hasura to create a reusable plan for a query in this case.
183
-
184
-
.. note::
185
-
186
-
Hasura is fast even for queries which cannot have a reusable plan.
187
-
This should concern you only if you face a high volume of traffic (thousands of requests per second).
188
-
189
59
.. _data_validation_pg_indexes:
190
60
191
61
Using PG indexes
192
62
^^^^^^^^^^^^^^^^
193
63
194
64
`Postgres indexes <https://www.tutorialspoint.com/postgresql/postgresql_indexes.htm>`__ are special lookup tables that Postgres can use to speed up data lookup.
195
-
An index acts as a pointer to data in a table, and it works very similar to an index in the back of a book.
65
+
An index acts as a pointer to data in a table, and it works very similar to an index in the back of a book.
196
66
If you look in the index first, you'll find the data much quicker than searching the whole book (or - in this case - database).
197
67
198
68
Let's say we know that ``authors`` table is frequently queried by ``name``:
@@ -223,14 +93,14 @@ The following statement sets an index on ``name`` in the ``authors`` table.
223
93
224
94
.. tab:: CLI
225
95
226
-
:ref:`Create a migration manually <manual_migrations>` and add your create index statement to the ``up.sql`` file.
96
+
:ref:`Create a migration manually <manual_migrations>` and add your create index statement to the ``up.sql`` file.
227
97
Also, add an SQL statement to revert that statement to the ``down.sql`` file in case you need to :ref:`roll back <roll_back_migrations>` the migration.
Copy file name to clipboardExpand all lines: docs/graphql/core/databases/postgres/queries/variables-aliases-fragments-directives.rst
-4
Original file line number
Diff line number
Diff line change
@@ -50,10 +50,6 @@ In order to make a query re-usable, it can be made dynamic by using variables.
50
50
"author_id": 1
51
51
}
52
52
53
-
.. admonition:: Variables and performance
54
-
55
-
Variables have an impact on query performance. Refer to :ref:`query performance <query_performance>` to learn more about Hasura's query plan caching and about optimizing when using variables.
0 commit comments