-
Notifications
You must be signed in to change notification settings - Fork 402
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
Slow introspection when using multiple custom types in a query #530
Comments
This is most likely the PostgreSQL's new JIT that is slow on that query. Try turning it off ( |
Thanks @elprans that does seem to be the issue. adding Since I can't change the
|
I've been also bitten by this bug. Postgresql 10 and 9.4 -- experienced timeouts only on 9.4. One more thing: it seems asyncpg doesn't use prepared queries for SELECT
t.oid,
t.typelem AS elemtype,
t.typtype AS kind
FROM
pg_catalog.pg_type AS t
INNER JOIN pg_catalog.pg_namespace ns ON (ns.oid = t.typnamespace)
WHERE
t.typname = $1 AND ns.nspname = $2 I see this in logs repeating for each type encoder/decoder registration. |
Hi, @elprans
I've been having trouble with this query because it's too slow, so if I turned off JIT as you said, the query was faster. But what can I do to stop them from issuing the query? |
Postgres does not send enough information to describe query output at the protocol level. In order to decode complex data types, like composite types, ranges, arrays, etc, |
That shouldn't be the case. That query is executed using regular |
Thank you very much for your answer, @elprans |
To turn of the jit with sqlalchemy 1.4.0b asyncpg … self.engine = create_async_engine(
- connection_string, pool_size=pool_size, **kwargs
+ connection_string, pool_size=pool_size, connect_args={'server_settings':{'jit': 'off'}}, **kwargs
) |
or if you're using GINO
|
…ries * refs MagicStack/asyncpg#530: apply "jit: off" option to DB connections - It is specified in `ai.backend.manager.models.base.pgsql_connect_opts` * Reuse the same single connection for all GraphQL resolvers and mutation methods
* fix: a long-standing transaction error - It is now reproducible reliably with the new SQLAlchemy + asyncpg combination! - Also fixed a hidden type conversion bug in AgentRegistry.set_kernel_status() due to a comma typo.... * fix/test: Update codes for population of DB fixtures - Eliminate manual primary key checks for duplicate entries by utilizing PostgreSQL's "on conflict" (upsert) support. - Fix up using special characters in database passwords by correctly escaping them using `urllib.parse.quote_plus()`. * fix: Do not rely on `rowcount` for SELECT queries - rowcount in SQLAlchemy does NOT represent the number of fetched rows for SELECT queries, in contrast to the Python's standard DB API. * fix: excessive DB connection establishment delay and optimize GQL queries - refs MagicStack/asyncpg#530: apply "jit: off" option to DB connections - It is specified in `ai.backend.manager.models.base.pgsql_connect_opts` - Reuse the same single connection for all GraphQL resolvers and mutation methods * fix: consistently use urlquote for db passwords * fix: all DB connections are now transactions * refactor: Finally, rename "dbpool" to "db"
* fix: a long-standing transaction error - It is now reproducible reliably with the new SQLAlchemy + asyncpg combination! - Also fixed a hidden type conversion bug in AgentRegistry.set_kernel_status() due to a comma typo.... * fix/test: Update codes for population of DB fixtures - Eliminate manual primary key checks for duplicate entries by utilizing PostgreSQL's "on conflict" (upsert) support. - Fix up using special characters in database passwords by correctly escaping them using `urllib.parse.quote_plus()`. * fix: Do not rely on `rowcount` for SELECT queries - rowcount in SQLAlchemy does NOT represent the number of fetched rows for SELECT queries, in contrast to the Python's standard DB API. * fix: excessive DB connection establishment delay and optimize GQL queries - refs MagicStack/asyncpg#530: apply "jit: off" option to DB connections - It is specified in `ai.backend.manager.models.base.pgsql_connect_opts` - Reuse the same single connection for all GraphQL resolvers and mutation methods * fix: consistently use urlquote for db passwords * fix: all DB connections are now transactions * refactor: Finally, rename "dbpool" to "db" Backported-From: main Backported-To: 20.09
In order to alleviate the serialisation caused by the single-threaded REST API frontend server we switch the respective database functions to asyncio. This allows the server to at least accept more requests while database operations are in progress. The works very well with MariaDB. Postgres shows a slow response on initial operations up to about 15 seconds caused by an autovacuum operation triggered by an introspection feature of asyncpg (MagicStack/asyncpg#530). The workaround is to disable the JIT of newer postgresql versions server-side for the time being. sqlite flat-out runs into "database is locked" errors. More research is required here. We rewrite the database URL to specific asyncio dialects for now. The plan is to switch to asyncio completely so we can leave them alone in the end. The SQLAlchemy dependency is raised to 1.4.24 because this is the first version to support asyncmy, the new asyncio mysql driver. Move sleeping for retries out of the critical section protected by locking to allow for even more parallelism. Remove the lock on analysis journal retrieval since reads should never conflict with each other. We need to keep the locking in analysis_add() even with async because multiple async calls of the routine may be in progress at various stages of processing and conflict with and possibly deadlock each other, particularly when using sqlite which will throw 'database is locked' errors after a timeout. Having a threading and asyncio Lock protect adding and updating of analyses from threading and asyncio context is likely to not work as required. The only hope is to switch analysis update to asyncio as well. Start to move to the 2.0 SQLAlchemy API using session.execute() and select(), delete() and update() statements. For the asyncio API this is requied since session.query() is not supported there. We switch some non-asyncio users as well while we're at it. This also allows for reusing of statements across retries. Start using session context handlers to get rid of explicit session closing. The testsuite is updated to match.
In order to alleviate the serialisation caused by the single-threaded REST API frontend server we switch the respective database functions to asyncio. This allows the server to at least accept more requests while database operations are in progress. The works very well with MariaDB. Postgres shows a slow response on initial operations up to about 15 seconds caused by an autovacuum operation triggered by an introspection feature of asyncpg (MagicStack/asyncpg#530). The workaround is to disable the JIT of newer postgresql versions server-side for the time being. sqlite flat-out runs into "database is locked" errors. More research is required here. We rewrite the database URL to specific asyncio dialects for now. The plan is to switch to asyncio completely so we can leave them alone in the end. The SQLAlchemy dependency is raised to 1.4.24 because this is the first version to support asyncmy, the new asyncio mysql driver. Move sleeping for retries out of the critical section protected by locking to allow for even more parallelism. Remove the lock on analysis journal retrieval since reads should never conflict with each other. We need to keep the locking in analysis_add() even with async because multiple async calls of the routine may be in progress at various stages of processing and conflict with and possibly deadlock each other, particularly when using sqlite which will throw 'database is locked' errors after a timeout. Having a threading and asyncio Lock protect adding and updating of analyses from threading and asyncio context is likely to not work as required. The only hope is to switch analysis update to asyncio as well. Start to move to the 2.0 SQLAlchemy API using session.execute() and select(), delete() and update() statements. For the asyncio API this is requied since session.query() is not supported there. We switch some non-asyncio users as well while we're at it. This also allows for reusing of statements across retries. Start using session context handlers to get rid of explicit session closing. The testsuite is updated to match.
In order to alleviate the serialisation caused by the single-threaded REST API frontend server we switch the respective database functions to asyncio. This allows the server to at least accept more requests while database operations are in progress. The works very well with MariaDB. Postgres shows a slow response on initial operations up to about 15 seconds caused by an autovacuum operation triggered by an introspection feature of asyncpg (MagicStack/asyncpg#530). The workaround is to disable the JIT of newer postgresql versions server-side for the time being. sqlite flat-out runs into "database is locked" errors. More research is required here. We rewrite the database URL to specific asyncio dialects for now. The plan is to switch to asyncio completely so we can leave them alone in the end. The SQLAlchemy dependency is raised to 1.4.24 because this is the first version to support asyncmy, the new asyncio mysql driver. Move sleeping for retries out of the critical section protected by locking to allow for even more parallelism. Remove the lock on analysis journal retrieval since reads should never conflict with each other. We need to keep the locking in analysis_add() even with async because multiple async calls of the routine may be in progress at various stages of processing and conflict with and possibly deadlock each other, particularly when using sqlite which will throw 'database is locked' errors after a timeout. Having a threading and asyncio Lock protect adding and updating of analyses from threading and asyncio context is likely to not work as required. The only hope is to switch analysis update to asyncio as well. Start to move to the 2.0 SQLAlchemy API using session.execute() and select(), delete() and update() statements. For the asyncio API this is requied since session.query() is not supported there. We switch some non-asyncio users as well while we're at it. This also allows for reusing of statements across retries. Start using session context handlers to get rid of explicit session closing. The testsuite is updated to match.
In order to alleviate the serialisation caused by the single-threaded REST API frontend server we switch the respective database functions to asyncio. This allows the server to at least accept more requests while database operations are in progress. The works very well with MariaDB. Postgres shows a slow response on initial operations up to about 15 seconds caused by an autovacuum operation triggered by an introspection feature of asyncpg (MagicStack/asyncpg#530). The workaround is to disable the JIT of newer postgresql versions server-side for the time being. sqlite flat-out runs into "database is locked" errors. More research is required here. We rewrite the database URL to specific asyncio dialects for now. The plan is to switch to asyncio completely so we can leave them alone in the end. The SQLAlchemy dependency is raised to 1.4.24 because this is the first version to support asyncmy, the new asyncio mysql driver. Move sleeping for retries out of the critical section protected by locking to allow for even more parallelism. Remove the lock on analysis journal retrieval since reads should never conflict with each other. We need to keep the locking in analysis_add() even with async because multiple async calls of the routine may be in progress at various stages of processing and conflict with and possibly deadlock each other, particularly when using sqlite which will throw 'database is locked' errors after a timeout. Having a threading and asyncio Lock protect adding and updating of analyses from threading and asyncio context is likely to not work as required. The only hope is to switch analysis update to asyncio as well. Start to move to the 2.0 SQLAlchemy API using session.execute() and select(), delete() and update() statements. For the asyncio API this is requied since session.query() is not supported there. We switch some non-asyncio users as well while we're at it. This also allows for reusing of statements across retries. Start using session context handlers to get rid of explicit session closing. The testsuite is updated to match.
In order to alleviate the serialisation caused by the single-threaded REST API frontend server we switch the respective database functions to asyncio. This allows the server to at least accept more requests while database operations are in progress. The works very well with MariaDB. Postgres shows a slow response on initial operations up to about 15 seconds caused by an autovacuum operation triggered by an introspection feature of asyncpg (MagicStack/asyncpg#530). The workaround is to disable the JIT of newer postgresql versions server-side for the time being. sqlite flat-out runs into "database is locked" errors. More research is required here. We rewrite the database URL to specific asyncio dialects for now. The plan is to switch to asyncio completely so we can leave them alone in the end. The SQLAlchemy dependency is raised to 1.4.24 because this is the first version to support asyncmy, the new asyncio mysql driver. Move sleeping for retries out of the critical section protected by locking to allow for even more parallelism. Remove the lock on analysis journal retrieval since reads should never conflict with each other. We need to keep the locking in analysis_add() even with async because multiple async calls of the routine may be in progress at various stages of processing and conflict with and possibly deadlock each other, particularly when using sqlite which will throw 'database is locked' errors after a timeout. Having a threading and asyncio Lock protect adding and updating of analyses from threading and asyncio context is likely to not work as required. The only hope is to switch analysis update to asyncio as well. Start to move to the 2.0 SQLAlchemy API using session.execute() and select(), delete() and update() statements. For the asyncio API this is requied since session.query() is not supported there. We switch some non-asyncio users as well while we're at it. This also allows for reusing of statements across retries. Start using session context handlers to get rid of explicit session closing. The testsuite is updated to match.
In order to alleviate the serialisation caused by the single-threaded REST API frontend server we switch the respective database functions to asyncio. This allows the server to at least accept more requests while database operations are in progress. The works very well with MariaDB. Postgres shows a slow response on initial operations up to about 15 seconds caused by an autovacuum operation triggered by an introspection feature of asyncpg (MagicStack/asyncpg#530). The workaround is to disable the JIT of newer postgresql versions server-side for the time being. sqlite flat-out runs into "database is locked" errors. More research is required here. We rewrite the database URL to specific asyncio dialects for now. The plan is to switch to asyncio completely so we can leave them alone in the end. The SQLAlchemy dependency is raised to 1.4.24 because this is the first version to support asyncmy, the new asyncio mysql driver. Move sleeping for retries out of the critical section protected by locking to allow for even more parallelism. Remove the lock on analysis journal retrieval since reads should never conflict with each other. We need to keep the locking in analysis_add() even with async because multiple async calls of the routine may be in progress at various stages of processing and conflict with and possibly deadlock each other, particularly when using sqlite which will throw 'database is locked' errors after a timeout. Having a threading and asyncio Lock protect adding and updating of analyses from threading and asyncio context is likely to not work as required. The only hope is to switch analysis update to asyncio as well. Start to move to the 2.0 SQLAlchemy API using session.execute() and select(), delete() and update() statements. For the asyncio API this is requied since session.query() is not supported there. We switch some non-asyncio users as well while we're at it. This also allows for reusing of statements across retries. Start using session context handlers to get rid of explicit session closing. The testsuite is updated to match.
|
|
@elprans this doesn't help, every first query i get engine = create_async_engine(
url,
json_serializer=simplejson.dumps,
json_deserializer=json_deserializer,
connect_args={'server_settings': {'jit': 'off'}},
**kwargs,
) |
look like any case doesn't work if you don't make |
Did somebody bug report to PG maillists? |
Is it possible to perform introspection manually? Without waiting for the query to be performed? Right now we need to manually execute the query in order to cache introspection. |
@stevanmilic agree it will be good to have a way to provide a static schema for asyncpg to avoid such runtime overhead. |
@elprans is it possible to execute SQL query without going through the prepare statement flow? It's rather a blocker for high freq apps to use asyncpg with enums if you must go through introspection, which is really slow. Something like |
I worked around this problem by registering all enums on connection creation: ENUM_TYPE = (
("public", "order_type_t"),
("orders", "user_type_t"),
)
async def register_enum_types(conn):
for schema, typename in ENUM_TYPE:
await conn.set_builtin_type_codec(typename, schema=schema, codec_name="text")
pool = await asyncpg.create_pool(..., init=register_enum_types) |
I have a WIP patch to automatically disable JIT for the introspection query.
Yes, via |
No, |
the issue with a local PostgreSQL install?: postgresql is installed locally on archlinux
uvloop?: yes
I have a few custom types (
CREATE TYPE ...
) in my database, and I'm running into issues where asyncpg's introspection stage on queries using 2 or more of these types are taking > 1 second to complete.e.g.
add
log_min_duration_statement = 500
to the default postgresql.confCreate a database
test
with schema:and run the following code:
watching the logs (on my system
sudo journalctl -u postgresql -f
, will show something like:I've traced this back to the call to
_introspect_types
in connection.py.From a bit of testing, it only happens if there are multiple custom types used in the query. e.g. if i change the query to simply be
INSERT INTO bigthings (num) VALUES ($1::FOURBIGINTS)
, then everything is nice and fast as expected, or if i change thebigthings.words
column to aVARCHAR
, then there is no problem. But as soon as I include two or more custom types (e.g. 2 enum types or 2 tuple types, or a mix) then I see the slow downs.Is there anything I can do to either remove the need for this introspection (e.g. giving asyncpg some hints about these types), or maybe I'm doing something wrong that I can correct?
The text was updated successfully, but these errors were encountered: