tortoise / tortoise/tortoise-orm

Unable to perform any orm related stuff from a websocket connection

Open
#902 8 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
5.6k
Forks
516
Avg merge
2d 21h
Merged PRs (30d)
9

Description

Describe the bug
Whenever I try to connect to a websocket connection from pytest, and I try to run some queries in the database, it crashes and returns the following error

Traceback:

apps/users/selectors.py:7: in get_user
    return await User.get_or_none(**kwargs)
/usr/local/lib/python3.8/site-packages/tortoise/queryset.py:885: in _execute
    instance_list = await self._db.executor_class(
/usr/local/lib/python3.8/site-packages/tortoise/backends/base/executor.py:124: in execute_select
    _, raw_results = await self.db.execute_query(query.get_sql())
/usr/local/lib/python3.8/site-packages/tortoise/backends/asyncpg/client.py:36: in translate_exceptions_
    return await func(self, *args)
/usr/local/lib/python3.8/site-packages/tortoise/backends/asyncpg/client.py:186: in execute_query
    return len(rows), rows
/usr/local/lib/python3.8/site-packages/tortoise/backends/base/client.py:308: in __aexit__
    await self.pool.release(self.connection)
/usr/local/lib/python3.8/site-packages/asyncpg/pool.py:666: in release
    return await asyncio.shield(ch.release(timeout))
/usr/local/lib/python3.8/site-packages/asyncpg/pool.py:218: in release
    raise ex
/usr/local/lib/python3.8/site-packages/asyncpg/pool.py:208: in release
    await self._con.reset(timeout=budget)
/usr/local/lib/python3.8/site-packages/asyncpg/connection.py:1311: in reset
    await self.execute(reset_query, timeout=timeout)
/usr/local/lib/python3.8/site-packages/asyncpg/connection.py:297: in execute
    return await self._protocol.query(query, timeout)
asyncpg/protocol/protocol.pyx:323: in query
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   asyncpg.exceptions._base.InterfaceError: cannot perform operation: another operation is in progress

See my code below:


async def is_authenticated(token: str = Security(oauth2_scheme)) -> Union[User, Exception]:
    """ Verifies access token, returns user if it is valid (authenticated) else throws an error"""
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
        if payload['token_type'] != "access":
            raise CustomHTTPException(status_code=status.HTTP_403_FORBIDDEN,
                                      detail="Could not validate credentials - Wrong token type)")
        token_data = TokenData(**payload)
    except jwt.JWTError:
        raise CustomHTTPException(status_code=status.HTTP_403_FORBIDDEN,
                                  detail="Could not validate credentials - Invalid signature of access token")
    user = await get_user(id=token_data.user_id)  **# Error is thrown right here** 
    if not user:
        raise CustomHTTPException(status_code=status.HTTP_404_NOT_FOUND,
                                  detail="User not found")
    return user

async def is_authenticated_ws(websocket: WebSocket,
                              token: str = Query("")):
    # Websocket specific authentication
    try:
        return await is_authenticated(token)
    except:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)


@websocket_app.websocket("/")
async def websocket_endpoint(websocket: WebSocket,
                             current_user: User = Depends(is_authenticated_ws)):
    if not current_user:
        return

    service = await accept_user_connection(user_id=current_user.id, websocket=websocket)
    await run_until_first_complete(
        (producer_handler, {"service": service}),
        (consumer_handler, {"service": service, "current_user": current_user})
    )


class TestWebSockets:
    @pytest.mark.asyncio
    async def test_websocket_events(self, ws_client, actor_user):
        token = create_token(actor_user.id, generate_verification_code())
        with ws_client.websocket_connect(f"/ws/?token={token['access_token']}") as websocket:
            ...


Obviously it has to do something with the event loop. It is a huge but since in a production environment I should be able to test websocket events without any issues but right now I cant even query something from the database before I connect to a websocket.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/users/selectors.py and TestWebSockets.test_websocket_events, then follow the asyncpg traceback through tortoise/backends/asyncpg/client.py and the connection release path. Reproduce the websocket test and inspect how the event loop and database connection are used around is_authenticated_ws. Done means the websocket test can query the database without asyncpg.InterfaceError.

Written by the indexing model from the issue text.

Assessment

Tech stack
postgresql, python
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.