Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
ErikKalkoken opened this issue May 26, 2023 · 1 comment · May be fixed by #305
Open

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

ErikKalkoken opened this issue May 26, 2023 · 1 comment · May be fixed by #305

Comments

@ErikKalkoken
Copy link

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

  • OS: Ubuntu 22.04
  • Python version: 3.11
  • aiosqlite version: 0.19.0
  • Can you repro on 'main' branch? yes
  • Can you repro in a clean virtualenv? yes
@ErikKalkoken ErikKalkoken changed the title Event loop is closed exception raised during shutdown "Event loop is closed" exception raised during shutdown May 26, 2023
@davidandreoletti
Copy link

davidandreoletti commented Aug 6, 2024

@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:

  • User thread's asyncio event loop exists and is not closed.
    • Let's call this loop loop1.
  • User thread creates a Connection instance.
    • Connection's own thread has also an asyncio event loop. Let's call this loop loop2
  • User thread execute a statement on the Connection instance
    • statement is executed on loop2
  • User thread closes it own loop1 for (so far) unknown reason.
    • aiosqlite await for the statement's result running on loop2.
    • once available, aiosqlite set the statement's result as the future's result.
      • future's result can't be set (via function set_result / set_exception) since the function can't be executed on the future's binded loop loop1 because loop1 is closed.
  • This behaviour yields the exception "Event loop closed" in loop1's execution context

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants