Skip to content
New issue

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

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

Async support not compatible with alternative event loop implementations like uvloop #276

Closed
citreae535 opened this issue Dec 25, 2023 · 4 comments
Labels
bug Something isn't working patch available

Comments

@citreae535
Copy link

  1. What versions are you using?
    python-oracledb 2.0.0 on Python 3.12 and Oracle Database XE 18c. Both run inside docker containers on Windows 11 23H2 with WSL 2 backend. The database container image is gvenzl/oracle-xe:18-faststart.
  1. Is it an error or a hang or a crash?
    Error.

  2. What error(s) or behavior you are seeing?
    Using connect_async() with uvloop (a faster drop-in replacement for asyncio) throws the following exception:
    (The first line is the success output of asyncio.run(main()).)

('TEST',) 
Traceback (most recent call last):
  File "src/oracledb/impl/thin/connection.pyx", line 502, in oracledb.thin_impl.AsyncThinConnImpl._connect_with_address
  File "src/oracledb/impl/thin/protocol.pyx", line 567, in _connect_phase_one
  File "src/oracledb/impl/thin/protocol.pyx", line 730, in _connect_tcp
  File "src/oracledb/impl/thin/transport.pyx", line 301, in oracledb.thin_impl.Transport.set_from_socket
AttributeError: 'uvloop.loop.TCPTransport' object has no attribute 'setsockopt'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 189, in _run_module_as_main
  File "<frozen runpy>", line 148, in _get_module_details
  File "<frozen runpy>", line 112, in _get_module_details
  File "/app/example_service/__init__.py", line 18, in <module>
    uvloop.run(main())
  File "/app/.venv/lib/python3.12/site-packages/uvloop/__init__.py", line 109, in run
    return __asyncio.run(
           ^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 194, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.12/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "uvloop/loop.pyx", line 1517, in uvloop.loop.Loop.run_until_complete
  File "/app/.venv/lib/python3.12/site-packages/uvloop/__init__.py", line 61, in wrapper
    return await main
           ^^^^^^^^^^
  File "/app/example_service/__init__.py", line 8, in main
    async with oracledb.connect_async(
  File "/app/.venv/lib/python3.12/site-packages/oracledb/connection.py", line 1398, in __aenter__
    await self._connect_coroutine
  File "/app/.venv/lib/python3.12/site-packages/oracledb/connection.py", line 1443, in _connect
    await impl.connect(params_impl)
  File "src/oracledb/impl/thin/connection.pyx", line 609, in connect
  File "src/oracledb/impl/thin/connection.pyx", line 605, in oracledb.thin_impl.AsyncThinConnImpl.connect
  File "src/oracledb/impl/thin/connection.pyx", line 563, in _connect_with_params
  File "src/oracledb/impl/thin/connection.pyx", line 543, in _connect_with_description
  File "src/oracledb/impl/thin/connection.pyx", line 512, in _connect_with_address
  File "/app/.venv/lib/python3.12/site-packages/oracledb/errors.py", line 162, in _raise_err
    raise exc_type(_Error(message)) from cause
oracledb.exceptions.OperationalError: DPY-6005: cannot connect to database (CONNECTION_ID=+o4qjIZQoVmu5EZP54G9xw==).
'uvloop.loop.TCPTransport' object has no attribute 'setsockopt'
  1. Does your application call init_oracle_client()?
    No.
  1. Include a runnable Python script that shows the problem.
import asyncio

import oracledb
import uvloop


async def main():
    async with oracledb.connect_async(
        user="test", password="test", dsn="host.docker.internal/xepdb1"
    ) as connection:
        with connection.cursor() as cursor:
            await cursor.execute("select user from dual")
            async for result in cursor:
                print(result)


asyncio.run(main())
uvloop.run(main())
  1. Cause
    The thin mode transport implementation detects Async mode by checking if the supplied transport is an instance of asyncio.Transport. To support alternative implementations without depending on them directly, we need to detect Async mode without hard-coded runtime instance checks. Maybe passing in an explicit flag variable?
    self._is_async = isinstance(transport, asyncio.Transport)
    if self._is_async:
    sock = transport.get_extra_info('socket')
    else:
    sock = transport
    if description.expire_time > 0:
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
    if hasattr(socket, "TCP_KEEPIDLE") \
    and hasattr(socket, "TCP_KEEPINTVL") \
    and hasattr(socket, "TCP_KEEPCNT"):
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE,
    description.expire_time * 60)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 6)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 10)
    sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
@anthony-tuininga
Copy link
Member

I have pushed a patch that should add this support. If you are able to build from source you can verify that it works for you.

@citreae535
Copy link
Author

I've confirmed that the patch works. Thanks a lot. Can we expect a patch release soon?

@anthony-tuininga
Copy link
Member

Definitely! I am hoping to make a patch release sometime in the next week or so.

@anthony-tuininga
Copy link
Member

The patch has been included in version 2.0.1 which was just released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working patch available
Projects
None yet
Development

No branches or pull requests

2 participants