gino-starlette icon indicating copy to clipboard operation
gino-starlette copied to clipboard

Cannot release connection after query is canceled.

Open thomas-maschler opened this issue 5 years ago • 0 comments

Expected Behavior

When canceling query, connection should be released back into pool.

Actual Behavior

After canceling query (and catching errors in code) gino_startlette stills throws error asyncpg.exceptions.QueryCanceledError when trying to release connection.

Steps to Reproduce the Problem

from asyncpg import QueryCanceledError
from fastapi import FastAPI
from gino_starlette import Gino

app = FastAPI()

db = Gino(
    app,
    kwargs=dict(server_settings=dict(statement_timeout="1")), # cancel query after 1 ms
)


@app.get("/")
async def root():
    message = "success"
    try:
        await db.scalar("SELECT 1")
    except QueryCanceledError:
        message = "timeout"

    return {"message": message}

When called, Endpoint will correctly return {"message": "timeout"} but app will still throw QueryCanceledError

Traceback (most recent call last):
  File "****/site-packages/uvicorn/protocols/http/httptools_impl.py", line 385, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "****/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "****/site-packages/fastapi/applications.py", line 181, in __call__
    await super().__call__(scope, receive, send)
  File "****/site-packages/starlette/applications.py", line 102, in __call__
    await self.middleware_stack(scope, receive, send)
  File "****/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "****/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "****/site-packages/gino_starlette.py", line 83, in __call__
    await conn.release()
  File "****/site-packages/gino/engine.py", line 300, in release
    await dbapi_conn.release(True)
  File "****/site-packages/gino/engine.py", line 48, in release
    return await self._release()
  File "****/site-packages/gino/engine.py", line 84, in _release
    await self._pool.release(conn)
  File "****/site-packages/gino/dialects/asyncpg.py", line 280, in release
    await self._pool.release(conn)
  File "****/site-packages/asyncpg/pool.py", line 654, in release
    return await asyncio.shield(ch.release(timeout))
  File "****/site-packages/asyncpg/pool.py", line 216, in release
    raise ex
  File "****/site-packages/asyncpg/pool.py", line 206, in release
    await self._con.reset(timeout=budget)
  File "****/site-packages/asyncpg/connection.py", line 1137, in reset
    await self.execute(reset_query, timeout=timeout)
  File "****/site-packages/asyncpg/connection.py", line 295, in execute
    return await self._protocol.query(query, timeout)
  File "asyncpg/protocol/protocol.pyx", line 316, in query
asyncpg.exceptions.QueryCanceledError: canceling statement due to statement timeout

Specifications

  • Python version: 3.8
  • GINO version: 1.0.1
  • Starlette version: 0.1.1

Workarounds

I can suppress the error message when replacing this line https://github.com/python-gino/gino-starlette/blob/master/src/gino_starlette.py#L83 with this:

try:
    await conn.release()
except QueryCanceledError:
    pass

But I am not quite sure if this is the best approach

thomas-maschler avatar Nov 13 '20 02:11 thomas-maschler