omnilib / aiosqlite

asyncio bridge to the standard sqlite3 module
https://aiosqlite.omnilib.dev
MIT License
1.22k stars 94 forks source link

Aiosqlite leaves thread hanging #290

Open raees-khan opened 7 months ago

raees-khan commented 7 months ago
class ProxyClass:
     def __init__(self):
         self._conn = None
         self._connected = False

    async def connect(self):
        self._conn = await aiosqlite.connect(':memory:')
        self._connected = True

    def close(self):
        await self._conn.close()
        self._connected = False

    def __getattr__(self, name):
        return getattr(self._conn, name)

async def run():
    db = ProxyClass()
    await db.connect()
   try:
        #perform some long running db updates
        #commit changes
   finally:
        await db.close()

I import this in a script and call run using asyncio.run(run()). It finishes but the program never exits because the thread does not get stopped and ctrl+c spits following.

^CException ignored in: <module 'threading' from '/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/threading.py'> Traceback (most recent call last): File "/Applications/Xcode.app/Contents/Developer/Library/Frameworks/Python3.framework/Versions/3.9/lib/python3.9/threading.py", line 1448, in _shutdown lock.acquire() KeyboardInterrupt:

amyreese commented 7 months ago

I can’t replicate this with the example code. Are you sure your “long running db updates” have actually completed before pressing ctrl-c? Can you provide more insight on what that long running functionality entails?

TJAkalit commented 7 months ago

Hello I have the same problem. But in my case its short select for two rows in result.

raees-khan commented 7 months ago

I have found that it happens when you have multiple connections (aka multiple threads).

import aiosqlite import asyncio

class ProxyClass:
    def __init__(self):
        self._conn = None
        self._connected = False

    async def connect(self):
        self._conn = await aiosqlite.connect(':memory:')
        self._connected = True

    async def close(self):
        await self._conn.close()
        self._connected = False

    def __getattr__(self, name):
        return getattr(self._conn, name)

class DBRunner:

    def __init__(self):
        self._dbs = {
            "db1": ProxyClass(),
            "db2": ProxyClass()
        }

    async def init_dbs(self):
        await asyncio.gather(
            *[db.connect() for db in self._dbs.values()]
        )

    async def close_dbs(self):
        for db in self._dbs.values():
            await db.close()

    async def run(self, name):
        db = self._dbs[name]
        await db.execute("CREATE TABLE memview_v1 (X, Y)")
        await db.execute("INSERT INTO memview_v1 (X, Y) VALUES(1,2)")
        await db.commit()       

async def main():
    runner = DBRunner()
    try:
        await runner.init_dbs()
        await asyncio.gather(*[runner.run(name) for name in ('db1', 'db2')])
    finally:
        await runner.close_dbs()

asyncio.run(main())

for each connection a separate thread is created. The actual code makes thousands of inserts updates and deletes to disk based db connection, but I ensure to call db.close() of each db in my finally block. The program never really exits and the shell hangs and ctrl+c yields that output