diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-05-14 12:50:11 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-05-17 16:32:22 -0400 |
| commit | 0e53221eef50b3274841fbd1eb41e32f5dfc4e69 (patch) | |
| tree | e1c13d067b0db5e0aeee8b934dc0f934154ef9f3 /doc/build | |
| parent | 79de84b25e87bbb7fa94f0dd513b4abc76e05a7e (diff) | |
| download | sqlalchemy-0e53221eef50b3274841fbd1eb41e32f5dfc4e69.tar.gz | |
Update transaction / connection handling
step one, do away with __connection attribute and using
awkward AttributeError logic
step two, move all management of "connection._transaction"
into the transaction objects themselves where it's easier
to follow.
build MarkerTransaction that takes the role of
"do-nothing block"
new connection datamodel is: connection._transaction, always
a root, connection._nested_transaction, always a nested.
nested transactions still chain to each other as this
is still sort of necessary but they consider the root
transaction separately, and the marker transactions
not at all.
introduce new InvalidRequestError subclass
PendingRollbackError. Apply to connection and session
for all cases where a transaction needs to be rolled
back before continuing. Within Connection,
both PendingRollbackError as well as ResourceClosedError
are now raised directly without being handled by
handle_dbapi_error(); this removes these two exception
cases from the handle_error event handler as well as
from StatementError wrapping, as these two exceptions are
not statement oriented and are instead programmatic
issues, that the application is failing to handle database
errors properly.
Revise savepoints so that when a release fails, they set
themselves as inactive so that their rollback() method
does not throw another exception.
Give savepoints another go on MySQL, can't get release working
however get support for basic round trip going
Fixes: #5327
Change-Id: Ia3cbbf56d4882fcc7980f90519412f1711fae74d
Diffstat (limited to 'doc/build')
| -rw-r--r-- | doc/build/errors.rst | 62 |
1 files changed, 42 insertions, 20 deletions
diff --git a/doc/build/errors.rst b/doc/build/errors.rst index 599b91e26..9af96f9b9 100644 --- a/doc/build/errors.rst +++ b/doc/build/errors.rst @@ -225,47 +225,69 @@ sooner. :ref:`connections_toplevel` +.. _error_8s2b: + +Can't reconnect until invalid transaction is rolled back +---------------------------------------------------------- + +This error condition refers to the case where a :class:`_engine.Connection` was +invalidated, either due to a database disconnect detection or due to an +explicit call to :meth:`_engine.Connection.invalidate`, but there is still a +transaction present that was initiated by the :meth:`_engine.Connection.begin` +method. When a connection is invalidated, any :class:`_engine.Transaction` +that was in progress is now in an invalid state, and must be explicitly rolled +back in order to remove it from the :class:`_engine.Connection`. + .. _error_8s2a: This connection is on an inactive transaction. Please rollback() fully before proceeding ------------------------------------------------------------------------------------------ This error condition was added to SQLAlchemy as of version 1.4. The error -refers to the state where a :class:`_engine.Connection` is placed into a transaction -using a method like :meth:`_engine.Connection.begin`, and then a further "sub" transaction -is created within that scope; the "sub" transaction is then rolled back using -:meth:`.Transaction.rollback`, however the outer transaction is not rolled back. +refers to the state where a :class:`_engine.Connection` is placed into a +transaction using a method like :meth:`_engine.Connection.begin`, and then a +further "marker" transaction is created within that scope; the "marker" +transaction is then rolled back using :meth:`.Transaction.rollback` or closed +using :meth:`.Transaction.close`, however the outer transaction is still +present in an "inactive" state and must be rolled back. The pattern looks like:: engine = create_engine(...) connection = engine.connect() - transaction = connection.begin() + transaction1 = connection.begin() + # this is a "sub" or "marker" transaction, a logical nesting + # structure based on "real" transaction transaction1 transaction2 = connection.begin() transaction2.rollback() - connection.execute(text("select 1")) # we are rolled back; will now raise + # transaction1 is still present and needs explicit rollback, + # so this will raise + connection.execute(text("select 1")) - transaction.rollback() +Above, ``transaction2`` is a "marker" transaction, which indicates a logical +nesting of transactions within an outer one; while the inner transaction +can roll back the whole transaction via its rollback() method, its commit() +method has no effect except to close the scope of the "marker" transaction +itself. The call to ``transaction2.rollback()`` has the effect of +**deactivating** transaction1 which means it is essentially rolled back +at the database level, however is still present in order to accommodate +a consistent nesting pattern of transactions. +The correct resolution is to ensure the outer transaction is also +rolled back:: -Above, ``transaction2`` is a "sub" transaction, which indicates a logical -nesting of transactions within an outer one. SQLAlchemy makes great use of -this pattern more commonly in the ORM :class:`.Session`, where the FAQ entry -:ref:`faq_session_rollback` describes the rationale within the ORM. + transaction1.rollback() -The "subtransaction" pattern in Core comes into play often when using the ORM -pattern described at :ref:`session_external_transaction`. As this pattern -involves a behavior called "connection branching", where a :class:`_engine.Connection` -serves a "branched" :class:`_engine.Connection` object to the :class:`.Session` via -its :meth:`_engine.Connection.connect` method, the same transaction behavior comes -into play; if the :class:`.Session` rolls back the transaction, and savepoints -have not been used to prevent a rollback of the entire transaction, the -outermost transaction started on the :class:`_engine.Connection` is now in an inactive -state. +This pattern is not commonly used in Core. Within the ORM, a similar issue can +occur which is the product of the ORM's "logical" transaction structure; this +is described in the FAQ entry at :ref:`faq_session_rollback`. +The "subtransaction" pattern is to be removed in SQLAlchemy 2.0 so that this +particular programming pattern will no longer be available and this +error message will no longer occur in Core. .. _error_dbapi: |
