aiosqlite icon indicating copy to clipboard operation
aiosqlite copied to clipboard

Aiosqlite leaves thread hanging

Open raees-khan opened this issue 1 year ago • 3 comments

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:

raees-khan avatar Apr 05 '24 19:04 raees-khan

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?

amyreese avatar Apr 06 '24 21:04 amyreese

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

TJAkalit avatar Apr 08 '24 20:04 TJAkalit

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

raees-khan avatar Apr 09 '24 16:04 raees-khan

Got hanging by the following code at MacOS with python3.10 and aiosqlite==0.20.0

import asyncio
from contextlib import asynccontextmanager

import aiosqlite


@asynccontextmanager
async def open_db():
    async with aiosqlite.connect("db.sqlite3"):
        yield


@asynccontextmanager
async def nest_open():
    async with open_db():
        yield


async def gen():
    async with nest_open():
        yield


async def main():
    async for _ in gen():
        raise TypeError()


asyncio.run(main())

Related issue #306

waketzheng avatar Dec 11 '24 11:12 waketzheng

Dirty workaround while we wait for a release with the daemon option:

import asyncio
import aiosqlite
from aiosqlite import Cursor

async def main():
    awaitable_db = aiosqlite.connect(":memory:")
	# thread not started yet. It starts in the `__await__` method
    awaitable_db.daemon = True
    # Now it will start with daemon == True
	db = await awaitable_db
    cursor: Cursor = await db.execute("select 1")
    print(await cursor.fetchone())


if __name__ == "__main__":
    asyncio.run(main())

garbelini avatar Jan 09 '25 00:01 garbelini

Please see https://github.com/omnilib/aiosqlite/issues/299#issuecomment-2630067292

amyreese avatar Feb 03 '25 07:02 amyreese