aioodbc icon indicating copy to clipboard operation
aioodbc copied to clipboard

Hangs on subsequent connect, timeout ignored

Open madduck opened this issue 1 year ago • 3 comments

Hello, I have to deal with a Microsoft Access "database" on the Windows "operating system". Yes, thank you for your sympathy.

I have aioodbc working to the point where I can get an async connection & cursor, execute a query, and fetch results. Once. If, within the same async app, I then try to create another connection to an Access "database", even if it's a different one (I copy it to a temporary file just to be sure), the app will hang awaiting the connection to happen. When this is the case, I need to cancel the app, which then results in this traceback:

  File "…\tptools\webserver.py", line 151, in load_tp_file
    async with aioodbc.connect(
               ^^^^^^^^^^^^^^^^
  File "…\site-packages\aioodbc\utils.py", line 86, in __aenter__
    self._obj = await self._coro
                ^^^^^^^^^^^^^^^^
  File "…\site-packages\aioodbc\connection.py", line 277, in _connect
    await conn._connect()
  File "…\site-packages\aioodbc\connection.py", line 81, in _connect
    self._conn = await f
                 ^^^^^^^
  File "…\Lib\concurrent\futures\thread.py", line 59, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pyodbc.Error: ('HY000', 'The driver did not supply an error!')

as you can see, the code is awaiting the connection when it gets cancelled. It doesn't matter how long I wait before I Ctrl-C my way out of the code.

Also, I did set timeout=10 on the connect() call, but that does not seem to do anything, at least not in this context.

This is the DSN I used:

DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\Users\martin\AppData\Local\Temp\TP2jsonuqc7y660\Demo.tp;Pwd=d4R2GY76w2qzZ

As I said, it works once always, sometimes twice, I've even seen it work 5 times in a row — the code is triggered whenever the database file's mtime changes — but eventually, it will hang.

Of course, if I turn tracing on in the Windows ODBC manager, the problem never happens. So this looks like a race condition of sorts, on this quality "operating system" with this quality "database".

Any clues?

PS: I tried to make a test case, but the database is sensitive. If I use a test database, that is a bit smaller, I cannot reproduce the problem.

madduck avatar Dec 25 '24 20:12 madduck

As a further piece of information, trying to use asyncio.timeout or asyncio.wait_for do not change the behaviour:

conn = await asyncio.wait_for(aioodbc.connect(dsn=connstr), timeout=10)

… this will run indefinitely, until I ctrl-c my way out, then we get the exact same above traceback.

madduck avatar Dec 25 '24 21:12 madduck

Also note that I have not been able to reproduce this problem with pyodbc synchronously, i.e. with pyodbc, I can load the same database a million times in a row without problems.

madduck avatar Dec 25 '24 22:12 madduck

odbctest.zip

Here is a test file that illustrates the problem. Note that a simple test database is included. Run like so:

> python odbctestpy -i i/path/tp/some/access.mdb -y 'select * from playermatch' -p 1
Running synchronously…
Looping
356 results
Looping
356 results
[…]

and observe how it works just fine, endlessly.

Now, run it again with -a, which makes the script use aioodbc instead:

> python odbctestpy -i i/path/tp/some/access.mdb -y 'select * from playermatch' -p 1 -a
Running asynchronously…
Looping
356 results
Looping
356 results
Looping
Traceback (most recent call last):
  File "C:\Users\martin\tptools\tptools\cli\odbctest.py", line 103, in <module>
    sys.exit(main())
             ~~~~^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\click\core.py", line 1161, in __call__
    return self.main(*args, **kwargs)
           ~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\click\core.py", line 1082, in main
    rv = self.invoke(ctx)
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\click\core.py", line 1443, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\click\core.py", line 788, in invoke
    return __callback(*args, **kwargs)
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\click\decorators.py", line 33, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "C:\Users\martin\tptools\tptools\cli\odbctest.py", line 96, in main
    asyncio.run(async_poll_db(config=kwargs))
    ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 195, in run
    return runner.run(main)
           ~~~~~~~~~~^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\asyncio\base_events.py", line 725, in run_until_complete
    return future.result()
           ~~~~~~~~~~~~~^^
  File "C:\Users\martin\tptools\tptools\cli\odbctest.py", line 41, in async_poll_db
    async with aioodbc.connect(dsn=connstr) as conn:
               ~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\aioodbc\utils.py", line 86, in __aenter__
    self._obj = await self._coro
                ^^^^^^^^^^^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\aioodbc\connection.py", line 277, in _connect
    await conn._connect()
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\site-packages\aioodbc\connection.py", line 81, in _connect
    self._conn = await f
                 ^^^^^^^
  File "C:\Users\martin\AppData\Local\Programs\Python\Python313\Lib\concurrent\futures\thread.py", line 59, in run
    result = self.fn(*self.args, **self.kwargs)
pyodbc.Error: ('HY000', 'The driver did not supply an error!')

madduck avatar Mar 23 '25 18:03 madduck