omnilib / aiosqlite

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

"Event loop is closed" exception raised during shutdown #241

Open ErikKalkoken opened 1 year ago

ErikKalkoken commented 1 year ago

Description

I am current building a persistent queue library based on aiosqlite and think I might have found a bug.

When a task is still trying to perform SQL queries during shutdown it will raise the following exception:

Traceback (most recent call last):
  File "/usr/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "/home/erik/python/projects/aiodiskqueue/venv/lib/python3.11/site-packages/aiosqlite/core.py", line 121, in run
    get_loop(future).call_soon_threadsafe(set_exception, future, e)
  File "/usr/lib/python3.11/asyncio/base_events.py", line 806, in call_soon_threadsafe
    self._check_closed()
  File "/usr/lib/python3.11/asyncio/base_events.py", line 519, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

In order to allow for a graceful shutdown of a service I think it should log the SQL statement that failed, instead of producing this exception.

Here is a minimal code example to reproduce this behavior:

import asyncio
import aiosqlite

async def consumer(db_path):
    async with aiosqlite.connect(db_path, isolation_level=None) as db:
        for num in range(100):
            message = f"message {num + 1}"
            await db.execute("INSERT INTO messages (message) VALUES (?);", (message,))

async def main():
    db_path = "event_loop_bug.sqlite"
    async with aiosqlite.connect(db_path, isolation_level=None) as db:
        await db.execute("DROP TABLE IF EXISTS messages;")
        await db.execute("CREATE TABLE messages (message TEXT);")
    asyncio.create_task(consumer(db_path))

asyncio.run(main())

Details

davidandreoletti commented 2 months ago

@amyreese Currently experiencing this on py312 / aiosqlite v0.20.0 too. @ErikKalkoken PS: On py311 / aiosqlite v0.20.0, I did not experience this error.

Reviewing the code, the event flows is this:

Therefore, any transaction queue item results must forwarded to loop2 before await connection.close() returns.