-
Notifications
You must be signed in to change notification settings - Fork 96
Description
hi gang -
our sqlalchemy test suite for the new async oracledb driver we are developing hangs semi-reliably on one particular series of tests that are fielding an intentional IntegrityError from a cursor.executemany() call.
the condition is extremely difficult to reproduce, and I've come up with a script that seems to reproduce it a bit more portably when run on a linux machine, but it still takes many iterations for the condition to hit. The script below runs the same set of operations over and over again - running it against an Oracle 18c express database here, it will hang usually within 200 iterations. but i have no idea if it will do this in other circumstances. if i move the create table step out of the loop, it doesnt hang at all.
import asyncio
import oracledb
import uuid
async def main():
conn = await oracledb.connect_async(
dsn="(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=oracle18c)(PORT=1521))(CONNECT_DATA=(SID=xe)))",
password="tiger",
user="scott",
)
cursor = conn.cursor()
try:
await cursor.execute("DROP TABLE data")
except:
pass
cursor.close()
i = 0
while True:
await run_async_thing(conn)
i += 1
print(f"finished run: {i}")
async def run_async_thing(conn):
cursor = conn.cursor()
await cursor.execute(
"""
CREATE TABLE data (
id VARCHAR2(30 CHAR) NOT NULL,
data VARCHAR2(50 CHAR),
has_server_default VARCHAR2(30 CHAR) DEFAULT 'some_server_default',
"uid" CHAR(32),
PRIMARY KEY (id)
)
"""
)
ret0, ret1, ret2 = (
cursor.var(oracledb.STRING, arraysize=10),
cursor.var(oracledb.STRING, arraysize=10),
cursor.var(oracledb.STRING, arraysize=10),
)
parameters = [
{
"data": f"d{i}",
'"uid"': uuid.uuid4().hex,
"ret_0": ret0,
"ret_1": ret1,
"ret_2": ret2,
}
for i in range(10)
]
try:
await cursor.executemany(
'INSERT INTO data (data, "uid") VALUES (:data, :"uid") '
"RETURNING data.id, data.data, data.has_server_default "
"INTO :ret_0, :ret_1, :ret_2",
parameters,
)
except oracledb.IntegrityError as err:
print("got error as expected!")
print("next go!")
await cursor.execute("DROP TABLE data")
cursor.close()
asyncio.run(main())so if I run it here it looks like:
got error as expected!
next go!
finished run: 1
got error as expected!
next go!
finished run: 2
got error as expected!
next go!
finished run: 3
got error as expected!
next go!
finished run: 4
got error as expected!
next go!
... keeps running ...
finished run: 29
got error as expected!
next go!
finished run: 30
got error as expected!
next go!
finished run: 31
then it's hung. press ctrl-C and stack shows as follows:
^CTraceback (most recent call last):
File "/usr/lib64/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.12/asyncio/base_events.py", line 684, in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
File "/home/classic/dev/sqlalchemy/test4.py", line 23, in main
await run_async_thing(conn)
File "/home/classic/dev/sqlalchemy/test4.py", line 62, in run_async_thing
await cursor.executemany(
File "/home/classic/.venv3/lib64/python3.12/site-packages/oracledb/cursor.py", line 1067, in executemany
await self._impl.executemany(
File "src/oracledb/impl/thin/cursor.pyx", line 297, in executemany
File "src/oracledb/impl/thin/protocol.pyx", line 817, in _process_single_message
File "src/oracledb/impl/thin/protocol.pyx", line 818, in oracledb.thin_impl.BaseAsyncProtocol._process_single_message
File "src/oracledb/impl/thin/protocol.pyx", line 769, in _process_message
File "src/oracledb/impl/thin/protocol.pyx", line 872, in _reset
File "src/oracledb/impl/thin/packet.pyx", line 571, in wait_for_packets_async
asyncio.exceptions.CancelledError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/classic/dev/sqlalchemy/test4.py", line 77, in <module>
asyncio.run(main())
File "/usr/lib64/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/lib64/python3.12/asyncio/runners.py", line 123, in run
raise KeyboardInterrupt()
KeyboardInterrupt
that's what I got so far. Will see if @CaselIT can reproduce on his environment.