Skip to content

Prepared statements being recreated on every call of fetch #453

Closed
@mklokocka

Description

@mklokocka
  • asyncpg version: 0.18.3
  • PostgreSQL version: 9.4
  • Do you use a PostgreSQL SaaS? If so, which? Can you reproduce
    the issue with a local PostgreSQL install?
    : I use the official Docker image with custom schema and test data.
  • Python version: 3.7
  • Platform: MacOS
  • Do you use pgbouncer?: No
  • Did you install asyncpg with pip?: Yes
  • If you built asyncpg locally, which version of Cython did you use?: -
  • Can the issue be reproduced under both asyncio and
    uvloop?
    : Yes

Hello,

I am facing a peculiar problem with the way prepared statements are handled. I use the following architecture:

aiohttp application, which initializes a pool of 1 to 20 db connections on init.

Data is periodically refreshed from the DB (once in a few minutes for most tables). I have a special class which handles the loading of data from DB and caches them to memory and to Redis (since multiple containers of the same app are running and I would like to minimize fetches from DB). This class is instantiated by a factory method which creates (besides other arguments) a load coroutine, which gets query passed into it by the factory.

The queries have no parameters and are static through out the runtime.

load functions works by getting a connection from the pool, and calling connection.fetch on the given query. As per my understanding, the query should then be turned into a prepared statement, cached into a builtin LRU cache, and reused in later calls. However, it seems that each call to load (which is periodic) gets a new LRU cache for some reason, creating the prepared statements anew. But when I run connection.fetch on SELECT * FROM pg_prepared_statements I see that the number of prepared statements held by the connection increases in each call of fetch.

Indeed, adding some prints to connection.py I found out that the statements get recreated and put into the cache on each call, since the cache is empty. I thought that perhaps it is because the connections I get from the pool differ, but since pg_prepared_statements is local to a session (a connection?) I think this is not the case. Indeed, limiting the size of the pool to max_size=1 did not solve this issue.

This causes my Postgres to slowly drain more and more memory until the connections are reset. Disabling the LRU cache with statement_cache_size=0 avoids this, but I believe that this behaviour is not intended.

I tried to make a minimal reproducer but haven't yet succeeded.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions