enum and array in preparated statement hammer performance
See original GitHub issue- asyncpg version:
asyncpg==0.23.0(also tested withasyncpg==0.21.0 - PostgreSQL version:
PostgreSQL 13.3 (Ubuntu 13.3-0ubuntu0.21.04.1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 10.3.0-1ubuntu1) 10.3.0, 64-bit - Python version:
Python 3.8.10 - Platform: Ubuntu 21.04
- Do you use pgbouncer?: no
- Did you install asyncpg with pip?: yes
- Can the issue be reproduced under both asyncio and uvloop?: yes, makes no different
I have no idea what’s going on here, there’s a very weird (and big!) performance impact sometimes when you use an enum and a list/array together as prepared arguments.
I had this on a production system, but here is a minimal reproduction of the problem (debug is just here to test performance, it makes not difference:
import asyncio
import asyncpg
from devtools import debug
async def main():
conn = await asyncpg.connect('postgresql://postgres@localhost/test')
try:
await conn.execute(
"""
drop table if exists users;
drop type if exists user_spam;
create type user_spam as enum ('foo', 'bar');
create table users(
id serial primary key,
spam_enum user_spam default 'foo',
spam_varchar varchar(5) default 'foo',
str_list varchar(31)[]
)
"""
)
with debug.timer('spam_enum,variables,two-fields'):
await conn.execute(
'insert into users (spam_enum, str_list) values ($1, $2) returning id',
'bar', ('foo', 'bar')
)
with debug.timer('spam_enum,variables,only-spam_enum'):
await conn.execute('insert into users (spam_enum) values ($1) returning id', 'bar')
with debug.timer('spam_enum,variables,only-str_list'):
await conn.execute('insert into users (str_list) values ($1) returning id', ('foo', 'bar'))
with debug.timer('spam_varchar,variables,two-fields'):
await conn.execute(
'insert into users (spam_varchar, str_list) values ($1, $2) returning id',
'bar', ('foo', 'bar')
)
with debug.timer('spam_enum,static,two-fields'):
await conn.execute(
"insert into users (spam_enum, str_list) values ('bar', '{foo,bar}'::varchar[]) returning id",
)
with debug.timer('spam_varchar,static,two-fields'):
await conn.execute(
"insert into users (spam_varchar, str_list) values ('bar', '{foo,bar}'::varchar[]) returning id",
)
print('number of users created:', await conn.fetchval('select count(*) from users'))
finally:
await conn.close()
if __name__ == '__main__':
asyncio.run(main())
Output:
spam_enum,variables,two-fields: 0.679s elapsed
spam_enum,variables,only-spam_enum: 0.000s elapsed
spam_enum,variables,only-str_list: 0.000s elapsed
spam_varchar,variables,two-fields: 0.000s elapsed
spam_enum,static,two-fields: 0.000s elapsed
spam_varchar,static,two-fields: 0.000s elapsed
number of users created: 6
For now my work around is to use simple varchar fields instead of enums, but I’d love to know why this is happening.
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (6 by maintainers)
Top Results From Across the Web
Using a PreparedStatement to persist an array of Java Enums ...
And finally when I am running my application I have an array of the "Equipment" enums which I want to persist to the...
Read more >TypeScript Features to Avoid - Hacker News
Some people will argue a preference for string literal union types over enums because the string literal types don't have any runtime overhead....
Read more >Red Hat Satellite 6.11 Hammer CLI Guide
This document describes how to use the Hammer CLI tool to configure and manage Red Hat Satellite.
Read more >The Curse and Blessings of Dynamic SQL - Erland Sommarskog
This is followed by a short chapter on the performance aspects of dynamic SQL and highlights a few rules you should follow.
Read more >Chapter 3. Castor JDO - GitHub Pages
Castor JDO uses JDBC prepared statements to execute SQL statements against the specified RDBMS of your choice. Per definition, Castor JDO does not...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found

It only makes sense for queries that do lots of computation in complex expressions over a large number of rows. Unfortunately, Postgres deems asyncpg’s introspection query as “complex” and triggers JIT compilation on it, even though the query isn’t that complicated, just large.
This is a frequent source of grief, actually, there’s been a bunch of reports like this, so we might want to put a mitigation in place and disable
jitlocally when running the introspection query. Unfortunately, that’ll increase its overhead whenjitis off, although not as dramatically as you were seeing here.