aio-libs/aioodbc

Connection not returning to pool

dszmaj opened this issue · 3 comments

Hey,
I have confusing problem. There is an endpoint that makes for example 15 queries. On development environment I create pool of exactly 10 to 20 connections and I expect that after hitting this endpoint I will be able to hit it again, especially that it don't use those connections all at once, but I get concurrent.futures._base.CancelledError error instead so it looks like connection didn't return to pool and framework triggered standard RequestTimeoutError (I made this assumption because increasing pool size solves the problem).

I make queries with this code:

from collections import namedtuple

from sanic.exceptions import NotFound


def _row_namedtuple(cursor):
    return namedtuple('Row', _field_names(cursor))


def _field_names(cursor):
    return [name[0].lower() for name in cursor.description]


class DbExecutor:
    def __init__(self, sql, pool, params=()):
        self.sql = sql
        self.params = params
        self.pool = pool
        self._connection = None
        self._cursor = None

    async def __aenter__(self):
        self._connection = await self.pool.acquire()
        self._cursor = await self._connection.cursor()
        await self._cursor.execute(self.sql, self.params)
        return self._cursor

    async def __aexit__(self, exc_type, exc, tb):
        await self._cursor.close()
        await self._connection.close()


async def fetch_dict_all(sql, pool, params=()):
    async with DbExecutor(sql, pool, params) as cursor:
        return [dict(zip(_field_names(cursor), row)) for row in await cursor.fetchall()]


async def fetch_dict_row(sql, pool, params=()):
    async with DbExecutor(sql, pool, params) as cursor:
        row = await cursor.fetchone()
        return dict(zip(_field_names(cursor), row)) if row else []

create pool with this code:

@app.listener('before_server_start')
async def create_pool(app, loop):
    dsn = 'DRIVER=OracleODBC;DBQ=server;UID=login;PWD=password'
    app.db = await aioodbc.create_pool(dsn=dsn, loop=loop, minsize=10, maxsize=10)


@app.listener('after_server_stop')
async def discard_pool(app, loop):
    app.db.close()
    await app.db.wait_closed()

example of query:

details = await fetch_dict_all('sql query', app.db, params)
Ubuntu 16.04
Oracle ODBC Driver
aioodbc == 0.2.0
pyodbc == 4.0.21

You need to return connection to the pool explicitly if you do not use context manager:

    conn = await pool.acquire()
    cur = await conn.cursor()
    await cur.execute("SELECT 42;")
    r = await cur.fetchall()
    await cur.close()
    await pool.release(conn)

or

    async with pool.acquire() as conn:
        cur = await conn.cursor()
        await cur.execute("SELECT 42;")
        r = await cur.fetchall()
        await cur.close()

Hey, thanks a lot, it solved issue and can be closed. You saved my ass actually :P

So to speak, Oracle connection working well and fast.

You are welcome, glad that worked for you.