Skip to content

_StatementCache does not call on_remove callback when clear()'ed #416

New issue

Have a question about this project? No Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “No 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? No Sign in to your account

Closed
un-def opened this issue Mar 15, 2019 · 2 comments
Closed

_StatementCache does not call on_remove callback when clear()'ed #416

un-def opened this issue Mar 15, 2019 · 2 comments

Comments

@un-def
Copy link
Contributor

un-def commented Mar 15, 2019

  • asyncpg version: 0.18.3
  • PostgreSQL version: 9.6
  • Do you use a PostgreSQL SaaS?: no
  • Python version: 3.7
  • Platform: Ubuntu 18.04.2 LTS
  • Do you use pgbouncer?: no
  • Did you install asyncpg with pip?: yes
  • Can the issue be reproduced under both asyncio and
    uvloop?
    : not related

_StatementCache on_remove callback is called for expired entries (1) and for least recently used entries when the cache is full (2), but not when clear() method is called (3).

Connection sets own _maybe_gc_stmt method (4) as a callback for _StatementCache. The method is used to mark non-referenced PreparedStatementState objects as closed and put them in _stmts_to_close set to schedule to close them later (they will be closed on the server side when _cleanup_stmts is called (5)).

As noted above, the callback is not called when the cache is cleared by _StatementCache.clear method, therefore the statements will never be closed on the postgres side (“closed” in the meaning of the Close frontend protocol message) and can no longer be used by asyncpg. This leads to memory leak on the server side until the connection is closed.

There are two Connection methods that used to drop the cache:_drop_local_statement_cache and _drop_global_statement_cache (6). Both of them (directly or indirectly) call _StatementCache.clear. Some Connection methods (set_type_codec, reset_type_codec, set_builtin_type_codec, reload_schema_state) drop the cache unconditionally, some methods do so if the database schema has been changed (_do_execute → (execute, executemany, fetch, fetchval, fetchrow)). In any case, each cache drop may increase the number of unreachable named prepared statements that eat postgres memory.


Is this a bug or an expected behavior?

un-def added a commit to un-def/asyncpg that referenced this issue Mar 19, 2019
@un-def
Copy link
Contributor Author

un-def commented Mar 19, 2019

I've added some extra checks to test_cache_invalidation.py, here is an example of failed tests.

@elprans
Copy link
Member

elprans commented Mar 19, 2019

Closed via #417.

No Sign up for free to join this conversation on GitHub. Already have an account? No Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants