diff --git a/docs/transactions.rst b/docs/transactions.rst index a3e6e00a2..513e292dc 100644 --- a/docs/transactions.rst +++ b/docs/transactions.rst @@ -7,12 +7,29 @@ Transactions Tortoise ORM provides a simple way to manage transactions. You can use the ``atomic()`` decorator or ``in_transaction()`` context manager. -``atomic()`` and ``in_transaction()`` can be nested, and the outermost block will -be the one that actually commits the transaciton. Tortoise ORM doesn't support savepoints yet. +``atomic()`` and ``in_transaction()`` can be nested. The inner blocks will create transaction savepoints, +and if an exception is raised and then caught outside of a nested block, the transaction will be rolled back +to the state before the block was entered. The outermost block will be the one that actually commits the transaction. +The savepoints are supported for Postgres, SQLite, MySQL and SQLite. -In most cases ``asyncio.gather`` or similar ways to spin up concurrent tasks can be used safely -when querying the database or using transactions. Tortoise ORM will ensure that for the duration -of a query, the database connection is used exclusively by the task that initiated the query. + .. code-block:: python3 + + # this block will commit changes on exit + async with in_transaction(): + await MyModel.create(name='foo') + try: + # this block will create a savepoint + async with in_transaction(): + await MyModel.create(name='bar') + # this will rollback to the savepoint, meaning that + # the 'bar' record will not be created + raise Exception() + except Exception: + pass + +When using ``asyncio.gather`` or similar ways to spin up concurrent tasks in a transaction block, +avoid having nested transaction blocks in the concurrent tasks. Transactions are stateful and nested +blocks are expected to run sequentially, not concurrently. .. automodule:: tortoise.transactions diff --git a/tortoise/backends/mysql/client.py b/tortoise/backends/mysql/client.py index f99edc594..d8b9e2118 100644 --- a/tortoise/backends/mysql/client.py +++ b/tortoise/backends/mysql/client.py @@ -17,6 +17,7 @@ import asyncmy as mysql from asyncmy import errors from asyncmy.charset import charset_by_name + from asyncmy.constants import COMMAND except ImportError: import aiomysql as mysql from pymysql.charset import charset_by_name diff --git a/tortoise/backends/odbc/client.py b/tortoise/backends/odbc/client.py index 9a52cf83f..39bf16099 100644 --- a/tortoise/backends/odbc/client.py +++ b/tortoise/backends/odbc/client.py @@ -205,13 +205,10 @@ async def rollback(self) -> None: self._connection._conn.autocommit = True async def savepoint(self) -> None: - # TODO pass async def savepoint_rollback(self) -> None: - # TODO pass async def release_savepoint(self) -> None: - # TODO pass