Hangs on subsequent connect, timeout ignored
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.
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.
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.
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!')