Skip to content

SQLite transaction stays open when its task is cancelled #188

Closed

Description

@jrderuiter

A cancelled task can leave an SQLite transaction open. After that, every new transaction on the connection fails with cannot start a transaction within a transaction.

Cause

TxnConnection.transaction() in mautrix/util/async_db/aiosqlite.py rolls back only on Exception:

try:
    yield
except Exception:
    await self.rollback()
    raise
else:
    await self.commit()
finally:
    in_transaction.reset(token)

asyncio.CancelledError is not an Exception. When a task is cancelled inside the transaction, neither rollback() nor commit() runs. The connection goes back to the pool with the transaction still open. The next BEGIN TRANSACTION fails.

Impact

We use mautrix 0.21.1 through Hermes Agent, which keeps the Matrix crypto store in SQLite and cancels work with timeouts. After the first cancellation inside a crypto store transaction, the bot cannot share room keys, and every encrypted message fails.

Reproduce

import asyncio, os
from mautrix.util.async_db import Database

async def main():
    db = Database.create("sqlite:///" + os.path.abspath("repro.db"))
    await db.start()
    await db.execute("CREATE TABLE IF NOT EXISTS t (x INT)")

    async def slow():
        async with db.acquire() as conn, conn.transaction():
            await conn.execute("INSERT INTO t VALUES (1)")
            await asyncio.sleep(10)

    task = asyncio.create_task(slow())
    await asyncio.sleep(0.2)
    task.cancel()
    try:
        await task
    except asyncio.CancelledError:
        pass

    async with db.acquire() as conn, conn.transaction():
        await conn.execute("INSERT INTO t VALUES (2)")

asyncio.run(main())

The last transaction fails with sqlite3.OperationalError: cannot start a transaction within a transaction.

Fix

Roll back on BaseException instead of Exception.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions