Skip to content

Race condition on __async_init__ when min_size > 0 #320

Closed
@spumer

Description

@spumer
def get_pool(loop=None):
    global pool
    if pool is None:
        pool = asyncpg.create_pool(**config.DTABASE, loop=loop)
    return pool

Task1:

pool = get_pool()
await pool  # init

Task2:

pool = get_pool()
await pool  # init

Got AssertionError for asyncpg 0.15 and InternalClientError for asyncpg 0.16

  File "asyncpg/pool.py", line 356, in _async__init__
    await first_ch.connect()
  File "asyncpg/pool.py", line 118, in connect
    assert self._con is None

Both tasks try to connect PoolConnectionHolder's to ensure min_size connections will be ready.

  1. Task1 create pool
  2. Task1 await ensure min_size on async_init
  3. Task2 get same pool
  4. Task2 see initialization not done
  5. Task1 done await
  6. Task2 await ensure min_size on async_init and failed

Previously i try to use module (cached-property)[https://github.com/pydanny/cached-property], and this use-case broken cause for async properties it's save Future and don't use any additional locks.
And if init failed, exception will be cached too.
This is why i don't await pool when create it, just when i need connection.

Simple workaround is set min_size to 0. Acquire and then connect. Connection acquiring is safe.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions